LeakCanary 是一个非常强大的内存泄露监测工具,可以实现打印内存泄露的信息。Android 的内存泄露检测主要是对Activity的回收情况检测,如果Activity调用了onDestroy方法,没有及时回收就是意味着出现了内存泄露,Android Studio 提供的内存泄露工具就是通过这种方式实现的,然后通过dump分析对应的依赖情况,LeakCanary的原理也是如此,下面我们通过源码分析来验证这一点。
LeakCanary 初始化
LeakCanary.install(this);
LeakCanary 2.0 的结构已经改变了,不再是通过这种方式初始化。
public final class LeakCanary {
public static @NonNull RefWatcher install(@NonNull Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {
return new AndroidRefWatcherBuilder(context);
}
}
通过阅读LeakCanary的源码,结合AndroidRefWatcherBuilder类,可以看到最终创建ActivityRefWatcher、AndroidOFragmentRefWatcher。
ActivityRefWatcher
ActivityRefWatcher的代码量不多,关键的信息就是 application.registerActivityLifecycleCallbacks,从这里验证了我们的初步想法,就是通过监听 Activity的生命周期,在onActivityDestroyed的时候把Activity丢进监听的任务中。
public final class ActivityRefWatcher {
public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) {
Application application = (Application) context.getApplicationContext();
ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
}
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
};
...
}
AndroidOFragmentRefWatcher
上面的ActivityRefWatcher是对Activity的监听,AndroidOFragmentRefWatcher是对Fragment进行监听,不过仅仅支持Android O以上的版本。
@RequiresApi(Build.VERSION_CODES.O) //
class AndroidOFragmentRefWatcher implements FragmentRefWatcher {
private final RefWatcher refWatcher;
AndroidOFragmentRefWatcher(RefWatcher refWatcher) {
this.refWatcher = refWatcher;
}
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);
}
}
RefWatcher
接下来我们看看 refWatcher.watch这个方法,这里是检测调用了onDestroy的Activity,然后调用了ensureGoneAsync
的方法,这里可以看到,使用了WatchExecutor执行异步任务,他的实现类是AndroidWatchExecutor
,是基于HandlerThread实现,这里就不展开。核心是ensureGone
方法,这个方法中,调用了 gcTrigger.runGc()
对当前的内存进行回收,然后调用removeWeaklyReachableReferences
移除已经回收的对象,经过内存回收如果对象还没有被回收就说明这个对象(Activity、Fragment)存在了内存泄露问题了,接下来就是通过 HeapDump
类获取内存的镜像信息保存到文件中,然后通过heapdumpListener.analyze
进一步分析。
public final class RefWatcher {
public static final RefWatcher DISABLED = new RefWatcherBuilder<>().build();
private final WatchExecutor watchExecutor;
private final DebuggerControl debuggerControl;
private final GcTrigger gcTrigger;
private final HeapDumper heapDumper;
private final HeapDump.Listener heapdumpListener;
private final HeapDump.Builder heapDumpBuilder;
private final Set retainedKeys;
private final ReferenceQueue
获取dump的方式:Debug.dumpHprofData(heapDumpFile.absolutePath)
ServiceHeapDumpListener
在上一步已经生成了Dump文件,在这里监听器中,启动了一个HeapAnalyzerService前台服务,通过前台服务进行下一步分析。
public final class ServiceHeapDumpListener implements HeapDump.Listener {
private final Context context;
private final Class extends AbstractAnalysisResultService> listenerServiceClass;
public ServiceHeapDumpListener(@NonNull final Context context,
@NonNull final Class extends AbstractAnalysisResultService> listenerServiceClass) {
this.listenerServiceClass = checkNotNull(listenerServiceClass, "listenerServiceClass");
this.context = checkNotNull(context, "context").getApplicationContext();
}
@Override public void analyze(@NonNull HeapDump heapDump) {
checkNotNull(heapDump, "heapDump");
HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
}
HeapAnalyzerService
从这个服务中使用HeapAnalyzer类对heapDump文件进行分析。
public final class HeapAnalyzerService extends ForegroundService
implements AnalyzerProgressListener {
@Override protected void onHandleIntentInForeground(@Nullable Intent intent) {
if (intent == null) {
CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
return;
}
String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
HeapAnalyzer heapAnalyzer =
new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses);
AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,
heapDump.computeRetainedHeapSize);
AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
}
}
最后
到这里就不再往下面分析,对Dump的分析是一个非常复杂的过程,这里用了一个叫haha
的库,也是square开发的,好调皮的名字。
com.squareup.haha:haha:2.0.4