[TOC]
# LeakCanary源碼分析
> 基于版本 1.6.3
## 使用
### 依賴
```java
dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3'
// Optional, if you use support library fragments:
debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3'
}
```
### 初始化
Application 注入 初始化代碼
```java
public class ExampleApplication extends Application {
@Override public void onCreate() {
super.onCreate();
//LeakCanary.isInAnalyzerProcess(this)會調用 sInServiceProcess(context, HeapAnalyzerService.class)來判斷是否跟HeapAnalyzerService在同一個進程中。
if (LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
LeakCanary.install(this);
// Normal app init code...
}
}
```
aar 自帶 以下不需要開發者編寫
HeapAnalyzerService是一個IntentServcie,主要用來分析生成的hprof文件。我們看下HeapAnalyzerService的清單配置:
```xml
<service
android:name=".internal.HeapAnalyzerService"
android:process=":leakcanary"
android:enabled="false"
/>
```
可以看到,這個服務運行在單獨的“:leakcanary”進程中。如果你瀏覽下LeakCanary的其他android組件(DisplayLeakService,DisplayLeakActivity等)清單配置,會發現它們都是運行在這個進程中的。為什么HeapAnalyzerService要單獨放在這個進程?因為內存泄露時,生成的hprof文件很大,解析的時候會耗費大量內存,如果放在app主進程中,可能會導致OOM。
另外,我們也注意到`android:enabled="false"`這個屬性設置,那說明默認情況下,HeapAnalyzerService這個service是不可用的,**那它什么時候打開呢?** 帶著這個疑問我們接著往下看:
```java
//com.squareup.leakcanary.internal.LeakCanaryInternals
public static boolean isInServiceProcess(Context context, Class<? extends Service> serviceClass) {
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo;
try {
packageInfo = packageManager.getPackageInfo(context.getPackageName(), GET_SERVICES);
} catch (Exception e) {
CanaryLog.d(e, "Could not get package info for %s", context.getPackageName());
return false;
}
String mainProcess = packageInfo.applicationInfo.processName;
ComponentName component = new ComponentName(context, serviceClass);
ServiceInfo serviceInfo;
try {
serviceInfo = packageManager.getServiceInfo(component, 0);
} catch (PackageManager.NameNotFoundException ignored) {
// Service is disabled.
/****標記1*******.首次運行的時候,HeapAnalyzerService組件沒有打開,所以到這里就返回了***/
return false;
}
//HeapAnalyzerService激活后才會有下邊的代碼
if (serviceInfo.processName.equals(mainProcess)) {/****標記2*******/
CanaryLog.d("Did not expect service %s to run in main process %s", serviceClass, mainProcess);
// Technically we are in the service process, but we're not in the service dedicated process.
return false;
}
int myPid = android.os.Process.myPid();
ActivityManager activityManager =
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.RunningAppProcessInfo myProcess = null;
List<ActivityManager.RunningAppProcessInfo> runningProcesses;
try {
runningProcesses = activityManager.getRunningAppProcesses();
} catch (SecurityException exception) {
// https://github.com/square/leakcanary/issues/948
CanaryLog.d("Could not get running app processes %d", exception);
return false;
}
if (runningProcesses != null) {
for (ActivityManager.RunningAppProcessInfo process : runningProcesses) {
if (process.pid == myPid) {
myProcess = process;
break;
}
}
}
if (myProcess == null) {
CanaryLog.d("Could not find running process for %d", myPid);
return false;
}
return myProcess.processName.equals(serviceInfo.processName);
}
```
上邊我們說過,HeapAnalyzerService組件默認是關閉的,所以一次執行的時候,到代碼中1標識的位置就結束了。第二次執行的時候,才會往下走。此外,在2標記的的位置,如果你把HeapAnalyzerService的進程設置為跟主進程一樣,LeakCanary依然可以工作,但要小心OOM。再往下就是判斷HeapAnalyzerService服務進程是否跟主進程一樣。
### 查看內存泄漏
## 原理
### 如何判斷內存泄漏
> 弱引用探測內存泄露
WeakReference(T referent, ReferenceQueue<? super T> q)
referent被gc回收時,會將包裹它的弱引用注冊到ReferenceQueue中,在gc后判斷ReferenceQueue有沒有referent包裹的WeakReference,就可以判斷是否被gc正常回收。
### `LeakCanary.install`
```java
public static @NonNull RefWatcher install(@NonNull Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {
return new AndroidRefWatcherBuilder(context);
}
```
intall方法中,生成了AndroidRefWatcherBuilder對象,設置了一些參數,并最終調用了buildAndInstall()。
### `AndroidRefWatcherBuilder`
|參數類型|默認實現|android平臺實現類|含義|
| --- | --- | ---| --- |
|HeapDump.Listener | HeapDump.Listener.NONE |ServiceHeapDumpListener |執行分析HeapDump,在android中是在啟動DisplayLeakService在一個新的的進程中,執行解析dump |
|DebuggerControl |DebuggerControl.NONE |AndroidDebuggerControl |判斷進程是否處于debug狀態 |
|HeapDumper |HeapDumper.NONE |AndroidHeapDumper |dump heap到文件中 |
|GcTrigger |GcTrigger.DEFAULT |GcTrigger.DEFAULT |主動調用GC|
|WatchExecutor |WatchExecutor.NONE |AndroidWatchExecutor |延遲調用GC的執行器;在android平臺是內部有一個HandThread,通過 handler來做延遲操作|
|HeapDump.Builder |HeapDump.Builder |HeapDump.Builder |主要是構造執行dump時的一些參數|
注意兩點:
+ AndroidRefWatcherBuilder與RefWatcherBuilder使用了builder模式,可以看下是怎么用泛型實現Builder模式的繼承結構的。
+ 參數里邊的默認實現,都是在接口定義中使用匿名內部類定義了一個默認的的實現,這種寫法可以參考,比如DebuggerControl的定義
```java
public interface DebuggerControl {
DebuggerControl NONE = new DebuggerControl() {
@Override public boolean isDebuggerAttached() {
return false;
}
};
boolean isDebuggerAttached();
}
```
上邊說的HeapDump.Builder(Builder模式),最終會構造一個HeapDump對象,看下他有哪些參數:
|參數名稱 |類型 |含義 |
| --- | --- | --- |
|heapDumpFile |File |hprof文件路徑 |
|referenceKey |String |是一個UUID,用來唯一標識一個要監測的對象的|
|referenceName |String |用戶給監控對象自己定義的名字|
|excludedRefs |ExcludedRefs |要排除的類集合;因為有些內存泄露,不|是我們的程序導致的,而是系統的bug,這些bug我們無能為力,所以做了這樣一個列|表,把這些泄露問題排除掉,不會展示給我們|
|watchDurationMs |long |執行RefWatcher.watch()之后,到最終監測到內|存泄露花費的時間|
|gcDurationMs |long |手動執行gc花費的時間|
|heapDumpDurationMs |long |記錄dump內存花費的時間|
|computeRetainedHeapSize |boolean |是否計算持有的堆的大小|
|reachabilityInspectorClasses |List<Class<? extends Reachability.Inspector>> | ? |
+ AndroidRefWatcherBuilder#listenerServiceClass()
```java
/**
* Sets a custom {@link AbstractAnalysisResultService} to listen to analysis results. This
* overrides any call to {@link #heapDumpListener(HeapDump.Listener)}.
*/
public @NonNull AndroidRefWatcherBuilder listenerServiceClass(
@NonNull Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
enableDisplayLeakActivity = DisplayLeakService.class.isAssignableFrom(listenerServiceClass);
return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
}
```
`ServiceHeapDumpListener`:先簡單說下這個listsener的作用,當監測到內存泄露時,就會hprof文件回調給這個listener,在回調中它會啟啟動獨立的進程,解析這這個文件,并將解析結果傳給`AbstractAnalysisResultService`的實現類,也即install方法中傳入的`DisplayLeakService`,`DisplayLeakService`會彈出系統通知提示發生了內存泄露,這個listener將整個流程串了起來。 `DisplayLeakService` 最后會講到!
再往下看最后調用的`AndroidRefWatcherBuilder.buildAndInstall()`:
```java
/**
* Creates a {@link RefWatcher} instance and makes it available through {@link
* LeakCanary#installedRefWatcher()}.
*
* Also starts watching activity references if {@link #watchActivities(boolean)} was set to true.
*
* @throws UnsupportedOperationException if called more than once per Android process.
*/
public @NonNull RefWatcher buildAndInstall() {
if (LeakCanaryInternals.installedRefWatcher != null) {
throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
}
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
if (enableDisplayLeakActivity) {
LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
}
if (watchActivities) {
ActivityRefWatcher.install(context, refWatcher);
}
if (watchFragments) {
FragmentRefWatcher.Helper.install(context, refWatcher);
}
}
LeakCanaryInternals.installedRefWatcher = refWatcher;
return refWatcher;
}
```
build()是用之前傳的參數構造了一個RefWatcher對象返回給我們,檢測對象內存泄露的邏輯都是這個類處理的。
接下來異步激活組件DisplayLeakActivity,它是用來展示泄露列表信息的,激活組件調用的是 **packageManager.setComponentEnabledSetting()** 這個api,它可以實現一些特殊的功能,比如如果有系統權限,可以屏蔽應用開機監聽廣播,有興趣的可以google一下。因為這個方法可能會阻塞IPC通信,所以放在了異步里邊執行。
+ LeakCanaryInternals.setEnabledAsync()
```java
public static void setEnabledAsync(Context context, final Class<?> componentClass,
final boolean enabled) {
final Context appContext = context.getApplicationContext();
AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
@Override public void run() {
setEnabledBlocking(appContext, componentClass, enabled);
}
});
}
public static void setEnabledBlocking(Context appContext, Class<?> componentClass,
boolean enabled) {
ComponentName component = new ComponentName(appContext, componentClass);
PackageManager packageManager = appContext.getPackageManager();
int newState = enabled ? COMPONENT_ENABLED_STATE_ENABLED : COMPONENT_ENABLED_STATE_DISABLED;
// Blocks on IPC.
packageManager.setComponentEnabledSetting(component, newState, DONT_KILL_APP);
}
```
激活之后,我們桌面上會多一個圖標,為什么會多一個圖標?謎底就在他的清單配置中的intent-filter
```xml
激活之后,我們桌面上會多一個圖標,為什么會多一個圖標?謎底就在他的清單配置中的intent-filter
<activity
android:theme="@style/leak_canary_LeakCanary.Base"
android:name=".internal.DisplayLeakActivity"
android:process=":leakcanary"
....
>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
```
那具體什么時候觸發檢測的呢?這就是ActivityRefWatcher和FragmentRefWatcher要做的事兒。對于Activity和Fragment來說,當它的onDestory()被回調之后,就應該被系統回調掉。
### `ActivityRefWatcher`
+ com.squareup.leakcanary.ActivityRefWatcher#install()
```java
public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) {
Application application = (Application) context.getApplicationContext();
ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
}
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
};
```
到這里我們就能證實了當`Acitivity.onDestroy()` 監聽內存泄露 `refWatcher.watch(activity);`
### `FragmentRefWatcher`
+ com.squareup.leakcanary.internal.FragmentRefWatcher.Helper#install()
```java
public static void install(Context context, RefWatcher refWatcher) {
List<FragmentRefWatcher> fragmentRefWatchers = new ArrayList<>();
if (SDK_INT >= O) {
fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher));
}
try {
Class<?> fragmentRefWatcherClass = Class.forName(SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME);
Constructor<?> constructor =
fragmentRefWatcherClass.getDeclaredConstructor(RefWatcher.class);
FragmentRefWatcher supportFragmentRefWatcher =
(FragmentRefWatcher) constructor.newInstance(refWatcher);
fragmentRefWatchers.add(supportFragmentRefWatcher);
} catch (Exception ignored) {
}
if (fragmentRefWatchers.size() == 0) {
return;
}
Helper helper = new Helper(fragmentRefWatchers);
Application application = (Application) context.getApplicationContext();
application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks);
}
private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
for (FragmentRefWatcher watcher : fragmentRefWatchers) {
watcher.watchFragments(activity);
}
}
};
```
Fragment的onDestory() `監聽的是Acitivity#onDestory()` 監聽要麻煩一些,我們知道以前不管是原生和support庫中的Fragment是沒有統一的系統回調的,后來在Support庫中和android O以上的版本增加了對fragment的生命周期回調`fragmentManager.registerFragmentLifecycleCallbacks()`。
我們只看下是如何對Support庫中的fragment處理的。
+ com.squareup.leakcanary.internal.AndroidOFragmentRefWatcher
```java
@RequiresApi(Build.VERSION_CODES.O) //
class AndroidOFragmentRefWatcher implements FragmentRefWatcher {
private final RefWatcher refWatcher;
AndroidOFragmentRefWatcher(RefWatcher refWatcher) {
this.refWatcher = refWatcher;
}
private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
new FragmentManager.FragmentLifecycleCallbacks() {
@Override public void onFragmentViewDestroyed(FragmentManager fm, Fragment fragment) {
View view = fragment.getView();
if (view != null) {
refWatcher.watch(view);
}
}
@Override
public void onFragmentDestroyed(FragmentManager fm, Fragment fragment) {
refWatcher.watch(fragment);
}
};
@Override public void watchFragments(Activity activity) {
FragmentManager fragmentManager = activity.getFragmentManager();
fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
}
}
```
+ com.squareup.leakcanary.internal.SupportFragmentRefWatcher
```java
class SupportFragmentRefWatcher implements FragmentRefWatcher {
private final RefWatcher refWatcher;
SupportFragmentRefWatcher(RefWatcher refWatcher) {
this.refWatcher = refWatcher;
}
private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
new FragmentManager.FragmentLifecycleCallbacks() {
@Override public void onFragmentViewDestroyed(FragmentManager fm, Fragment fragment) {
View view = fragment.getView();
if (view != null) {
refWatcher.watch(view);
}
}
@Override public void onFragmentDestroyed(FragmentManager fm, Fragment fragment) {
refWatcher.watch(fragment);
}
};
@Override public void watchFragments(Activity activity) {
if (activity instanceof FragmentActivity) {
FragmentManager supportFragmentManager =
((FragmentActivity) activity).getSupportFragmentManager();
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
}
}
}
```
可以看到,對于Fragment來對,LeakCanary除了監控fragment又沒有回收外,還會監測Fragment的view有沒有被及時回收。AndroidOFragmentRefWatcher的代碼跟上邊的類似。
在android O及以上版本是通過`AndroidOFragmentRefWatcher`來處理,如果用的是最新的support庫,是通過`SupportFragmentRefWatcher`來處理,他們都繼承在FragmentRefWatcher。`AndroidOFragmentRefWatcher`和 `SupportFragmentRefWatcher` 代碼基本一樣為了適配API !
### `RefWatcher.watch()`
>從上可以看到,不管是`Fragment`,`Activity`還是`View`最終都調用了同一個`RefWatcher.watch()`方法,
以Activity:
```java
/**
* Watches the provided references and checks if it can be GCed. This method is non blocking,
* the check is done on the {@link WatchExecutor} this {@link RefWatcher} has been constructed
* with.
*
* @param referenceName An logical identifier for the watched object.
*/
public void watch(Object watchedReference, String referenceName) {
if (this == DISABLED) {
return;
}
checkNotNull(watchedReference, "watchedReference");
checkNotNull(referenceName, "referenceName");
final long watchStartNanoTime = System.nanoTime();
String key = UUID.randomUUID().toString();
retainedKeys.add(key);
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
ensureGoneAsync(watchStartNanoTime, reference);
}
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
// com.squareup.leakcanary.AndroidWatchExecutor watchExecutor
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}
```
`KeyedWeakReference` 繼承自WeakReference,增加的key和referenceName,這個key會同時放入Set集合中,方便gc回收對象后,做對比。
`watchExecutor` 在Android中實現是`AndroidWatchExecutor`
```java
// com.squareup.leakcanary.AndroidWatchExecutor#execute
@Override public void execute(@NonNull Retryable retryable) {
if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
waitForIdle(retryable, 0);
} else {
postWaitForIdle(retryable, 0);
}
}
```
`waitForIdle` 和 `postWaitForIdle` 最終還是調用 `waitForIdle`
```java
private void waitForIdle(final Retryable retryable, final int failedAttempts) {
// This needs to be called from the main thread.
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override public boolean queueIdle() {
postToBackgroundWithDelay(retryable, failedAttempts);
return false;
}
});
}
private void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) {
long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor);
long delayMillis = initialDelayMillis * exponentialBackoffFactor;
// HandlerThread
backgroundHandler.postDelayed(new Runnable() {
@Override public void run() {
Retryable.Result result = retryable.run();
if (result == RETRY) {
postWaitForIdle(retryable, failedAttempts + 1);
}
}
}, delayMillis);
}
```
這個類會在主UI線程空閑的時候,向內部的HandlerThread發送一個消息,執行execute方法傳給他的Retryable對象,
調用`retryable.run()`,如果返回RETRY會繼續等待下一個主UI線程空閑回調,直至返回為Done,每次重試都會增大間隔時間。
**注意:是通過 Looper.myQueue().addIdleHandler()來監聽系統主線程空閑的**
到這里我們就應該知道`ensureGone`最開始這個方法被執行的 `HandlerThread`線程中
```java
@SuppressWarnings("ReferenceEquality") // Explicitly checking for named null.
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime();
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
//第一次調用如果系統GC 減少引用
removeWeaklyReachableReferences();
if (debuggerControl.isDebuggerAttached()) {
// The debugger can create false leaks.
return RETRY;
}
if (gone(reference)) {
return DONE;
}
gcTrigger.runGc();
//第二次調用 主動GC 在判斷引用 減少誤報
removeWeaklyReachableReferences();
if (!gone(reference)) {
//到這里就有可能出現內存泄露了
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
// 關鍵時刻 heapDumper 實現是 com.squareup.leakcanary.AndroidHeapDumper
File heapDumpFile = heapDumper.dumpHeap();
//生成 dump 文件
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.
return RETRY;
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
// 將dump文件構造成HeapDump 對象
HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
.referenceName(reference.name)
.watchDurationMs(watchDurationMs)
.gcDurationMs(gcDurationMs)
.heapDumpDurationMs(heapDumpDurationMs)
.build();
//HeapDump 對象 heapdumpListener對應的是ServiceHeapDumpListener的analyze()
heapdumpListener.analyze(heapDump);
}
return DONE;
}
private boolean gone(KeyedWeakReference reference) {
return !retainedKeys.contains(reference.key);
}
private void removeWeaklyReachableReferences() {
// WeakReferences are enqueued as soon as the object to which they point to becomes weakly
// reachable. This is before finalization or garbage collection has actually happened.
KeyedWeakReference ref;
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
retainedKeys.remove(ref.key);
}
}
```
`removeWeaklyReachableReferences`調用了兩次,第一次調用后,如果系統gc正常回收,retainedKeys就不會再包含`KeyedWeakReference` 的key;如果沒有回收,會手動調用gc,再判斷,減少誤報;如果兩次gc后,對象仍然沒有被回收,可能就是真的內存泄露了。再調用heapDumper.dumpHeap()得到內存數據。heapDumper對應的是`AndroidHeapDumper`,`LeakDirectoryProvider` 指定了dumperHeap()的文件夾路徑。
`heapDumper.dumpHeap();`生成文件后,并且生成HeapDump 對象,HeapDump 對象 heapdumpListener對應的是`ServiceHeapDumpListener`的analyze()
+ com.squareup.leakcanary.AndroidHeapDumper
```java
@Override
protected @NonNull HeapDumper defaultHeapDumper() {
LeakDirectoryProvider leakDirectoryProvider =
LeakCanaryInternals.getLeakDirectoryProvider(context);
return new AndroidHeapDumper(context, leakDirectoryProvider);
}
@Override @Nullable
public File dumpHeap() {
File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();
if (heapDumpFile == RETRY_LATER) {
return RETRY_LATER;
}
FutureResult<Toast> waitingForToast = new FutureResult<>();
showToast(waitingForToast);
if (!waitingForToast.wait(5, SECONDS)) {
CanaryLog.d("Did not dump heap, too much time waiting for Toast.");
return RETRY_LATER;
}
Notification.Builder builder = new Notification.Builder(context)
.setContentTitle(context.getString(R.string.leak_canary_notification_dumping));
Notification notification = LeakCanaryInternals.buildNotification(context, builder);
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
int notificationId = (int) SystemClock.uptimeMillis();
notificationManager.notify(notificationId, notification);
Toast toast = waitingForToast.get();
try {
Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
cancelToast(toast);
notificationManager.cancel(notificationId);
return heapDumpFile;
} catch (Exception e) {
CanaryLog.d(e, "Could not dump heap");
// Abort heap dump
return RETRY_LATER;
}
}
```
dumpHeap會彈出一個通知,顯示Dumping,并調用`Debug.dumpHprofData(heapDumpFile.getAbsolutePath())`來保存內存,如果異常dumpHprofData異常會重試。
如果dump成功heapDumper.dumpHeap(),會構造HeapDump 對象,`ensureGone() --> heapdumpListener.analyze(heapDump);` 并傳遞給`heapdumpListener`。前邊我們說過`heapdumpListener`對應的是`ServiceHeapDumpListener`的analyze()
> heapdumpListener 是在 `com.squareup.leakcanary.LeakCanary#install()`中調用 `listenerServiceClass`,`heapDumpListener()`
```java
// com.squareup.leakcanary.ServiceHeapDumpListener#analyze
@Override
public void analyze(@NonNull HeapDump heapDump) {
checkNotNull(heapDump, "heapDump");
HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
// com.squareup.leakcanary.internal.HeapAnalyzerService#runAnalysis
public static void runAnalysis(Context context, HeapDump heapDump,
Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
setEnabledBlocking(context, HeapAnalyzerService.class, true);
setEnabledBlocking(context, listenerServiceClass, true);
Intent intent = new Intent(context, HeapAnalyzerService.class);
intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());
intent.putExtra(HEAPDUMP_EXTRA, heapDump);
ContextCompat.startForegroundService(context, intent);
}
```
**[注意]**:`DisplayLeakActivity`,已經調用pm.setComponentEnabledSetting()方法激活組件了,但listenerServiceClass對應的`DisplayLeakService`和`HeapAnalyzerService`還沒有激活,所以在啟動`HeapAnalyzerService`時,要先把這兩個組件激活。
`HeapAnalyzerService`是一個前臺IntentService,最終回調到他的`onHandleIntentInForeground`方法:
```java
@Override
protected void onHandleIntentInForeground(@Nullable Intent intent) {
if (intent == null) {
CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
return;
}
String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
HeapAnalyzer heapAnalyzer =
new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses);
//生成泄露對象引用鏈
AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,
heapDump.computeRetainedHeapSize);
// listenerClassName=DisplayLeakService
AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
}
```
在onHandleIntentInForeground中根據Intent的參數,構造了heapAnalyzer ,調用`heapAnalyzer.checkForLeak()`得到泄露對象的引用鏈。
```java
public static void sendResultToListener(@NonNull Context context,
@NonNull String listenerServiceClassName,
@NonNull HeapDump heapDump,
@NonNull AnalysisResult result) {
Class<?> listenerServiceClass;
// DisplayLeakService
try {
listenerServiceClass = Class.forName(listenerServiceClassName);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
Intent intent = new Intent(context, listenerServiceClass);
File analyzedHeapFile = AnalyzedHeap.save(heapDump, result);
if (analyzedHeapFile != null) {
intent.putExtra(ANALYZED_HEAP_PATH_EXTRA, analyzedHeapFile.getAbsolutePath());
}
ContextCompat.startForegroundService(context, intent);
}
```
返回的結果通過AnalyzedHeap.save(heapDump, result)保存到了一個文件沖,并返回給listenerServiceClassName代表的Sersvice,即DisplayLeakService,這個服務也是一個前臺IntentService,最終回調到DisplayLeakService的onHeapAnalyzed方法:
```java
public class DisplayLeakService extends AbstractAnalysisResultService {
@Override
protected final void onHeapAnalyzed(@NonNull AnalyzedHeap analyzedHeap) {
HeapDump heapDump = analyzedHeap.heapDump;
AnalysisResult result = analyzedHeap.result;
String leakInfo = leakInfo(this, heapDump, result, true);
CanaryLog.d("%s", leakInfo);
heapDump = renameHeapdump(heapDump);
boolean resultSaved = saveResult(heapDump, result);
String contentTitle;
if (resultSaved) {
PendingIntent pendingIntent =
DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey);
if (result.failure != null) {
contentTitle = getString(R.string.leak_canary_analysis_failed);
} else {
String className = classSimpleName(result.className);
if (result.leakFound) {
if (result.retainedHeapSize == AnalysisResult.RETAINED_HEAP_SKIPPED) {
if (result.excludedLeak) {
contentTitle = getString(R.string.leak_canary_leak_excluded, className);
} else {
contentTitle = getString(R.string.leak_canary_class_has_leaked, className);
}
} else {
String size = formatShortFileSize(this, result.retainedHeapSize);
if (result.excludedLeak) {
contentTitle =
getString(R.string.leak_canary_leak_excluded_retaining, className, size);
} else {
contentTitle =
getString(R.string.leak_canary_class_has_leaked_retaining, className, size);
}
}
} else {
contentTitle = getString(R.string.leak_canary_class_no_leak, className);
}
}
String contentText = getString(R.string.leak_canary_notification_message);
showNotification(pendingIntent, contentTitle, contentText);
} else {
onAnalysisResultFailure(getString(R.string.leak_canary_could_not_save_text));
}
afterDefaultHandling(heapDump, result, leakInfo);
}
}
```
將泄露結果在系統通知欄上顯示。
以上,LeakCanary從注冊Activity和Fragment的生命周期回調,到`onDestory`中開始`RefWatcher.watch()`,然后dumpHeap,再分析內存泄露引用鏈,最后將結果展示到系統通知欄的整個過程就基本分析完畢了。

