Android內(nèi)存泄漏終極解決篇(下)
一、概述
在 Android內(nèi)存泄漏終極解決篇(上)中我們介紹了如何檢查一個(gè)App是否存在內(nèi)存泄漏的問(wèn)題,本篇將總結(jié)典型的內(nèi)存泄漏的代碼,并給出對(duì)應(yīng)的解決方案。內(nèi)存泄漏的主要問(wèn)題可以分為以下幾種類型:
- 靜態(tài)變量引起的內(nèi)存泄漏
- 非靜態(tài)內(nèi)部類引起的內(nèi)存泄漏
- 資源未關(guān)閉引起的內(nèi)存泄漏
二、靜態(tài)變量引起的內(nèi)存泄漏
在java中靜態(tài)變量的生命周期是在類加載時(shí)開(kāi)始,類卸載時(shí)結(jié)束。換句話說(shuō),在android中其生命周期是在進(jìn)程啟動(dòng)時(shí)開(kāi)始,進(jìn)程死亡時(shí)結(jié)束。所以在程序的運(yùn)行期間,如果進(jìn)程沒(méi)有被殺死,靜態(tài)變量就會(huì)一直存在,不會(huì)被回收掉。如果靜態(tài)變量強(qiáng)引用了某個(gè)Activity中變量,那么這個(gè)Activity就同樣也不會(huì)被釋放,即便是該Activity執(zhí)行了onDestroy(不要將執(zhí)行onDestroy和被回收劃等號(hào))。這類問(wèn)題的解決方案為:1.尋找與該靜態(tài)變量生命周期差不多的替代對(duì)象。2.若找不到,將強(qiáng)引用方式改成弱引用。比較典型的例子如下:
單例引起的Context內(nèi)存泄漏
public class IMManager {
private Context context;
private static IMManager mInstance;
public static IMManager getInstance(Context context) {
if (mInstance == null) {
synchronized (IMManager.class) {
if (mInstance == null)
mInstance = new IMManager(context);
}
}
return mInstance;
}
private IMManager(Context context) {
this.context = context;
}
}
當(dāng)調(diào)用getInstance時(shí),如果傳入的context是Activity的context。只要這個(gè)單例沒(méi)有被釋放,這個(gè)Activity也不會(huì)被釋放。
解決方案
傳入Application的context,因?yàn)锳pplication的context的生命周期比Activity長(zhǎng),可以理解為Application的context與單例的生命周期一樣長(zhǎng),傳入它是最合適的。
public class IMManager {
private Context context;
private static IMManager mInstance;
public static IMManager getInstance(Context context) {
if (mInstance == null) {
synchronized (IMManager.class) {
if (mInstance == null)
//將傳入的context轉(zhuǎn)換成Application的context
mInstance = new IMManager(context.getApplicationContext());
}
}
return mInstance;
}
private IMManager(Context context) {
this.context = context;
}
}
三、非靜態(tài)內(nèi)部類引起的內(nèi)存泄漏
在java中,創(chuàng)建一個(gè)非靜態(tài)的內(nèi)部類實(shí)例,就會(huì)引用它的外圍實(shí)例。如果這個(gè)非靜態(tài)內(nèi)部類實(shí)例做了一些耗時(shí)的操作,就會(huì)造成外圍對(duì)象不會(huì)被回收,從而導(dǎo)致內(nèi)存泄漏。這類問(wèn)題的解決方案為:1.將內(nèi)部類變成靜態(tài)內(nèi)部類 2.如果有強(qiáng)引用Activity中的屬性,則將該屬性的引用方式改為弱引用。3.在業(yè)務(wù)允許的情況下,當(dāng)Activity執(zhí)行onDestory時(shí),結(jié)束這些耗時(shí)任務(wù)。
內(nèi)部線程造成的內(nèi)存泄漏
public class LeakAty extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.aty_leak);
test();
}
public void test() {
//匿名內(nèi)部類會(huì)引用其外圍實(shí)例LeakAty.this,所以會(huì)導(dǎo)致內(nèi)存泄漏
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
解決方案
將非靜態(tài)匿名內(nèi)部類修改為靜態(tài)匿名內(nèi)部類
public class LeakAty extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.aty_leak);
test();
}
//加上static,變成靜態(tài)匿名內(nèi)部類
public static void test() {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
Handler引起的內(nèi)存泄漏
public class LeakAty extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.aty_leak);
fetchData();
}
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 0:
// 刷新數(shù)據(jù)
break;
default:
break;
}
};
};
private void fetchData() {
//獲取數(shù)據(jù)
mHandler.sendEmptyMessage(0);
}
}
mHandler 為匿名內(nèi)部類實(shí)例,會(huì)引用外圍對(duì)象LeakAty.this,如果該Handler在Activity退出時(shí)依然還有消息需要處理,那么這個(gè)Activity就不會(huì)被回收。
解決方案
public class LeakAty extends Activity {
private TextView tvResult;
private MyHandler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.aty_leak);
tvResult = (TextView) findViewById(R.id.tvResult);
handler = new MyHandler(this);
fetchData();
}
//第一步,將Handler改成靜態(tài)內(nèi)部類。
private static class MyHandler extends Handler {
//第二步,將需要引用Activity的地方,改成弱引用。
private WeakReference<LeakAty> atyInstance;
public MyHandler(LeakAty aty) {
this.atyInstance = new WeakReference<LeakAty>(aty);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
LeakAty aty = atyInstance == null ? null : atyInstance.get();
//如果Activity被釋放回收了,則不處理這些消息
if (aty == null||aty.isFinishing()) {
return;
}
aty.tvResult.setText("fetch data success");
}
}
private void fetchData() {
// 獲取數(shù)據(jù)
handler.sendEmptyMessage(0);
}
@Override
protected void onDestroy() {
//第三步,在Activity退出的時(shí)候移除回調(diào)
super.onDestroy();
handler.removeCallbacksAndMessages(null);
}
}
四、資源未關(guān)閉引起的內(nèi)存泄漏
當(dāng)使用了BraodcastReceiver、Cursor、Bitmap等資源時(shí),當(dāng)不需要使用時(shí),需要及時(shí)釋放掉,若沒(méi)有釋放,則會(huì)引起內(nèi)存泄漏。
綜上所述,內(nèi)存泄漏的主要情況為上面的三大類型,最終歸結(jié)為一點(diǎn),就是資源在不需要的時(shí)候沒(méi)有被釋放掉。所以在編碼的過(guò)程中要注意這些細(xì)節(jié),提高程序的性能。
- Android Studio 3.0上分析內(nèi)存泄漏的原因
- Android性能優(yōu)化之利用強(qiáng)大的LeakCanary檢測(cè)內(nèi)存泄漏及解決辦法
- Android內(nèi)存泄漏終極解決篇(上)
- Android 有效的解決內(nèi)存泄漏的問(wèn)題實(shí)例詳解
- Android性能優(yōu)化之利用Rxlifecycle解決RxJava內(nèi)存泄漏詳解
- Android開(kāi)發(fā):淺談MVP模式應(yīng)用與內(nèi)存泄漏問(wèn)題解決
- 淺談Android應(yīng)用的內(nèi)存優(yōu)化及Handler的內(nèi)存泄漏問(wèn)題
- Android 內(nèi)存泄漏的幾種可能總結(jié)
- 詳解Android內(nèi)存泄漏檢測(cè)與MAT使用
- Android內(nèi)存泄漏的輕松解決方法
相關(guān)文章
Android自定義TextView實(shí)現(xiàn)drawableLeft內(nèi)容居中
這篇文章主要介紹了Android自定義TextView實(shí)現(xiàn)drawableLeft內(nèi)容居中的相關(guān)資料,需要的朋友可以參考下2017-06-06
簡(jiǎn)單談?wù)凙ndroid中SP與DP的區(qū)別
Android里面的sp和dp網(wǎng)上有很多文章都談過(guò)了,但是看后總有一種意猶未盡的感覺(jué)。現(xiàn)在我也來(lái)談?wù)刣p和sp,和大家交流一下,不對(duì)之處歡迎拍磚。2016-09-09
Android中l(wèi)istview嵌套scrollveiw沖突的解決方法
這篇文章主要為大家詳細(xì)介紹了Android中l(wèi)istview嵌套scrollveiw沖突的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01
Android ViewPager相冊(cè)橫向移動(dòng)的實(shí)現(xiàn)方法
本篇文章小編為大家介紹,Android ViewPager相冊(cè)橫向移動(dòng)的實(shí)現(xiàn)方法。需要的朋友參考下2013-04-04

