使用Leak Canary檢測應用的內(nèi)存泄露

歡迎Follow我的GitHub, 關注我的簡書. 其余參考Android目錄.

內(nèi)存泄露

本文的合集已經(jīng)編著成書趁蕊,高級Android開發(fā)強化實戰(zhàn)淤毛,歡迎各位讀友的建議和指導胖腾。在京東即可購買:https://item.jd.com/12385680.html

Android

LeakCanary是檢測App內(nèi)存泄露的工具, 內(nèi)存泄露是Android開發(fā)中常見的問題, 使用程序的穩(wěn)定性下降.

注意: 集成到低版本應用, 會報出Bug.
Error:(2) Error retrieving parent for item: No resource found that matches the given name 'android:Theme.Material'.
修改編譯版本: compileSdkVersion 21 即可.

本文示例的Github下載地址.

主要原因:
生命周期較長的類使用Activity的Context, 導致Activity被引用, 無法被及時回收. 除了需要Activity頁面支持的控件類, 如Dialog等, 其他全部使用應用的Context替換Activity的Context, 即Context.getApplicationContext(). 還有就是單例不要持有頁面的控件, 單例持有控件, 控件附屬頁面, 最終頁面得不到釋放, 單例可以使用回調(diào)修改頁面, 內(nèi)部僅僅保留處理數(shù)據(jù)部分.

LeakCanary可以檢查出頁面的泄露問題, 并提供具體位置.

泄露

表明: LeakSingle的靜態(tài)單例sInstance引用LeakSingle的mContext, 從而導致MainActivity的instance泄露.

內(nèi)存泄露的原因已經(jīng)理解了, 那么我來講解一下如何檢測應用吧.


1. 依賴和引入

build.gradle的依賴.

    debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1' // or 1.4-beta1
    releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' // or 1.4-beta1
    testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' // or 1.4-beta1

引入應用

public class DemoApplication extends Application {
    @Override public void onCreate() {
        super.onCreate();
        LeakCanary.install(this);
    }
}

配置非常簡單, 會增加一個附屬應用, 去掉Application的引用, 就可以移除LeakCanary.


2. 泄露單例

泄露單例, 引入頁面的TextView, 強制保留的父Activity, 會導致內(nèi)存泄露, 可以在onDestroy時, 解引用避免.

/**
 * 泄露單例, 設計混亂, 單例只應該做事務性的工作, 頁面操作應該使用回調(diào).
 * 本示例僅做為反面示例, 切勿學習.
 * <p/>
 * Created by wangchenlong on 16/1/25.
 */
public class LeakSingle {
    private Context mContext;
    private TextView mTextView;

    private static LeakSingle sInstance;

    private LeakSingle(Context context) {
        mContext = context;
    }

    public static LeakSingle getInstance(Context context) {
        if (sInstance == null) {
            sInstance = new LeakSingle(context);
        }
        return sInstance;
    }

    // 內(nèi)存泄露
    public void setRetainedTextView(TextView tv) {
        mTextView = tv;
        mTextView.setText(mContext.getString(R.string.app_name));
    }

    // 刪除引用, 防止泄露
    public void removeRetainedTextView() {
        mTextView = null;
    }
}

單例只應該做事務性的工作, 頁面操作應該使用回調(diào), 不是引入控件. 本示例僅做為反面示例, 切勿學習.

3. 泄露內(nèi)存

調(diào)用單例, 兩種引用都會導致內(nèi)存泄露, 第一種是Context引用泄露, 第二種是子控件引用泄露. 避免方式是在onDestroy中, 清除引用.

最優(yōu)方式:
在單例中只執(zhí)行事務性工作, 不執(zhí)行具體頁面操作, 可以使用接口回調(diào), 異步處理.

public class MainActivity extends AppCompatActivity {

    @Bind(R.id.main_tv_text) TextView mTvText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);

        /**
         * me.chunyu.spike.wcl_leakcanary_demo.MainActivity has leaked:
         * GC ROOT static me.chunyu.spike.wcl_leakcanary_demo.LeakSingle.sInstance
         * references me.chunyu.spike.wcl_leakcanary_demo.LeakSingle.mContext
         * leaks me.chunyu.spike.wcl_leakcanary_demo.MainActivity instance
         */
