一、LeakCanary簡介
在Android開發中,內存泄漏一直是一個難題。為了解決這一問題,Square Open Source團隊開發了一款內存泄漏檢測庫:LeakCanary。LeakCanary可以非常方便地在運行時檢測應用程序的內存泄漏情況,也因此成為了Android開發中的重要工具之一。
LeakCanary基於一個簡單的原理,即通過引用鏈來判斷對象是否被釋放。如果被釋放的對象在引用鏈中沒有被引用,那麼就可以判斷出該對象已經被釋放,否則就表示該對象存在內存泄漏。
二、LeakCanary原理
下面我們來詳細講解一下LeakCanary的原理。例如我們需要檢測Activity是否存在內存泄漏問題:
public class TestActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }
我們需要為該Activity實現RefWatcher,通過監聽其生命周期來判斷是否存在內存泄漏:
public class TestActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RefWatcher refWatcher = LeakCanary.install(this.getApplication()); refWatcher.watch(this); } }
通過調用LeakCanary的install方法,我們可以獲取到一個RefWatcher實例。refWatcher.watch(this)就是指定了需要檢測的對象,這裡我們選擇檢測當前的Activity。
LeakCanary內部實現原理是,通過覆寫Application的onTrimMemory方法,檢測當前進程的內存情況。當檢測到內存緊張的情況時,會先調用系統的gc()方法,然後再檢查所有被註冊的對象,看看是否有已經被釋放的對象存在引用鏈中。如果有,則判斷為內存泄漏,LeakCanary會將這個內存泄漏的對象和引用鏈信息記錄到HeapAnalyzerService中,再次檢測時就會通過HeapAnalyzerService檢測是否存在內存泄漏問題。
三、LeakCanary使用
使用LeakCanary非常簡單,只需要在項目中添加以下依賴:
dependencies { debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7' }
然後在Application初始化時註冊LeakCanary:
public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); if (LeakCanary.isInAnalyzerProcess(this)) { return; } LeakCanary.install(this); } }
這樣就可以在應用程序中使用了,如果內存泄漏,LeakCanary會在Logcat輸出對應信息:
┬─── │ GC Root: Java local variable │ ├─ com.example.TestActivity instance │ Leaked: YES (ObjectWatcher was watching this because com.example.TestActivity received Activity#onDestroy() callback and Activity#mDestroyed is true) │ key = af5f35c2-dedd-4c86-bf7e-e4b1454881ed │ watchDurationMillis = 10508 │ retainedDurationMillis = -1 │ mContext instance of com.example.MyApplication │ mWindow instance of android.view.Window$1 │ mActionBar instance of android.app.ActionBarImpl │ mActionView instance of com.android.internal.view.menu.ActionMenuView │ mParent instance of android.widget.LinearLayout │ mPresenter instance of com.android.internal.view.menu.ActionMenuPresenter │ ↓ ActionMenuPresenter.mContext │ ~~~~~~ ├─ android.app.Application$1 instance │ Leaked: NO (Application is a singleton) │ ↓ Application$1.this$0 │ ~~~~~~ ├─ com.example.MyApplication instance │ Leaked: NO (Application is a singleton) │ ↓ MyApplication.leakToggle │ ~~~~~~~~~~ ├─ com.example.utils.LeakActivityWatcher instance │ Leaked: NO (ObjectWatcher was watching this) │ ↓ LeakActivityWatcher.context │ ~~~~~~~ ├─ com.example.MyApplication instance │ Leaked: NO (Application is a singleton) │ ↓ MyApplication.mActivityWatcher │ ~~~~~~~~~~~~~~~~ ├─ com.example.utils.LeakActivityWatcher instance │ Leaked: NO (ObjectWatcher was watching this) │ ├─ android.os.HandlerThread instance │ Leaked: NO (Thread-62 is running) │ ↓ HandlerThread.mLooper │ ~~~~~~ ├─ android.os.Looper instance │ Leaked: NO (Thread-62 is running) │ ↓ Looper.mQueue │ ~~~~~ ├─ android.os.MessageQueue instance │ Leaked: NO (Thread-62 is running) │ ↓ MessageQueue.mIdleHandlers │ ~~~~~~~~~~~~~ ├─ java.util.ArrayList instance │ Leaked: NO (Thread-62 is running) │ ↓ ArrayList.elementData │ ~~~~~~~~~~~ ├─ java.lang.Object[] array │ Leaked: NO (Thread-62 is running) │ Array length: 1 │ ↓ Object[0] android.os.BinderProxy instance │ Leaked: NO (Thread-62 is running) │ ├─ android.os.BinderProxy instance │ Leaked: NO (mContext is a system object) │ ↓ BinderProxy.mContext │ ~~~~~~~ ├─ com.android.server.am.ActivityManagerService$MainHandler instance │ Leaked: NO (mThread is a system object) │ ↓ ActivityManagerService$MainHandler.this$0 │ ~~~~~~ ├─ com.android.server.am.ActivityManagerService instance │ Leaked: NO (mContext is a system object) │ ↓ ActivityManagerService.mUiContext │ ~~~~~~~~~~ ╰→ com.android.server.firewall.IntentFirewall instance Leaked: YES (ObjectWatcher was watching this) key = d6bebb1e-13bd-45af-a786-491763d6c3e6 watchDurationMillis = 10808 retainedDurationMillis = -1
通過Logcat輸出我們可以看到,LeakCanary成功檢測出了內存泄漏信息。我們需要仔細分析這些信息,找到內存泄漏的原因,及時修復Bug。
四、結論
LeakCanary是一個非常實用的內存泄漏檢測庫,在開發中可以使用它監測應用程序是否存在內存泄漏問題,及時發現並修復內存泄漏問題,從而更好地提升應用程序的性能和穩定性。
原創文章,作者:JUNL,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/149809.html