不要小看那些信靠上帝的人,祂能借着他们成就大事
本篇文章就是为了说明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。