//        LeakSingle.getInstance(this).setRetainedTextView(mTvText);

        /**
         * me.chunyu.spike.wcl_leakcanary_demo.MainActivity has leaked:
         * GC ROOT static me.chunyu.spike.wcl_leakcanary_demo.LeakSingle.sInstance
         * references me.chunyu.spike.wcl_leakcanary_demo.LeakSingle.mTextView
         * references android.support.v7.widget.AppCompatTextView.mContext
         * leaks me.chunyu.spike.wcl_leakcanary_demo.MainActivity instance
         */
        LeakSingle.getInstance(this.getApplication()).setRetainedTextView(mTvText);
    }

    @Override protected void onDestroy() {
        super.onDestroy();
        // 防止內(nèi)泄露
        LeakSingle.getInstance(this.getApplication()).removeRetainedTextView();
    }
}

根據(jù)LeakCanary中的檢測結果, 修改內(nèi)存泄露的地方, 就可以完美的解決問題.


4. 解析泄露位置

新手程序員經(jīng)常會問我, 如何理解LeakCanary的錯誤信息, 我來舉個例子講解一些.
如圖:

泄露

LeakCanary的內(nèi)存泄露提示一般會包含三個部分:
第一部分(LeakSingle類的sInstance變量)引用第二部分(LeakSingle類的mContext變量), 導致第三部分(MainActivity類的實例instance)泄露.

應用最常見的泄露位置就是Activity的實例, 要手動或使用shell命令, 啟動所有的Activity. LeakCanary判斷時機是Activity啟動到結束, 檢測這一過程是否會發(fā)生內(nèi)存泄露.

啟動Activity的shell命令是

adb shell am start  -n [包名]/[Activity名]

詳細內(nèi)容參考第六點.


內(nèi)存泄露的問題對于應用的用戶體驗至關重要, 感謝Square的產(chǎn)品, 讓這件事變得如此簡單.

OK, that's all! Enjoy it!

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末腊脱,一起剝皮案震驚了整個濱河市土砂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌氮凝,老刑警劉巖羔巢,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異罩阵,居然都是意外死亡竿秆,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門稿壁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來幽钢,“玉大人,你說我怎么就攤上這事傅是》搜啵” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵喧笔,是天一觀的道長帽驯。 經(jīng)常有香客問我,道長书闸,這世上最難降的妖魔是什么尼变? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮浆劲,結果婚禮上嫌术,老公的妹妹穿的比我還像新娘。我一直安慰自己梳侨,他們只是感情好蛉威,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布日丹。 她就那樣靜靜地躺著走哺,像睡著了一般。 火紅的嫁衣襯著肌膚如雪哲虾。 梳的紋絲不亂的頭發(fā)上丙躏,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天择示,我揣著相機與錄音,去河邊找鬼晒旅。 笑死栅盲,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的废恋。 我是一名探鬼主播谈秫,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼鱼鼓!你這毒婦竟也來了拟烫?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤迄本,失蹤者是張志新(化名)和其女友劉穎硕淑,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嘉赎,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡置媳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了公条。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拇囊。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖靶橱,靈堂內(nèi)的尸體忽然破棺而出寂拆,到底是詐尸還是另有隱情,我是刑警寧澤抓韩,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布纠永,位于F島的核電站,受9級特大地震影響谒拴,放射性物質(zhì)發(fā)生泄漏尝江。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一英上、第九天 我趴在偏房一處隱蔽的房頂上張望炭序。 院中可真熱鬧,春花似錦苍日、人聲如沸惭聂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽耕腾。三九已至扫俺,卻和暖如春狼纬,著一層夾襖步出監(jiān)牢的瞬間疗琉,已是汗流浹背没炒。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工拳话, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留种吸,地道東北人镜盯。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像艺糜,于是被迫代替她去往敵國和親破停。 傳聞我的和親對象是個殘疾皇子真慢,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容