1. 程式人生 > >How to check memory leaks in android app?

How to check memory leaks in android app?

LeakCanary 是 Android 和 Java 記憶體洩露檢測框架,該框架是Square公司的一個開源庫,專案地址 leakcanary

Android 開發中你是否頻頻遇到記憶體洩露而無奈無從解決。說不定哪天你不小心寫的一行程式碼就導致了記憶體洩露。可以先看看這些問題導致的記憶體洩露 Android開發編碼規範導致的記憶體洩露問題,而LeakCanary 則很直白得檢測出了記憶體洩露並展示給我們。在使用它之前,我們來寫一個例子。

本地廣播,在開發中還是有一定的應用的,現在有這麼一個需求,要求使用本地廣播來實現,就是通過傳送一個退出程式的本地廣播,所有Activity接收到後就退出,這顯然是需要一個基礎的Activity,其他Activity繼承它。為了方便,這裡我們只使用一個Activity。

  1. publicclass MainActivity extends AppCompatActivity {  
  2.     publicfinalstatic String ACTION_EXIT_APP = "cn.edu.zafu.leakcanary.exit";  
  3.     privatestatic LocalBroadcastManager mLocalBroadcatManager;  
  4.     private BroadcastReceiver mExitReceiver = new BroadcastReceiver() {  
  5.         @Override
  6.         public
    void onReceive(Context context, Intent intent) {  
  7.             String action = intent.getAction();  
  8.             if (action.equals(ACTION_EXIT_APP)) {  
  9.                 Log.d("TAG""exit from broadcast");  
  10.                 finish();  
  11.             }  
  12.         }  
  13.     };  
  14.     @Override
  15.     protected
    void onCreate(Bundle savedInstanceState) {  
  16.         super.onCreate(savedInstanceState);  
  17.         setContentView(R.layout.activity_main);  
  18.         init();  
  19.     }  
  20.     privatevoid init() {  
  21.         IntentFilter filter = new IntentFilter();  
  22.         filter.addAction(ACTION_EXIT_APP);  
  23.         filter.addCategory(Intent.CATEGORY_DEFAULT);  
  24.         getLocalBroadcastManager().registerReceiver(mExitReceiver, filter);  
  25.     }  
  26.     private  LocalBroadcastManager getLocalBroadcastManager() {  
  27.         if (mLocalBroadcatManager == null) {  
  28.             mLocalBroadcatManager = LocalBroadcastManager.getInstance(this);  
  29.         }  
  30.         return mLocalBroadcatManager;  
  31.     }  
  32. }  

乍一看,是不是感覺寫的很對啊,那你就不夠細心了,這還是少量的程式碼,對於專案中日積月累的程式碼,記憶體洩露或許無處不在。我們使用LeakCanary 對我們的程式碼進行檢測下,看看到底哪裡發生了記憶體洩露,以及該如何解決。

使用方法也很簡單,首先加入依賴

  1. debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1'
  2. releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1'

從依賴中也是可以看出貓膩的。

然後在我們程式的Applictaion中進行安裝,當然,不要忘記在清單檔案中註冊該Application。

  1. publicclass App extends Application {  
  2.     @Override
  3.     publicvoid onCreate() {  
  4.         super.onCreate();  
  5.         LeakCanary.install(this);  
  6.     }  
  7. }  

我說就這麼簡單你會信,好了,我們安裝到手機上看看。安裝完成後執行該軟體,開啟後退出該軟體,這時候你發現桌面上多了一個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

原始碼下載