前言
Android 应用开发过程中,经常会遇到各种情况下的 Activity 内存泄露。一旦出现泄露,可能会引起应用内存占用过高,出现卡顿或者 OutOfMemoryError,所以在开发阶检测出内存泄露问题非常有必要。而 LeakCanary 是一个在开发阶段检测 Activity 内存泄露的一个利器。
应用接入
添加依赖
在 build.gradle 文件中,添加如下依赖,
dependencies {
...
// debugImplementation because LeakCanary should only run in debug builds.
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3’
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3'
...
}
注意:上面的使用的是 debugImplementation ,表示只会在 debug 构建时使用该依赖。而 releaseImplementation 表示在 release 构建时,使用此依赖,而 leakcanary-android-no-op 实际上没有做任何操作。
代码接入
一般在自定义 Application 类的 onCreate() 方法加入下面的代码即可,
if (BuildConfig.DEBUG) {
// Memory leak analysis,Only valid on debug version
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);
}
只有在 debug 版本时,才调用 LeakCanary.install() 方法。
当发生泄露时,会在通知栏有一个通知,点击进去后会看到下面一个界面,详细的展示了内存泄露的 trace,如下图,
实现原理
LeakCanary 的实现原理,主要依赖两个知识点,
- android.app.Application.ActivityLifecycleCallbacks:应用中所有 Activity 生命周期检测
- java.lang.ref.WeakReference 的两个参数构造方法:public WeakReference(T referent, ReferenceQueue<? super T> q)
ActivityLifecycleCallbacks 监控 Activity 的生命周期
LeakCanary 在 Application 中调用 install() 方法后,会通过 Application 注册 activity 生命周期回调接口,这样每个 Activity 的 onDestroy() 方法被调用的时候,LeakCanary 都能知道。具体的代码实现在LeakCanary 库的 ActivityRefWatcher 类中,
- lifecycleCallbacks 对象的实现
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new Application.ActivityLifecycleCallbacks() {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override public void onActivityStarted(Activity activity) {
}
@Override public void onActivityResumed(Activity activity) {
}
@Override public void onActivityPaused(Activity activity) {
}
@Override public void onActivityStopped(Activity activity) {
}
@Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override public void onActivityDestroyed(Activity activity) {
ActivityRefWatcher.this.onActivityDestroyed(activity);
}
};
- 注册 activity 的生命周期回调接口
public void watchActivities() {
// Make sure you don't get installed twice.
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks); //
}
WeakReference 观测 Activity 的引用状态
在 onActivityDestroyed 方法中,会使用 RefWatcher 来 watch 传入的 activity 对象,而 RefWatcher 类里面,会用到 WeakReference 的第二个构造函数,来观测 activity 对象的回收情况,核心代码如下,
final class KeyedWeakReference extends WeakReference<Object> {
...
}
private final ReferenceQueue<Object> queue;
public void watch(Object watchedReference, String referenceName) {
...
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
...
ensureGoneAsync(watchStartNanoTime, reference);
}
从上面的实现可知,KeyedWeakReference 继承自 WeakReference,所以实际还是基于 WeakReference 第二个构造函数实现的。在构造 KeyedWeakReference 对象时,第二个参数传入的一个 ReferenceQueue。
在 watch() 方法最后,调用了 ensureGoneAsync() 方法,在这个方法中,会主动触发一次 gc,并且检测 ReferenceQueue 中是否包含该 activity 对象,如果不包含,表示这个 activity 被强引用,产生了内存泄露,这时就会把 heap 信息 dump 出来,并使用 haha 对 HeapDump 对象进行分析,得到分析结果后进行通知栏提示,核心代码如下,
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
...
gcTrigger.runGc(); //主动触发 gc
removeWeaklyReachableReferences();
if (!gone(reference)) {
...
File heapDumpFile = heapDumper.dumpHeap();
...
//分析HeapDump对象
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
}
return DONE;
}
总结
LeakCanary 可以帮助开发者在调试阶段,方便的检测出 Activity 内存泄露问题。主要通过 Application.ActivityLifecycleCallbacks 和 java.lang.ref.WeakReference 这两个知识点来实现。当出现内存泄露时,通过 haha 来分析 dump heap,并提示用户。