How to check memory leaks in android app?
LeakCanary 是 Android 和 Java 記憶體洩露檢測框架,該框架是Square公司的一個開源庫,專案地址 leakcanary。
Android 開發中你是否頻頻遇到記憶體洩露而無奈無從解決。說不定哪天你不小心寫的一行程式碼就導致了記憶體洩露。可以先看看這些問題導致的記憶體洩露 Android開發編碼規範導致的記憶體洩露問題,而LeakCanary 則很直白得檢測出了記憶體洩露並展示給我們。在使用它之前,我們來寫一個例子。
本地廣播,在開發中還是有一定的應用的,現在有這麼一個需求,要求使用本地廣播來實現,就是通過傳送一個退出程式的本地廣播,所有Activity接收到後就退出,這顯然是需要一個基礎的Activity,其他Activity繼承它。為了方便,這裡我們只使用一個Activity。
- publicclass MainActivity extends AppCompatActivity {
- publicfinalstatic String ACTION_EXIT_APP = "cn.edu.zafu.leakcanary.exit";
- privatestatic LocalBroadcastManager mLocalBroadcatManager;
- private BroadcastReceiver mExitReceiver = new BroadcastReceiver() {
- @Override
-
public
- String action = intent.getAction();
- if (action.equals(ACTION_EXIT_APP)) {
- Log.d("TAG", "exit from broadcast");
- finish();
- }
- }
- };
- @Override
-
protected
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- init();
- }
- privatevoid init() {
- IntentFilter filter = new IntentFilter();
- filter.addAction(ACTION_EXIT_APP);
- filter.addCategory(Intent.CATEGORY_DEFAULT);
- getLocalBroadcastManager().registerReceiver(mExitReceiver, filter);
- }
- private LocalBroadcastManager getLocalBroadcastManager() {
- if (mLocalBroadcatManager == null) {
- mLocalBroadcatManager = LocalBroadcastManager.getInstance(this);
- }
- return mLocalBroadcatManager;
- }
- }
乍一看,是不是感覺寫的很對啊,那你就不夠細心了,這還是少量的程式碼,對於專案中日積月累的程式碼,記憶體洩露或許無處不在。我們使用LeakCanary 對我們的程式碼進行檢測下,看看到底哪裡發生了記憶體洩露,以及該如何解決。
使用方法也很簡單,首先加入依賴
- debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1'
- releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1'
從依賴中也是可以看出貓膩的。
然後在我們程式的Applictaion中進行安裝,當然,不要忘記在清單檔案中註冊該Application。
- publicclass App extends Application {
- @Override
- publicvoid onCreate() {
- super.onCreate();
- LeakCanary.install(this);
- }
- }
我說就這麼簡單你會信,好了,我們安裝到手機上看看。安裝完成後執行該軟體,開啟後退出該軟體,這時候你發現桌面上多了一個Leaks的圖示。
開啟它後通知欄會有一個通知,通知你發生了記憶體洩露
然後在軟體裡你會看到記憶體洩露的跟蹤資訊。
點選下方的delete可以刪除此條資訊。
仔細一看,原來是我們的mLocalBroadcatManager發生了洩露,註冊本地廣播的時候,傳入了this,系統內部保持了這個引用,當我們退出Activity時,這個引用還是指向我們的Activity,導致Activity回收失敗。那麼怎麼解決了,既然退出的時候還持有引用,那麼我們取消註冊這個廣播這個引用不就沒了嗎,重寫onDestroy方法,進行取消註冊。
<code class="hljs java has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onDestroy</span>() { <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.onDestroy(); getLocalBroadcastManager().unregisterReceiver(mExitReceiver); }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li></ul>
重新執行一下,咦,你發現記憶體不再洩露了。該軟體裡不再提示記憶體洩露的跟蹤資訊了。
就是這麼簡單,如果想更進一步瞭解使用方法,比如檢測Fragment有沒有洩露。可以參考官方給的例子,並且記憶體洩露的跟蹤資訊也是可以上傳到伺服器的,更多內容,參考 leakcanary
原始碼下載