LeakCanary原理初探


在没有 LeakCanary 的时候,我们想要找出内存泄露,首先想到的是手动检查代码,祈祷可以找到自己可以确认的可能出现内存泄露的代码,但是总会无功而返。于是我们调试代码,拿到出现了内存泄露的堆内存信息,打开 MAT 进行分析,讲真,MAT 真是不太容易看懂。而第一次看到这个库就被功能和易用性惊艳到了,用 LeakCanary 检测内存泄露也有一段时间了,工作效率提高了不少,能提早发现内存泄露 bug,避免线上 bug 率。

用了这么久,也不知道其内在的原理到底是怎样的,今天就来对其原理进行简单的了解。

有几个问题,是需要找到答案的:

  1. LeakCanary 是怎样分析出内存引用出现泄露了
  2. LeakCanary 是怎样监听的所有 Activity 的生命周期的

本文基于 LeakCanary 1.5.1 版本。

使用

我们现从 LeakCanary 的使用说起,一般我们在使用的时候,只需要两步就可以完成。

1.添加依赖

dependencies {
    debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1'
    releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
    testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
}

2.安装 LeakCanary

public class ExampleApplication 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...
  }
}

别忘了注册 Application

这样就可以使用 LeakCanary 对应用进行内存检测的分析了,如果出现了内存泄露,手机通知栏就会出现提示,点进去可以看见内存泄露的地方和所有的引用过程。详细的使用方法见我的另一篇文章:Android 检测内存泄露

原理分析

我们首先从 LeakCanary 的静态方法 install() 开始。

public static RefWatcher install(Application application) {
    return ((AndroidRefWatcherBuilder)refWatcher(application)
        .listenerServiceClass(DisplayLeakService.class)
        .excludedRefs(AndroidExcludedRefs.createAppDefaults().build()))
        .buildAndInstall();
}

install 方法分了四步完成了 LeakCanary 的所有准备工作。

1.调用 LeakCanary.refWatcher(context) 方法创建一个 AndroidRefWatcherBuilder 对象,此对象是 RefWatcher 的默认创建者。

2.绑定 DisplayLeakService,DisplayLeakService 用来处理堆内存信息的结果并显示通知。继承自 AbstractAnalysisResultService,该 Service 有一个抽象方法:onHeapAnalyzed(HeapDump var1, AnalysisResult var2),DisplayLeakService 重写此方法,获取其中的内存泄露信息,显示到通知栏。该 Service 继承自 IntentService,自动在后台线程执行任务,任务完成结束 Service。

3.创建默认的 AndroidExcludedRefs 类,来排除一些会被误认为或本身就存在内存泄露的系统问题。

4.最后创建并安装 RefWatcher。RefWatcher 就是用来检测 Activity 是否泄露的。

public RefWatcher buildAndInstall() {
    RefWatcher refWatcher = this.build();
    if(refWatcher != RefWatcher.DISABLED) {
        LeakCanary.enableDisplayLeakActivity(this.context);
        ActivityRefWatcher.install((Application)this.context, refWatcher);
    }

    return refWatcher;
}

buildAndInstall() 方法创建了一个 RefWatcher 对象,如果该对象如果不是 RefWatcher.DISABLED(RefWatcher 内部的一个空实现,用于 release 版本,这样 release 版本就不会进行内存泄露检测),则使 DisplayLeakActivity 有效,DisplayLeakActivity 是一个 Activity,用于在内存泄露后显示引用链,然后使用 ActivityRefWatcher 安装 refWatcher 对象。

com.squareup.leakcanary.ActivityRefWatcher.java

public static void install(Application application, RefWatcher refWatcher) {
    (new ActivityRefWatcher(application, refWatcher)).watchActivities();
}

public void watchActivities() {
    this.stopWatchingActivities();
    this.application.registerActivityLifecycleCallbacks(this.lifecycleCallbacks);
}

public void stopWatchingActivities() {
    this.application.unregisterActivityLifecycleCallbacks(this.lifecycleCallbacks);
}

install 方法 创建了自己的对象,并开始 watchActivities,watchActivities() 调用了 Application 的 registerActivityLifecycleCallbacks 方法,注册应用所有 Activity 的生命周期监听器,注册监听之前调用了 stopWatchingActivities(),保证不重复注册监听。

this.lifecycleCallbacks 监听所有 Activity 的生命周期是想干嘛?

private final ActivityLifecycleCallbacks lifecycleCallbacks = new ActivityLifecycleCallbacks() {
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
    }

    public void onActivityStarted(Activity activity) {
    }

    public void onActivityResumed(Activity activity) {
    }

    public void onActivityPaused(Activity activity) {
    }

    public void onActivityStopped(Activity activity) {
    }

    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
    }

    public void onActivityDestroyed(Activity activity) {
        ActivityRefWatcher.this.onActivityDestroyed(activity);
    }
};
void onActivityDestroyed(Activity activity) {
    this.refWatcher.watch(activity);
}

可以看到,该监听器只关注 Activity 的销毁,到这里,我们就知道 refWatcher.watch(activity) 方法特别重要,它拿到了所有 Activity 销毁的回调,从这里就开始了对 Activity 对象的内存泄露检测。

未完待续

参考文章:

  1. leakcanary wiki
  2. 带你学开源项目:LeakCanary-如何检测 Activity 是否泄漏
  3. LeakCanary 工作原理浅析

评论