LeakCanary 以1.5版本为例子,简单分析其中的原理。 LeakCanary 可以检测App的内存泄漏,在我们自定义的 Application 的 onCreate() 方法中执行 LeakCanary.install(this); 这行代码即可。代码很简单,我们看看它做了什么
public static RefWatcher install(Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
这个方法前两行是作一些参数配置,重点看 buildAndInstall() 方法
public RefWatcher buildAndInstall() {
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
LeakCanary.enableDisplayLeakActivity(context);
ActivityRefWatcher.installOnIcsPlus((Application) context, refWatcher);
}
return refWatcher;
}
通过 build() 创建一个 RefWatcher 对象,这是个build模式,看看它的代码
public final RefWatcher build() {
if (isDisabled()) {
return RefWatcher.DISABLED;
}
ExcludedRefs excludedRefs = this.excludedRefs;
if (excludedRefs == null) {
excludedRefs = defaultExcludedRefs();
}
HeapDump.Listener heapDumpListener = this.heapDumpListener;
if (heapDumpListener == null) {
heapDumpListener = defaultHeapDumpListener();
}
DebuggerControl debuggerControl = this.debuggerControl;
if (debuggerControl == null) {
debuggerControl = defaultDebuggerControl();
}
HeapDumper heapDumper = this.heapDumper;
if (heapDumper == null) {
heapDumper = defaultHeapDumper();
}
WatchExecutor watchExecutor = this.watchExecutor;
if (watchExecutor == null) {
watchExecutor = defaultWatchExecutor();
}
GcTrigger gcTrigger = this.gcTrigger;
if (gcTrigger == null) {
gcTrigger = defaultGcTrigger();
}
return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener,
excludedRefs);
}
其中 DebuggerControl 对应 AndroidDebuggerControl,HeapDumper 对应 AndroidHeapDumper,WatchExecutor 对应 AndroidWatchExecutor,GcTrigger 对应它内部的DEFAULT 对象,这几个类都比较简单,但比较有意思。
第一个就不说了,看 AndroidHeapDumper,这里面有几个知识点:FutureResult 的成员变量 AtomicReference 和 CountDownLatch,这是为了并发而准备的,CountDownLatch 是一个线程等待其他线程各自执行完毕后再执行本身,AtomicReference 则是保证原子性的安全;在showToast()方法中设置toast时,这里用了Looper队列MessageQueue的 addIdleHandler() 方法,它是UI空闲时执行,返回值为false则表示执行一次,true则是UI一旦空闲,就执行,没次数限制。
AndroidWatchExecutor 类是个执行功能类,它的构造方法中创建一个 HandlerThread 的子线程,然后把它对应的Looper作为参数,传入Handler的构造方法中,此时这个Handler就一直是在子线程中执行各种逻辑了,它里面通过代码执行延迟操作。 GcTrigger 这个里面执行的是GC操作, Runtime.getRuntime().gc() 比着 System.gc() 的优先级要高,所以优先使用这个。
继续回到 buildAndInstall() 方法,LeakCanary.enableDisplayLeakActivity(context) 的意思是打开显示内存泄漏的页面,这个页面把泄漏的数据以列表的形式展示出来;ActivityRefWatcher.installOnIcsPlus((Application) context, refWatcher) 这行代码则是对Activity执行监听的入口,看看对应的方法
public static void installOnIcsPlus(Application application, RefWatcher refWatcher) {
if (SDK_INT < ICE_CREAM_SANDWICH) {
// If you need to support Android < ICS, override onDestroy() in your base activity.
return;
}
ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
activityRefWatcher.watchActivities();
}
public void watchActivities() {
// Make sure you don't get installed twice.
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}
public void stopWatchingActivities() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);
}
首先是个版本判断,要大于或等于14版本,这里其实就是给 application 注册一个Activity的生命周期回调,注册之前先取消之前的,防止重复注册,看看 lifecycleCallbacks 的方法
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new Application.ActivityLifecycleCallbacks() {
...
@Override public void onActivityDestroyed(Activity activity) {
ActivityRefWatcher.this.onActivityDestroyed(activity);
}
};
void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
在Activity销毁时,执行了 refWatcher.watch(activity),看看它里面的代码
public void watch(Object watchedReference) {
watch(watchedReference, "");
}
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) {
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}
通过 randomUUID() 方法创建随机的字符串,然后把它存入set中;KeyedWeakReference 是个然引用,这里面传入的也有 ReferenceQueue 队列,看到这里,是不是很熟悉,Glide 的图片缓存中的软引用缓存,也是用的这种技术。看到这里,了解软引用和队列知识点的童鞋,就可以猜测出下面的步骤了
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime();
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
removeWeaklyReachableReferences();
if (debuggerControl.isDebuggerAttached()) {
// The debugger can create false leaks.
return RETRY;
}
if (gone(reference)) {
return DONE;
}
gcTrigger.runGc();
removeWeaklyReachableReferences();
if (!gone(reference)) {
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
File heapDumpFile = heapDumper.dumpHeap();
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.
return RETRY;
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
}
return DONE;
}
对象的引用,如果强引用还有链接,则软引用中对象也会一直存在,如果强引用的链接都被切断了,那么软引用中很容易就被gc回收了,并且会把它放入传入软引用构造中的队列里面,所以 removeWeaklyReachableReferences() 的意思就是检查队列中是否有对象,如果有,说明正常回收,没有内存泄漏,因为此时 Activity 是执行了onDestroy() 方法,如果有内存泄漏,它被别的对象持有,那么它是无法释放资源。 gone() 方法是判断key是否在set中,还是判断Activity是否被回收,如果没被回收,为了保险,这里执行了 gcTrigger.runGc(),也就是主动触发 gc 回收机制,睡眠100毫秒,重新执行 removeWeaklyReachableReferences() 和 gone() 方法,如果此时 Activity 还没被回收,那就说明是内存泄漏了,heapDumper 是 AndroidHeapDumper,在它里面去读取对象的引用路径等信息。读取分析数据用的是 haha 三方库。
在 LeakCanary 1.6 版本中,有对 Fragment 的监听:在手机系统O版本以上,Fragment功能中也新增了对它的声明周期监听回调,
public @NonNull RefWatcher buildAndInstall() {
if (LeakCanaryInternals.installedRefWatcher != null) {
throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
}
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
if (watchActivities) {
ActivityRefWatcher.install(context, refWatcher);
}
if (watchFragments) {
FragmentRefWatcher.Helper.install(context, refWatcher);
}
}
LeakCanaryInternals.installedRefWatcher = refWatcher;
return refWatcher;
}
AndroidOFragmentRefWatcher:
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);
}
这里是根据声明周期,把 rootview 和 fragment 添加到 RefWatcher 的监听方法中,关键就是 watchFragments(Activity activity) 方法是如何触发的,继续看 FragmentRefWatcher,创建了 Helper 对象,把 fragmentRefWatchers 作为参数传入构造方法中,用 ActivityLifecycleCallbacks 包裹了一层,ActivityLifecycleCallbacks 被添加到Application 的回调中,activity销毁时执行 ActivityLifecycleCallbacks 的 onActivityCreated() 方法,此时就触发了 AndroidOFragmentRefWatcher 的 watchFragments()方法。
如果我们想对其他类型的对象进行内存泄漏检测怎么办?Application中的 LeakCanary.install(this) 返回的是 RefWatcher 对象,此时可以做一个单例的Application对象,对外暴露 RefWatcher,这样其他地方就可以直接使用 RefWatcher 的 watch(Object watchedReference) 方法了,但这里需要自己控制时机,因为这里不会根据Activity的声明周期来触发。
LeakCanary 的2.0版本,做了部分修改,使用它时不需要手动LeakCanary.install(this)初始化了,应为它自己做了。里面有个继承 ContentProvider 的内容观察者,观察者的初始化方法居然比 Application 的初始化方法还要早;用了新的内存解析库代替了haha库,极大的减少了内存占用,提高了n被的速度。