不要小看那些信靠上帝的人,祂能借着他們成就大事
本篇文章就是爲了說明LeakCanary是如何打造Android平臺上的內存泄漏監測過程。按照剖析LeakCanary—— 中篇的思路進行展開論述。
1. 總覽LeakCanary的Android實現過程
這裏先羅列一些涉及Android平臺的具體實現類
- AndroidWatchExecutor —> WatchExecutor
- AndroidRefWatcherBuilder —> RefWatcherBuilder
- LeakCanaryInternals : 可以理解爲Android平臺的工具類
- AndroidExcludedRefs:枚舉(過濾Android SDK中的內存泄漏)
- HeapAnalyzerService -->ForegroundService —> IntentService
- DisplayLeakService —> AbstractAnalysisResultService —> ForegroundService —> IntentService
- ServiceHeapDumpListener —> HeapDump.Listener 監聽分析過程及結果。(HeapAnalyzerService,DisplayLeakService就是從這裏啓動的)
HeapAnalyzerService:分析堆內容肯定是個耗時操作,故LeakCanary採用開啓IntentService的方式處理;
同理,
DisplayLeakService :在使用LeakCanary的過程中,我們會看到一些通知(Notification) 以及展示泄漏情況的DisplayLeakActivity。
具體我們可以看到:
HeapAnalyzerService 在自己的runAnalysis()中啓動;
DisplayLeakService 在HeapAnalyzerService的onHandleIntentInForeground() 需要返回分析結果時才啓動(具體可以看 :AbstractAnalysisResultService)。
在分析結果沒有出來之前,DisplayLeakService的服務是不會啓動的。
2. AndroidWatchExecutor
AndroidWatchExecutorr是LeakCanary在Android系統中的監測者。
這裏利用**IdleHandler且是主線程的MessageQueue的IdleHandler **充分了體現LeakCanary的設計之巧妙啊…
關於IdleHandler可以查看 Handler的前世今生5 —— MessageQueue
通過MessageQueue的next()中我們可以知道,IdleHandler 只有在消息隊列爲空 或者 第一個消息在隊列中但還沒有到執行時間 的情況下會被執行。即:線程阻塞時會執行IdleHandler。
public final class AndroidWatchExecutor implements WatchExecutor {
private final Handler mainHandler;
private final Handler backgroundHandler;
...
@Override public void execute(@NonNull Retryable retryable) {
if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
waitForIdle(retryable, 0);
} else {
postWaitForIdle(retryable, 0);
}
}
private void postWaitForIdle(final Retryable retryable, final int failedAttempts) {
mainHandler.post(new Runnable() {
@Override public void run() {
waitForIdle(retryable, failedAttempts);
}
});
}
// 無論當前線程是否是主線程,最終都是要將事件放在主線程中執行。
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;
backgroundHandler.postDelayed(new Runnable() {
@Override public void run() {
Retryable.Result result = retryable.run();
if (result == RETRY) {
postWaitForIdle(retryable, failedAttempts + 1);
}
}
}, delayMillis);
}
}
3. AndroidRefWatcherBuilder
AndroidRefWatcherBuilder纔是LeakCanary在Android系統中真正執行者。
爲了更好滴了解 AndroidRefWatcherBuilder ,我們可以從實際出發,根據我們在Android的使用來探索其精髓。
3.1 LeakCanary在Android上的使用
LeakCanary在Android開發中,我們一般都是使用下面的代碼進行初始化的。
public class MainApp extends Application {
@Override public void onCreate() {
super.onCreate();
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...
}
}
我相信絕大多數地Android開發者對於上述代碼都不會陌生。
3.2 走進LeakCanary
- install()
創建一個AndroidRefWatcherBuilder對象並使其開始工作(過濾一些Android不必要的分析內容)
PS : AndroidExcludedRefs 是個枚舉,並不是ExcludedRefs的子類。
public static @NonNull RefWatcher install(@NonNull Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
- isInAnalyzerProcess()
作用:判斷監測內存泄漏的進程是否已經創建,從而避免重複創建。
實現:本質上 調用的是 LeakCanaryInternals 的isInServiceProcess()方法。感興趣的可以深入瞭解一下 LeakCanaryInternals 這個類。
這裏判斷的是HeapAnalyzerService,也證明了DisplayLeakService 並不是一直存在的。
public static boolean isInAnalyzerProcess(@NonNull Context context) {
Boolean isInAnalyzerProcess = LeakCanaryInternals.isInAnalyzerProcess;
// This only needs to be computed once per process.
if (isInAnalyzerProcess == null) {
isInAnalyzerProcess = isInServiceProcess(context, HeapAnalyzerService.class);
LeakCanaryInternals.isInAnalyzerProcess = isInAnalyzerProcess;
}
return isInAnalyzerProcess;
}
3.3 AndroidRefWatcherBuilder真面目
isAssignableFrom() 是從類繼承的角度判斷從屬關係的,調用者與參數都必須是C lass對象。
用法:父類.class.isAssignableFrom(子類.class)
返回值:false —— 調用者是參數的子類;否則返回true;
具體可以參考:java中isAssignableFrom()方法
public final class AndroidRefWatcherBuilder extends RefWatcherBuilder<AndroidRefWatcherBuilder> {
// 注意是默認觀察的延時時間 5s
private static final long DEFAULT_WATCH_DELAY_MILLIS = SECONDS.toMillis(5);
private final Context context;
private boolean watchActivities = true;
private boolean watchFragments = true;
// 這裏默認是false,意不意外?
private boolean enableDisplayLeakActivity = false;
// 這裏是涉及分析堆內存信息的服務
public @NonNull AndroidRefWatcherBuilder listenerServiceClass(
@NonNull Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
// 注意這裏:isAssignableFrom()方法
// 只要傳入的參數是DisplayLeakService或者其子類,就返回true.
enableDisplayLeakActivity = DisplayLeakService.class.isAssignableFrom(listenerServiceClass);
return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
}
// 真正的監聽操作在這裏
public @NonNull RefWatcher buildAndInstall() {
if (LeakCanaryInternals.installedRefWatcher != null) {
throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
}
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
// 是否要顯示 DisplayLeakActivity ,爲我們展示泄漏情況
if (enableDisplayLeakActivity) {
LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
}
// 觀察Activity的泄漏情況
if (watchActivities) {
ActivityRefWatcher.install(context, refWatcher);
}
// 觀察Fragment的泄漏情況
if (watchFragments) {
FragmentRefWatcher.Helper.install(context, refWatcher);
}
}
LeakCanaryInternals.installedRefWatcher = refWatcher;
return refWatcher;
}
...
}
相信你已經注意到ActivityRefWatcher 和 FragmentRefWatcher
簡要看一下:
- ActivityRefWatcher:在Activity銷燬的時候進行監聽
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
};
- FragmentRefWatcher是接口,其子類:
- SupportFragmentRefWatcher
- AndroidOFragmentRefWatcher (SDK >= 26)
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);
}
};
4. 總結
LeakCanary 充分利用了Android系統的特點來達到監測並分析內存泄漏的目的。這裏需要我們 重點回味的技巧:
- 使用IdleHandler來監測;
- 使用服務HeapAnalyzerService進行堆內存分析,若沒有返回分析結果,則不會啓動DisplayLeakService。