## 總結
總結下看源碼過程中學到的知識點:
+ 內存泄露如何判斷(WeakReference)
+ Debug.dumpHprofData()獲取內存數據,Debug.isDebuggerConnected()判斷是否debug
+ setComponentEnabledSetting()動態開關組件
+ Looper.myQueue().addIdleHandler()注冊UI線程空閑回調
[鏈接](https://www.jianshu.com/p/b857736d1461)
- 計算機基礎
- 簡答1
- 簡答2
- 專案
- 淺談0與1
- 淺談TCP_IP
- 淺談HTTP
- 淺談HTTPS
- 數據結構與算法
- 常見數據結構簡介
- 常用算法分析
- 常見排序算法
- Java數據結構類問題簡答
- 專案
- HashMap
- 淺談二叉樹
- 算法題
- 算法001_TopN問題
- 算法002_漢諾塔
- 編程思想
- 雜說
- 觀點_優秀程序設計的18大原則
- 設計模式_創建型
- 1_
- 2_
- 設計模式_結構型
- 1_
- 2_
- 設計模式_行為型
- 1_
- 2_
- Java相關
- 簡答1
- 簡答2
- 專案
- 淺談String
- 淺談Java泛型
- 淺談Java異常
- 淺談動態代理
- 淺談AOP編程
- 淺談ThreadLocal
- 淺談Volatile
- 淺談內存模型
- 淺談類加載
- 專案_數據結構
- 淺談SpareArray
- Android相關
- Android面試題
- 專案
- 推送原理解析
- Lint
- 自定義Lint
- Lint使用
- 優化案
- Apk體積優化
- Kotlin相關
- 簡答1
- 簡答2
- 三方框架相
- Okhttp3源碼分析
- ButterKnife源碼分析
- Glide4源碼分析
- Retrofit源碼分析
- RxJava源碼分析
- ARouter源碼分析
- LeakCanary源碼分析
- WMRouter源碼分析
- 跨平臺相關
- ReactNative
- Flutter
- Hybrid
- 優質源
- 資訊源
- 組件源
- 推薦