前一篇文章(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
深入解析LeakCanary库的实现原理,包括如何监测Activity和Fragment的内存泄漏,以及核心组件RefWatcher的工作流程。
1267

被折叠的 条评论
为什么被折叠?



