LeakCanary相关解析

2017-06-21  本文已影响0人  黄大大吃不胖

Android开发中经常会出现OOM的情况,使用LeakCanary可以对于OOM进行检测与分析,那么这一篇就通过分析LeakCanary
的源码来看看是如何检测内存泄露的。

LeakCanary使用

gradle中添加依赖,如下

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'

然后在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);
}

通过这样就实现了LeakCanary使用。

LeakCanary源码分析

上面使用的关键代码就是LeakCanary.install(this),来看看这里的源码

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


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

     return refWatcher;
 }

这里其实是调用了AndroidRefWatcherBuilder类中的buildAndInstall方法,这里首先是生成了一个RefWatcher,接下来把这个对象与提供的pplication传入到
ActivityRefWatcher.install方法,我们去看这个方法,如下

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

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

这里主要调用了watchActivities方法,而这个方法里面则是向application里注册了一个ActivitylifecycleCallbacks的回调函数,可以用来监听Application整个生命周期所有Activity的lifecycle事件

接下来我们看一下LeakCanary中提供的这个lifecycleCallbacks,如下

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);
    }
};

可以看到这里只是监听了所有Activity的onActivityDestroyed事件,当Activity被Destory时,调用ActivityRefWatcher.this.onActivityDestroyed(activity)函数。

接下来看一下这个ActivityRefWatcher中的onActivityDestroyed方法到底做了什么事情,源码如下

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

这里调用了RefWatcher的watch方法,通过调用后,如下

private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
    this.watchExecutor.execute(new Retryable() {
        public Result run() {
            return RefWatcher.this.ensureGone(reference, watchStartNanoTime);
        }
    });
}

Result ensureGone(KeyedWeakReference reference, long watchStartNanoTime) {
    long gcStartNanoTime = System.nanoTime();
    long watchDurationMs = TimeUnit.NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
    this.removeWeaklyReachableReferences();
    if(this.debuggerControl.isDebuggerAttached()) {
        return Result.RETRY;
    } else if(this.gone(reference)) {
        return Result.DONE;
    } else {
        this.gcTrigger.runGc();
        this.removeWeaklyReachableReferences();
        if(!this.gone(reference)) {
            long startDumpHeap = System.nanoTime();
            long gcDurationMs = TimeUnit.NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
            File heapDumpFile = this.heapDumper.dumpHeap();
            if(heapDumpFile == HeapDumper.RETRY_LATER) {
                return Result.RETRY;
            }

            long heapDumpDurationMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
            this.heapdumpListener.analyze(new HeapDump(heapDumpFile, reference.key, reference.name, this.excludedRefs, watchDurationMs, gcDurationMs, heapDumpDurationMs));
        }

        return Result.DONE;
    }
}

我们主要看一下ensureGone中的内容

在一个 activity 传给 RefWatcher 时会创建一个唯一的key对应这个activity,该key存入一个集合retainedKeys中。也就是说,所有我们想要观测的activity对应的唯一key都会被放入retainedKeys集合中。

基于我们对ReferenceQueue的了解,只要把队列中所有的reference取出来,并把对应retainedKeys里的key移除,剩下的key对应的对象都没有被回收。

1.ensureGone 首先调用removeWeaklyReachableReferences把已被回收的对象的key从retainedKeys移除,剩下的key都是未被回收的对象;

2.if(gone(reference))用来判断某个reference的key是否仍在retainedKeys里,若不在,表示已回收,否则继续;

3.gcTrigger.runGc();手动出发GC立即把所有WeakReference引用的对象回收;

4.removeWeaklyReachableReferences();再次清理retainedKeys,如果该reference还在retainedKeys里(if(!gone(reference))),表示泄漏;

5.利用heapDumper把内存情况dump成文件,并调用heapdumpListener进行内存分析,进一步确认是否发生内存泄漏。

6.如果确认发生内存泄漏,调用DisplayLeakService发送通知。

到这里leakCanary对于内存泄露的检测分析流程就完成了。

关于ActivityLifecycleCallbacks

ActivityLifecycleCallbacks是Application中声明的一个内部接口,结构如下

public interface ActivityLifecycleCallbacks {
    void onActivityCreated(Activity activity, Bundle savedInstanceState);
    void onActivityStarted(Activity activity);
    void onActivityResumed(Activity activity);
    void onActivityPaused(Activity activity);
    void onActivityStopped(Activity activity);
    void onActivitySaveInstanceState(Activity activity, Bundle outState);
    void onActivityDestroyed(Activity activity);
}

Application提供有一个registerActivityLifecycleCallbacks()的方法,需要传入的参数就是这个ActivityLifecycleCallbacks接口,作用和你猜的没错,就是在你调用这个方法传入这个接口实现类后,系统会在每个Activity执行完对应的生命周期后都调用这个实现类中对应的方法。

另外registerActivityLifecycleCallbacks()内部是把ActivityLifecycleCallbacks加到一个集合中,所以ActivityLifecycleCallbacks可以添加多个

而且ActivityLifecycleCallbacks只是在项目初始化的时候被装到集合中,并不会初始化任何东西,直到真正Activity执行到相应的生命周期,才会执行ActivityLifecycleCallbacks中的操作

所有使用ActivityLifecycleCallbacks会使我们代码会更加有创造力,因为使用ActivityLifecycleCallbacks可以实现很多不同的功能,而且是优雅的实现

简单想了一下ActivityLifecycleCallbacks可以实现什么功能。

等等。。。

上一篇下一篇

猜你喜欢

热点阅读