[置頂] 內存泄露之常見問題解決--初級篇


身為一個段子猿,我決定來寫寫最近的學習心得。

1.簡介

在整個Android開發過程中,內存泄露是導致OOM的一個重點因素。大概意思就是:GC無法回收原本應該被回收的對象,這個對象就引發了內存泄露。那有什么危害呢?手機的內存大小是有限的,如果不能釋放的話,你就無法創建新的對象,你的新界面等等就無法正常運行,然后程序就OOM了(OutOfMemory)。

2.OOM以及內存泄露

OOM通俗點講就是,你家里有2個廁所,本來你和你老婆用的話,都是夠用的,有一天你不小心造人了,從此家里有了1+1=3個人了。一天的凌晨,你起床,發現肚子不舒服,“我要上廁所!”(請求系統分配空余內存給當前app)。咦,老婆你在里面啊,那我去下一家(系統開始分析你需要的內存以及空余內存,准備分配)。啊,兒子你也蹲着啊(完了,2個廁所都被人占着了,內存不足,boom!boom!你此時肯定是奔潰的,拉屎忍不住了,肯定要異常奔潰了。OOM,app程序異常退出了)。

那么內存泄露呢?比如你買了一堆可擦除的畫板(畫板=內存條,畫板空間有限=內存有限),剛開始你拿去畫了一會畫,慢慢的用了不少畫板。此時你還在繼續畫,發現沒有新的畫板了,只好找剛才用過的,但是你發現用錯了畫筆,導致擦不掉!(不正常的使用context等導致內存泄露了,GC回收不了內存。)而且所以找遍了所有的畫板,都沒有找到空白的地方(分配不了新的空余內存給app),只好結束此次畫畫的事情(內存泄露導致內存不足,這個app程序OOM)。

3.配置

本篇是初級篇,就不過多描述理論性的東西了,大牛的文章都寫過,本人就不再進行描述了。那么我們如何解決內存泄露的問題呢?對於小白的我們肯定想有一款簡單易用、快速定位的內存泄露插件,那么我推薦LeakCanary傻瓜式檢測工具給大家。

ok,第一步肯定是怎么在項目里引用這個呢?首先,Android studio的項目引用第三方庫的方法你得知道。在build.gradle(下方圖里的1位置,2位置不是哦)里如下2個引用。什么,為什么有2個?其實有3個呢,原因很簡單,你總不會正式發布的包也加內存泄露吧?當然也可以正式發布時,把相關引用全都干掉。
這里寫圖片描述

dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3'
}

引用完了,咱們開始做一些初始化操作。在你的Application的實現類寫下下面2句。

/**
* 內存泄露檢測
*/

private RefWatcher refWatcher;
public static RefWatcher getRefWatcher(Context context) {
TTApplication application = (TTApplication) context.getApplicationContext();
return application.refWatcher;
}
@Override
public void onCreate() {
super.onCreate();
refWatcher = LeakCanary.install(this);//內存泄露檢測
}

然后在你的fragment的基類里加上這一句

  @Override
public void onDestroy() {
RefWatcher refWatcher = TTApplication.getRefWatcher(getActivity());
refWatcher.watch(this);//內存泄露檢測
}

好了,我們的配置全部完成了,是不是so easy?先休息一下看個美腿。






這里寫圖片描述
哦,不好意思放錯了。
這里寫圖片描述

4.常見內存泄露

下面就念咒語“哦瑪尼瑪尼哄”,”奔跑吧我的app”,”出來吧,萬惡的內存泄露”。(看下圖)
這里寫圖片描述

這里寫圖片描述
如果出現內存泄露,上方會有盾牌的通知消息(1位置),點擊后可以進入詳情界面(2位置)。
這里寫圖片描述
上方的圖片里我們能找到一些蛛絲馬跡,比如這個回調導致內存泄露了。我回憶了下代碼,因為本人這個項目是MVP模式,P層持有activity的context以及IView接口。常見的泄露基本都是上下文的持有導致的,所以應對這一點,我采用P層持有弱引用的方式。弱引用,只具有弱引用的對象擁有更短暫的生命周期。在垃圾回收器線程掃描它所管轄的內存區域的過程中,一旦發現了只具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存。此外,剛才的回調接口也需要設置null。這樣在activity銷毀時onDestroy方法里調用下面的destroy方法即可。重要的一點是,網絡請求在界面銷毀時最好進行cancel操作,比如volly框架的根據請求tag進行取消,這樣也是盡量避免接口回調導致的內存泄露。此外,動畫在界面離開時或者銷毀時,同時進行暫停或者銷毀。

private WeakReference<ShoppingCartActivity> mBaseActivity;
private IShoppingCartView mIShoppingCartView;
public ShoppingCartPresenter(ShoppingCartActivity context, IShoppingCartView
iShoppingCartView) {
mBaseActivity = new WeakReference<>(context);
mIShoppingCartView = iShoppingCartView;
}
@Override
public void destroy() {
mIShoppingCartView = null;
}

還有一種常見的就是handler的使用。通常我們直接new一個就完事了,Android studio會顯示大大的黃色警告區域。此時就需要利用靜態內部類以及弱引用解決了。平常需要context的地方就可以用 mActivity.get()代替了。

static class CommentHandler extends Handler {
WeakReference<HomeActivity> mActivity;


CommentHandler(HomeActivity activity) {
mActivity = new WeakReference<>(activity);
}

public void handleMessage(Message msg) {
super.handleMessage(msg);

}
}

在App不可避免的是webview加載網頁了,然后這個也是一個可怕的開始。webview界面內存泄露解決,不要xml設置webview,而是以代碼創建view對象的方式進行初始化,並且界面銷毀時調用 destroy等方法 。

    WebView  mWebView  =new WebView (this);

@Override
protected void onDestroy() {
if (mWebView != null) {
mWebView.getSettings().setBuiltInZoomControls(true);
mWebView.setVisibility(View.GONE);// 把destroy()延后
mWebView.removeAllViews();
mWebView.destroy();
}
super.onDestroy();
}

此外,Toast最好用ApplicationContext,如果用activity的context,吐司還沒結束的時候退出當前界面,這是內存也會泄露。
至於context與bitmap之間的點滴,我用了fresco圖片加載框架,還算好。而且這方面資料也不少,我暫時也不准備描述了。

好了,LeakCanary的簡單使用就到這里了。下次再來深度分析內存泄露的點點滴滴。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com