前一篇文章(LeakCanary 源码解析笔记整理(一))整理了有关 LeakCanary 检测 Activity 是否泄漏的相关源码以及原理的笔记。
然后这里再 继续对该库前期的实现 进行相关的笔记补充。且主要参考自:看完这篇 LeakCanary 原理分析,又可以虐面试官了!
上一篇有说过,能够监测 Activity 是否泄漏的关键是:
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityDestroyed(Activity activity) {
// 检测内存泄漏的关键,实际上,该方法可以检测所有对象是否内存泄漏,而不仅仅是 activity
refWatcher.watch(activity);
}
};
而实际上,不仅是监测 Activity,任何想要监测的对象,都是利用 refWatcher.watch(obj)
来实现的。
可以在自定义的 Application
中得到 RefWatcher
对象,然后通过该对象来实现监测某一对象的内存泄漏。(上一篇文章有提到 LeakCanary 库在 Android 4.0 之前是没法直接使用的,因此在 Android 4.0 之前需要手动在 Activity 的 onDestory()
方法中实现添加监听)
public class MyApplication extends Application {
private RefWatcher refWatcher;
@Override
public void onCreate() {
super.onCreate();
refWatcher = setupLeakCanary();
}
private RefWatcher setupLeakCanary() {
if (LeakCanary.isInAnalyzerProcess(this)) {
return RefWatcher.DISABLED;
}
return LeakCanary.install(this);
}
public static RefWatcher getRefWatcher(Context context) {
MyApplication leakApplication = (MyApplication) context.getApplicationContext();
return leakApplication.refWatcher;
}
}
在合适的时机调用即可。(合适的时机指的是某对象在程序中无需再使用了,应该被回收的时候)
MyApplication.getRefWatcher(context).watch(obj)
然后是对于 LeakCanary.install()
方法,也需要理一遍。
// LeakCanary.java
public static @NonNull RefWatcher install(@NonNull Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
总共涉及了四个方法:
(1)refWatcher(application)
// LeakCanary.java
public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {
return new AndroidRefWatcherBuilder(context);
}
// AndroidRefWatcherBuilder.java
public final class AndroidRefWatcherBuilder extends RefWatcherBuilder<AndroidRefWatcherBuilder> {
private static final long DEFAULT_WATCH_DELAY_MILLIS = SECONDS.toMillis(5);
private final Context context;
private boolean watchActivities = true;
private boolean watchFragments = true;
private boolean enableDisplayLeakActivity = false;
AndroidRefWatcherBuilder(@NonNull Context context) {
// 仅仅是将传入的 Context 保存起来
this.context = context.getApplicationContext();
}
...
}
refWatcher(application)
会得到一个 AndroidRefWatcherBuilder
对象,显而易见,这里利用构建者模式对 RefWatcher
进行配置。AndroidRefWatcherBuilder
继承自 RefWatcherBuilder
。
public class RefWatcherBuilder<T extends RefWatcherBuilder<T>> {
...
public RefWatcherBuilder() {
// 新建了一个 HeapDump 的构造器对象。其中 HeapDump 就是一个保存 heap dump 信息的数据结构
heapDumpBuilder = new HeapDump.Builder();
}
}
(2)AndroidRefWatcherBuilder#listenerServiceClass()
public @NonNull AndroidRefWatcherBuilder listenerServiceClass(
@NonNull Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
enableDisplayLeakActivity = DisplayLeakService.class.isAssignableFrom(listenerServiceClass);
return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
}
这里在第一篇笔记的后面有说过,目的是传入了一个 DisplayLeakService
的 Class 对象,此时会将 enableDisplayLeakActivity
置为 true(默认为 false,该属性用于控制是否能够展示用于显示泄露的界面 DisplayLeakActivity
)。
然后利用 heapDumpListener()
传递新建的 ServiceHeapDumpListener
对象并赋值给 heapDumpListener
成员变量,而 heapDumpListener
在第一篇里面有说(其作用就是分析 heapDump 对象)。
// RefWatcherBuilder.java
/** @see HeapDump.Listener */
public final T heapDumpListener(HeapDump.Listener heapDumpListener) {
this.heapDumpListener = heapDumpListener;
return self();
}
(3)AndroidRefWatcherBuilder#excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
// AndroidExcludedRefs.java
public static @NonNull ExcludedRefs.Builder createAppDefaults() {
return createBuilder(EnumSet.allOf(AndroidExcludedRefs.class));
}
public static @NonNull ExcludedRefs.Builder createBuilder(EnumSet<AndroidExcludedRefs> refs) {
ExcludedRefs.Builder excluded = ExcludedRefs.builder();
for (AndroidExcludedRefs ref : refs) {
if (ref.applies) {
ref.add(excluded);
((ExcludedRefs.BuilderWithParams) excluded).named(ref.name());
}
}
return excluded;
}
AndroidExcludedRefs
这个类,它是一个 enum
类,它声明了 Android SDK 和厂商定制的 SDK 中存在的内存泄露的 case,根据 AndroidExcludedRefs
这个类的类名就可看出这些 case 都会被 Leakcanary 的监测过滤掉。目前这个版本是有 46 种这样的 case 被包含在内,后续可能会一直增加。然后 EnumSet.allOf(AndroidExcludedRefs.class)
这个方法将会返回一个包含AndroidExcludedRefs
元素类型的 EnumSet
。Enum
是一个抽象类,在这里具体的实现类是通用正规型的 RegularEnumSet
,如果Enum
里面的元素个数大于64,则会使用存储大数据量的JumboEnumSet
。最后,在 createBuilder()
这个方法里面构建了一个排除引用的建造器 excluded
,将各式各样的 case 分门别类地保存起来再返回出去。
(4)AndroidRefWatcherBuilder#buildAndInstall()
public @NonNull RefWatcher buildAndInstall() {
if (LeakCanaryInternals.installedRefWatcher != null) {
throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
}
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
if (enableDisplayLeakActivity) {
// setEnabledAsync() 使用 AsyncTask 内部自带的 THREAD_POOL_EXECUTOR 线程池
// 进行异步阻塞式地显示 DisplayLeakActivity
LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
}
// 为 Application 注册 ActivityLifecycleCallbacks,这里就可以呼应文章开头说的那个关键之处
// 同时也呼应第一篇文章的开头
if (watchActivities) {
ActivityRefWatcher.install(context, refWatcher);
}
// 这里会尝试对标准的 fragment 进行监听
// 详细的说明见后面的内容
if (watchFragments) {
FragmentRefWatcher.Helper.install(context, refWatcher);
}
}
LeakCanaryInternals.installedRefWatcher = refWatcher;
return refWatcher;
}
// 建立 RefWatcher 实例并返回
public final RefWatcher build() {
if (isDisabled()) {
return RefWatcher.DISABLED;
}
// excludedRefs : 记录可以被忽略的泄漏路径
if (heapDumpBuilder.excludedRefs == null) {
heapDumpBuilder.excludedRefs(defaultExcludedRefs());
}
// heapDumpListener : 转储堆信息到hprof文件,并在解析完 hprof 文件后进行回调,
// 最后通知 DisplayLeakService 弹出泄漏提醒
HeapDump.Listener heapDumpListener = this.heapDumpListener;
if (heapDumpListener == null) {
heapDumpListener = defaultHeapDumpListener();
}
// debuggerControl : 判断是否处于调试模式,调试模式中不会进行内存泄漏检测。
// 因为在调试过程中可能会保留上一个引用从而导致错误信息上报
DebuggerControl debuggerControl = this.debuggerControl;
if (debuggerControl == null) {
debuggerControl = defaultDebuggerControl();
}
// heapDumper : 堆信息转储者,dump 内存泄漏处的 heap 信息到 hprof 文件
HeapDumper heapDumper = this.heapDumper;
if (heapDumper == null) {
heapDumper = defaultHeapDumper();
}
// watchExecutor : 用于在 Activity onDestroy() 之后并且主线程空闲时执行内存泄漏检测。
// 就是上一篇说过的利用 IdleHandler
WatchExecutor watchExecutor = this.watchExecutor;
if (watchExecutor == null) {
watchExecutor = defaultWatchExecutor();
}
// gcTrigger : 用于 GC
GcTrigger gcTrigger = this.gcTrigger;
if (gcTrigger == null) {
gcTrigger = defaultGcTrigger();
}
// reachabilityInspectorClasses : 用于要进行可达性检测的类列表
if (heapDumpBuilder.reachabilityInspectorClasses == null) {
heapDumpBuilder.reachabilityInspectorClasses(defaultReachabilityInspectorClasses());
}
return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener,
heapDumpBuilder);
}
// LeakCanaryInternals.setEnabledAsync()
public static void setEnabledAsync(Context context, final Class<?> componentClass,
final boolean enabled) {
final Context appContext = context.getApplicationContext();
AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
@Override public void run() {
setEnabledBlocking(appContext, componentClass, enabled);
}
});
}
这里需要详细说一下 FragmentRefWatcher.Helper.install(context, refWatcher)
的实现。
public static void install(Context context, RefWatcher refWatcher) {
List<FragmentRefWatcher> fragmentRefWatchers = new ArrayList<>();
// 如果为 Android O 则添加一个特殊的 AndroidOFragmentRefWatcher
if (SDK_INT >= O) {
fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher));
}
// 利用反射机制尝试添加 SupportFragmentRefWatcher
// 利用反射是因为如果要监测 android.support.v4.app.Fragment
// 还需要依赖额外的库 com.squareup.leakcanary:leakcanary-support-fragment
try {
Class<?> fragmentRefWatcherClass = Class.forName(SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME);
Constructor<?> constructor =
fragmentRefWatcherClass.getDeclaredConstructor(RefWatcher.class);
FragmentRefWatcher supportFragmentRefWatcher =
(FragmentRefWatcher) constructor.newInstance(refWatcher);
fragmentRefWatchers.add(supportFragmentRefWatcher);
} catch (Exception ignored) {
}
if (fragmentRefWatchers.size() == 0) {
return;
}
Helper helper = new Helper(fragmentRefWatchers);
Application application = (Application) context.getApplicationContext();
// 利用 Application 再添加一个 ActivityLifecycleCallbacks
application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks);
}
private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
// 当 Activity Created 的时候,利用该 Activity 以及前面添加的多种类型的
// FragmentRefWatcher 实现对 Fragment 的监测
for (FragmentRefWatcher watcher : fragmentRefWatchers) {
// 具体实现即通过调用 FragmentRefWatcher#watchFragments(Activity)
watcher.watchFragments(activity);
}
}
};
先看 AndroidOFragmentRefWatcher
类型的
@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, android.app.Fragment fragment) {
View view = fragment.getView();
if (view != null) {
refWatcher.watch(view);
}
}
@Override
public void onFragmentDestroyed(FragmentManager fm, android.app.Fragment fragment) {
refWatcher.watch(fragment);
}
};
@Override public void watchFragments(Activity activity) {
FragmentManager fragmentManager = activity.getFragmentManager();
fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
}
}
关键是在 AndroidOFragmentRefWatcher#watchFragments(Activity)
,会利用 activity 获取到对应的 FragmentManager
并利用其 registerFragmentLifecycleCallbacks()
方法实现对 android.app.Fragment
生命周期的监听,从而实现对 Fragment 和其 view 的内存泄漏的监测。
然后是 SupportFragmentRefWatcher
类型的。
class SupportFragmentRefWatcher implements FragmentRefWatcher {
private final RefWatcher refWatcher;
SupportFragmentRefWatcher(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) {
if (activity instanceof FragmentActivity) {
FragmentManager supportFragmentManager =
((FragmentActivity) activity).getSupportFragmentManager();
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
}
}
}
而对于 SupportFragmentRefWatcher
,其实现与 AndroidOFragmentRefWatcher
类似 ,只不过是针对 android.support.v4.app.Fragment
的。
总的来说,这里的 watcher
可能有两种,但不管是哪一种,都会使用当前传入的 activity 获取到对应的 FragmentManager/SupportFragmentManager
对象,调用它的registerFragmentLifecycleCallbacks()
方法,在对应的onDestroyView()
和onDestoryed()
方法执行完后,分别使用refWatcher.watch(view)
和refWatcher.watch(fragment)
进行内存泄漏的检测。
总结,该框架的实现主要分为如下 7 个步骤:
1、RefWatcher.watch()创建了一个KeyedWeakReference用于去观察对象。
2、然后,在后台线程中,它会检测引用是否被清除了,并且有触发GC。
3、如果引用仍然没有被清除,那么它将会把堆栈信息保存在文件系统中的.hprof文件里。
4、HeapAnalyzerService被开启在一个独立的进程中,并且HeapAnalyzer使用了HAHA开源库解析了指定时刻的堆栈快照文件heap
dump。
5、从heap dump中,HeapAnalyzer根据一个独特的引用key找到了KeyedWeakReference,并且定位了泄露的引用。
6、HeapAnalyzer为了确定是否有泄露,计算了到GC Roots的最短强引用路径,然后建立了导致泄露的链式引用。
7、这个结果被传回到app进程中的DisplayLeakService,然后一个泄露通知便展现出来了。
简单来说就是:
在一个Activity执行完onDestroy()之后,将它放入WeakReference中,然后将这个WeakReference类型的Activity对象与ReferenceQueque关联。这时再从ReferenceQueque中查看是否有该对象,如果没有,执行gc,再次查看,还是没有的话则判断发生内存泄露了。最后用HAHA这个开源库去分析dump之后的heap内存(主要就是创建一个HprofParser解析器去解析出对应的引用内存快照文件snapshot)。
摘抄自:https://github.com/JsonChao/Awesome-Android-Interview/blob/master/Android%E7%9B%B8%E5%85%B3/Android%E9%AB%98%E7%BA%A7%E9%9D%A2%E8%AF%95%E9%A2%98.md#%E8%BF%99%E4%B8%AA%E5%BA%93%E7%9A%84%E6%A0%B8%E5%BF%83%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86%E6%98%AF%E4%BB%80%E4%B9%88%E5%A6%82%E6%9E%9C%E8%AE%A9%E4%BD%A0%E5%AE%9E%E7%8E%B0%E8%BF%99%E4%B8%AA%E5%BA%93%E7%9A%84%E6%9F%90%E4%BA%9B%E6%A0%B8%E5%BF%83%E5%8A%9F%E8%83%BD%E4%BD%A0%E4%BC%9A%E8%80%83%E8%99%91%E6%80%8E%E4%B9%88%E5%8E%BB%E5%AE%9E%E7%8E%B0-3