1. 程式人生 > >記憶體洩露之常見問題解決--初級篇

記憶體洩露之常見問題解決--初級篇

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

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的簡單使用就到這裡了。下次再來深度分析記憶體洩露的點點滴滴。