在日常开发中,不可避免的会遇到内存泄漏的问题,从而导致App的内存使用紧张,严重的情况还会导致App的卡顿甚至是奔溃,所以需要开发人员解决这些内存泄漏的问题。
要解决内存泄漏的问题,首先就需要定位内存泄漏。这里可以借助Android Studio自带的内存检测工具Profile分析内存。反复进入退出同一个页面,dump一份内存快照,就可以分析出是否有内存泄漏的问题。但这样做的效率比较低,也不够全面,如果开发者忘记检测了,可能就把内存泄漏的问题给忽略掉了。这时候我们自然会想着要是有什么工具,能够自动的帮我们查找内存泄漏就好了,这样的工具自然是有的,这就是我们今天要介绍的主角:LeakCanary。只要项目中集成了这个sdk,LeakCanary就可以自动的帮我们查找内存泄漏。
一、LeakCanary的简单使用
LeakCanary是一个开源的第三方库,可以用于检测内存泄漏,并简单的分析内存泄漏的对象的引用链,帮助开发者定位内存泄漏的问题。
LeakCanary 开源库地址:https://github.com/square/leakcanary
在项目中集成LeakCanary,步骤非常的简单:
1. 在module的build.gradle文件中,依赖LeakCanary:
代码语言:javascript复制dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3'
// 可选,使用support库中的Fragment时,检测内存泄漏
debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3'
}
com.squareup.leakcanary:leakcanary-support-fragment:1.6.3这个是要支持support库下的Fragment,如果只检测android.app.*包下的Fragment,则可以不用引入这个。
2. 在自定义的Application中,安装LeakCanary:
代码语言:javascript复制public class LeakApplication extends Application {
@Override public void onCreate() {
super.onCreate();
// 判断当前进程是否是Leakcanary专门用于分析heap内存的而创建的那个进程,即HeapAnalyzerService所在的进程,如果是的话,则不进行Application中的初始化功能
if (LeakCanary.isInAnalyzerProcess(this)) {//1
return;
}
LeakCanary.install(this);
}
}
注释1:检测当前进程是否是运行:HeapAnalyzerService服务的进程,即分析内存快照的进程。
经过上面两步,LeakCanary就集成成功了,在debug环境中,LeakCanary就可以帮我们检测内存的泄漏。如果检测到某个Activity或者Fragment 有内存泄露,LeakCanary 就会以通知的形式提示我们,点开通知就可以看到对象的引用链了。
如果LeakCanary分析的引用链比较简单,还是无法定位内存泄漏的位置的话,则可以把LeakCanary保存的hprof文件导出来,通过mat工具详细的分析内存泄漏。
LeakCanary 截图
二、LeakCanary原理简单分析:
2-1、LeakCanary原理简述
经过上面的介绍,我们对LeakCanary的使用已经了解了。那么LeakCanary是如何起作用的呢,这里就对LeakCanary的原理做一个简单的阐述,为后面的源码分析做一个理论支持。
LeakCannary 的主要原理,其实很简单,大概可以分为以下几步:
- (1) 监测Activity 的生命周期的 onDestroy() 的调用。
- (2) 当某个 Activity 的 onDestroy() 调用后,便对这个 activity 创建一个带 ReferenceQueue 的弱引用,并且给这个弱引用创建了一个 key 保存在 Set集合 中。
- (3) 如果这个 activity 可以被回收,那么弱引用就会被添加到 ReferenceQueue 中。
- (4) 等待主线程进入 idle(即空闲)后,通过一次遍历,在 ReferenceQueue 中的弱引用所对应的 key 将从 retainedKeys 中移除,说明其没有内存泄漏。
- (5) 如果 activity 没有被回收,先强制进行一次 gc,再来检查,如果 key 还存在 retainedKeys 中,说明 activity 不可回收,同时也说明了出现了内存泄漏。
- (6) 发生内存泄露之后,dump内存快照,分析 hprof 文件,找到泄露路径(使用 haha 库分析),发送到通知栏
2-2、ActivityLifecycleCallbacks使用
在上面LeakCanary的原理简述中,我们知道,第一步就是要监听Activity的生命周期。但我们在安装LeakCanary的时候,就只调用了LeakCanary的install方法,其它啥都没有操作,LeakCanary是如何监听Activity的生命周期的呢?
所以在分析LeakCanary的源码之前,我们先了解一个基础知识:ActivityLifecycleCallbacks。借助ActivityLifecycleCallbacks,就可以全局监听Activity的生命周期。
2-2-1、ActivityLifecycleCallbacks的使用:
ActivityLifecycleCallbacks 是Application中声明的一个内部接口,定义如下:
代码语言:javascript复制public interface ActivityLifecycleCallbacks {
void onActivityCreated(Activity activity, Bundle savedInstanceState);
void onActivityStarted(Activity activity);
void onActivityResumed(Activity activity);
void onActivityPaused(Activity activity);
void onActivityStopped(Activity activity);
void onActivitySaveInstanceState(Activity activity, Bundle outState);
void onActivityDestroyed(Activity activity);
}
在Application中通过registerActivityLifecycleCallbacks()方法,注册ActivityLifecycleCallbacks回调。这样就可以全局监听Activity的生命周期了。如下:
代码语言:javascript复制public class MyApplication extends Application {
@Override
public void onCreate() {
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityStopped(Activity activity) {
Log.v("MyApplication", "onActivityStopped");
}
@Override
public void onActivityStarted(Activity activity) {
Log.v("MyApplication", "onActivityStarted");
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
Log.v("MyApplication", "onActivitySaveInstanceState");
}
@Override
public void onActivityResumed(Activity activity) {
Log.v("MyApplication", "onActivityResumed");
}
@Override
public void onActivityPaused(Activity activity) {
Log.v("MyApplication", "onActivityPaused");
}
@Override
public void onActivityDestroyed(Activity activity) {
Log.v("MyApplication", "onActivityDestroyed");
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
Log.v("MyApplication", "onActivityCreated");
}
});
}
}
打印日志如下:
代码语言:javascript复制2020-08-25 20:36:44.553 28323-28323/? V/MyApplication: onActivityPaused
2020-08-25 20:36:44.589 28323-28323/? V/MyApplication: onActivityCreated
2020-08-25 20:36:44.645 28323-28323/? V/MyApplication: onActivityStarted
2020-08-25 20:36:44.649 28323-28323/? V/MyApplication: onActivityResumed
2020-08-25 20:36:45.076 28323-28323/? V/MyApplication: onActivityStopped
2020-08-25 20:36:45.079 28323-28323/? V/MyApplication: onActivitySaveInstanceState
2020-08-25 20:36:47.485 28323-28323/? V/MyApplication: onActivityPaused
2020-08-25 20:36:47.503 28323-28323/? V/MyApplication: onActivityStarted
2020-08-25 20:36:47.506 28323-28323/? V/MyApplication: onActivityResumed
2020-08-25 20:36:47.895 28323-28323/? V/MyApplication: onActivityStopped
2020-08-25 20:36:47.898 28323-28323/? V/MyApplication: onActivityDestroyed
2-2-2、ActivityLifecycleCallbacks源码分析:
下面就看看源码,看看注册的这个回调是在什么时机调用的。Application的registerActivityLifecycleCallbacks()方法:
代码语言:javascript复制public void registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) {
synchronized (mActivityLifecycleCallbacks) {
mActivityLifecycleCallbacks.add(callback);
}
}
这里把ActivityLifecycleCallbacks回调放到了一个集合当中。
既然是分析Activity的生命周期,那么就进入Activity的源码看看生命周期方法,有没有回调我们注册的监听。Activity的onCreate源码如下:
代码语言:javascript复制protected void onCreate(@Nullable Bundle savedInstanceState) {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " this ": " savedInstanceState);
..............................................
mFragments.dispatchCreate();
dispatchActivityCreated(savedInstanceState);
if (mVoiceInteractor != null) {
mVoiceInteractor.attachActivity(this);
}
mRestoredFromBundle = savedInstanceState != null;
mCalled = true;
}
Activity的onCreate方法,又执行了dispatchActivityCreated方法:
代码语言:javascript复制private void dispatchActivityCreated(@Nullable Bundle savedInstanceState) {
// 获取Application对象,执行dispatchActivityCreated方法
getApplication().dispatchActivityCreated(this, savedInstanceState);
Object[] callbacks = collectActivityLifecycleCallbacks();
if (callbacks != null) {
for (int i = 0; i < callbacks.length; i ) {
((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityCreated(this,
savedInstanceState);
}
}
}
在这个方法中可以看出,在dispatchActivityCreated方法中,Activity调用了Application的dispatchActivityCreated方法:
代码语言:javascript复制void dispatchActivityCreated(@NonNull Activity activity,
@Nullable Bundle savedInstanceState) {
Object[] callbacks = collectActivityLifecycleCallbacks();// 获取所有注册的ActivityLifecycleCallbacks
if (callbacks != null) {
for (int i=0; i<callbacks.length; i ) {
((ActivityLifecycleCallbacks)callbacks[i]).onActivityCreated(activity,
savedInstanceState);// 回调onActivityCreated方法
}
}
}
private Object[] collectActivityLifecycleCallbacks() {
Object[] callbacks = null;
synchronized (mActivityLifecycleCallbacks) {
if (mActivityLifecycleCallbacks.size() > 0) {
callbacks = mActivityLifecycleCallbacks.toArray();
}
}
return callbacks;
}
从这里看出,最终就会回调到注册的ActivityLifecycleCallbacks的onActivityCreated方法。其它生命周期的方法回调与onCreate方法类似,这里就不一一分析了。
了解了这些基本知识之后,下面就从LeakCanary的install()方法为入口,看看LeakCanary具体是怎么检测到内存泄漏并弹出通知的。
三、LeakCanary源码分析:
对LeakCanary的源码分析,我们就从它的初始化入口开始分析,即在Application的调用的LeakCanary的install方法:
代码语言:javascript复制LeakCanary.install(this);
3-1、LeakCanary#install()
代码语言:javascript复制public static @NonNull RefWatcher install(@NonNull Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
在install()方法中的处理,可以分解为如下四步:
- 1、refWatcher(application),创建:AndroidRefWatcherBuilder对象。
- 2、链式调用listenerServiceClass(DisplayLeakService.class)
- 3、链式调用excludedRefs(AndroidExcludedRefs.createAppDefaults().build()),创建ExcludedRefs对象,排除的内存泄漏。
- 4、链式调用uildAndInstall()
3-1-1、第一步,调用LeakCanary的refWatcher方法:
代码语言:javascript复制public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {
return new AndroidRefWatcherBuilder(context);
}
// AndroidRefWatcherBuilder 类
public final class AndroidRefWatcherBuilder extends RefWatcherBuilder<AndroidRefWatcherBuilder> {
private final Context context;
AndroidRefWatcherBuilder(@NonNull Context context) {
this.context = context.getApplicationContext();
}
}
// RefWatcherBuilder
public class RefWatcherBuilder<T extends RefWatcherBuilder<T>> {
private final HeapDump.Builder heapDumpBuilder;
public RefWatcherBuilder() {
// 新建了一个HeapDump的构造器对象,其中HeapDump就是一个保存heap dump信息的数据结构。
heapDumpBuilder = new HeapDump.Builder();
}
}
这个方法很简单,就是创建了一个AndroidRefWatcherBuilder对象,并保存Application类型的Context。
AndroidRefWatcherBuilder是一个适配Android平台的引用观察者构造器对象,它继承了RefWatcherBuilder,RefWatcherBuilder是一个负责建立引用观察者RefWatcher实例的基类构造器,在其默认的构造方法中,新建了一个HeapDump的构造器对象,其中HeapDump就是一个保存heap dump信息的数据结构。
在new AndroidRefWatcherBuilder对象的时候,做了两件事:
- (1)、保存Application类型的Context
- (2)、创建HeapDump.Builder对象。
3-1-2、AndroidRefWatcherBuilder的listenerServiceClass方法:
代码语言:javascript复制public @NonNull AndroidRefWatcherBuilder listenerServiceClass(
@NonNull Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
enableDisplayLeakActivity = DisplayLeakService.class.isAssignableFrom(listenerServiceClass);
return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
}
/** @see HeapDump.Listener */
public final T heapDumpListener(HeapDump.Listener heapDumpListener) {
this.heapDumpListener = heapDumpListener;
return self();
}
// ServiceHeapDumpListener 类
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();
}
}
从上面代码可以看出,listenerServiceClass方法主要是保存了一个ServiceHeapDumpListener对象,ServiceHeapDumpListener里面存储着DisplayLeakService的Class对象和application对象。
DisplayLeakService:显示通知。
3-1-3、链式调用excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
代码语言:javascript复制// AndroidRefWatcherBuilder的excludedRefs方法
public final T excludedRefs(ExcludedRefs excludedRefs) {
heapDumpBuilder.excludedRefs(excludedRefs); // 保存ExcludedRefs对象到HeapDump.Builder对象中
return self();
}
下面看看AndroidExcludedRefs.createAppDefaults()方法:
代码语言:javascript复制 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中存在的内存泄露的案例,这些内存泄漏的情况都会被Leakcanary的监测过滤掉。
目前这个版本是有46种这样的case被包含在内,后续可能会一直增加。然后EnumSet.allOf(AndroidExcludedRefs.class)这个方法将会返回一个包含AndroidExcludedRefs元素类型的EnumSet。Enum是一个抽象类,在这里具体的实现类是通用正规型的RegularEnumSet,如果Enum里面的元素个数大于64,则会使用存储大数据量的JumboEnumSet。最后,在createBuilder这个方法里面构建了一个排除引用的建造器excluded,将各式各样的case分门别类地保存起来再返回出去。
3-1-4、链式调用AndroidRefWatcherBuilder#uildAndInstall()
代码语言:javascript复制private boolean watchActivities = true;
private boolean watchFragments = true;
public @NonNull RefWatcher buildAndInstall() {
// 1.判断LeakCanaryInternals.installedRefWatcher是否已经被赋值,如果被赋值了,则会抛出异常,警告buildAndInstall()这个方法应该仅仅只调用一次
if (LeakCanaryInternals.installedRefWatcher != null) {
throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
}
RefWatcher refWatcher = build();// 2. 构建引用观察者RefWatcher对象
if (refWatcher != DISABLED) {
if (enableDisplayLeakActivity) {
LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
}
if (watchActivities) {// 3. watchActivities默认为true
ActivityRefWatcher.install(context, refWatcher);// 监听Activity
}
if (watchFragments) {// 4. 观察Fragment
FragmentRefWatcher.Helper.install(context, refWatcher);
}
}
// 5.为LeakCanaryInternals.installedRefWatcher赋值
LeakCanaryInternals.installedRefWatcher = refWatcher;
return refWatcher;
从上面代码可以看出,buildAndInstall方法主要做了如下几件事:
- (1)、检测LeakCanaryInternals.installedRefWatcher是否已经被赋值,如果被赋值了,则会抛出异常,警告buildAndInstall()这个方法应该仅仅只调用一次
- (2)、根据前面三步提供的配置对象,构建一个RefWatcher对象。
- (3)、观察Activity是否泄漏
- (4)、观察Fragment是否泄漏
- (5)、为LeakCanaryInternals.installedRefWatcher赋值
(1)、构建一个RefWatcher对象。
这里再去看看注释2处的build()方法具体做了什么操作:
代码语言:javascript复制public final RefWatcher build() {
// 1. 是否监听泄漏
if (isDisabled()) {
return RefWatcher.DISABLED;
}
// 2. 可以被忽略的内存泄漏
if (heapDumpBuilder.excludedRefs == null) {
heapDumpBuilder.excludedRefs(defaultExcludedRefs());
}
// 3. 转储堆信息到hprof文件,并在解析完 hprof 文件后进行回调,最后通知 DisplayLeakService 弹出泄漏提醒。
HeapDump.Listener heapDumpListener = this.heapDumpListener;
if (heapDumpListener == null) {
heapDumpListener = defaultHeapDumpListener();
}
// 4. 判断是否处于调试模式,调试模式中不会进行内存泄漏检测。为什么呢?因为在调试过程中可能会保留上一个引用从而导致错误信息上报。
DebuggerControl debuggerControl = this.debuggerControl;
if (debuggerControl == null) {
debuggerControl = defaultDebuggerControl();
}
// 5. 堆信息转存者,dump 内存泄漏处的 heap 信息到 hprof 文件
HeapDumper heapDumper = this.heapDumper;
if (heapDumper == null) {
heapDumper = defaultHeapDumper();
}
// 6. 线程控制器,在 onDestroy() 之后并且主线程空闲时执行内存泄漏检测
WatchExecutor watchExecutor = this.watchExecutor;
if (watchExecutor == null) {
watchExecutor = defaultWatchExecutor();
}
// 7. 用于 GC,watchExecutor 首次检测到可能的内存泄漏,会主动进行 GC,GC 之后会再检测一次,仍然泄漏的判定为内存泄漏,最后根据heapDump信息生成相应的泄漏引用链。
GcTrigger gcTrigger = this.gcTrigger;
if (gcTrigger == null) {
gcTrigger = defaultGcTrigger();
}
// 8. 用于要进行可达性检测的类列表
if (heapDumpBuilder.reachabilityInspectorClasses == null) {
heapDumpBuilder.reachabilityInspectorClasses(defaultReachabilityInspectorClasses());
}
// 9.创建RefWatcher对象
return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener,
heapDumpBuilder);
}
从上面代码可以看出,build方法主要是根据配置的信息,构建一个RefWatcher对象。主要信息有:
- (1)、excludedRefs:记录可以被忽略的泄漏路径,这个在链式调用第三步的excludedRefs(AndroidExcludedRefs.createAppDefaults().build())方法中提供了。
- (2)、heapDumpListener:保存内存信息到hprof文件后的回调接口,这个在链式调用的listenerServiceClass方法中提供了。
- (3)、heapDumper:dump 内存信息并保存到 hprof 文件,在链式调用的第一步创建AndroidRefWatcherBuilder对象的时候,在RefWatcherBuilder的默认构造函数中有创建。
- (4)、watchExecutor:创建一个默认的线程池,AndroidWatchExecutor对象。
- (5)、gcTrigger:执行GC的对象
最后,根据这些对象,创建一个新的RefWatcher并将其返回。
(2)观察Activity:
这里主要是根据前面三步提供的配置对象,构建一个引用观察者:RefWatcher对象。通过这个对象,去观察对象是否泄漏。接下来看看ActivityRefWatcher#install方法的操作:
代码语言:javascript复制public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) {
Application application = (Application) context.getApplicationContext();
// 1.创建ActivityRefWatcher对象
ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
// 2. 为Application注册Activity生命周期监听回调
application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
}
// 3. 创建一个Application.ActivityLifecycleCallbacks对象
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityDestroyed(Activity activity) {
// 4.把Activity对象交给RefWatcher进行监控
refWatcher.watch(activity);// refWatcher
}
};
从上面代码可以看出,ActivityRefWatcher的静态方法:**install()**主要做了两件事:
- 创建一个ActivityRefWatcher对象
- 为Application对象注册一个Activity生命周期监听的回调。
在这个Activity生命周期的回调中,监听了Activity的onDestory方法,并在onActivityDestroyed方法中,把Activity对象交给ActivityRefWatcher对象监管。
(3)、观察Fragment:
在AndroidRefWatcherBuilder#uildAndInstall()方法中,除了会监测Activity之外,默认也会监测Fragment的内存泄漏。
代码语言:javascript复制// watchFragments默认为true
if (watchFragments) {// 4. 观察Fragment
FragmentRefWatcher.Helper.install(context, refWatcher);
}
接下来看看Helper的install方法:
代码语言:javascript复制private static final String SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME =
"com.squareup.leakcanary.internal.SupportFragmentRefWatcher";
public static void install(Context context, RefWatcher refWatcher) {
List<FragmentRefWatcher> fragmentRefWatchers = new ArrayList<>();
// 1. Android 8.0以上,创建一个android.app包下的Fragment观察者
if (SDK_INT >= O) {// android 8.0
fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher));
}
try {
// 2.反射创建监测support包下的Fragment观察者
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);
// 3. 注册Activity生命周期回调
Application application = (Application) context.getApplicationContext();
application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks);
}
// ActivityLifecycleCallbacks回调对象
private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
for (FragmentRefWatcher watcher : fragmentRefWatchers) {
watcher.watchFragments(activity);
}
}
};
从上面代码可以看出,一共做了三件事:
- Android 8.0及以上,创建android.app*包下的Fragment观察者:AndroidOFragmentRefWatcher
- 通过反射,创建support包下的Fragment观察者:"com.squareup.leakcanary.internal.SupportFragmentRefWatcher"
- 监听Activity生命周期,进而监听Fragment的生命周期。
所以对于:android.app.* 包下的Fragment,在android8.0以下,是没有对Fragment进行监测的,对于:support包下的Fragment,在没有引入:
代码语言:javascript复制 // 可选,使用support库中的Fragment时,检测内存泄漏
debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3'
库的情况下,也是没有对support包下的Fragment进行监测的。因为如果没有引用这个库的话,通过反射是找不到类:"com.squareup.leakcanary.internal.SupportFragmentRefWatcher",这样就没办法创建监测support包下的Fragment的观察者。
下面通过**AndroidOFragmentRefWatcher#watchFragments()**方法分析一下Fragment是如何被监测的:
代码语言:javascript复制private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
new FragmentManager.FragmentLifecycleCallbacks() {
@Override public void onFragmentViewDestroyed(FragmentManager fm, Fragment fragment) {
View view = fragment.getView();
if (view != null) {// 3. 监测Fragment的view是否内存泄漏
refWatcher.watch(view);
}
}
@Override
public void onFragmentDestroyed(FragmentManager fm, Fragment fragment) {
refWatcher.watch(fragment);// 4. 检测Fragment是否内存泄漏
}
};
@Override public void watchFragments(Activity activity) {
// 1. 获取到FragmentManager对象
FragmentManager fragmentManager = activity.getFragmentManager();
// 2. 为FragmentManager注册Fragment生命周期回调
fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
}
从上面代码可以看出,Fragment的生命周期监听跟Activity类似,只不过这里是通过为FragmentManager注册Fragment生命周期回调,从而全局监听Activity中所有的Fragment的生命周期。
在FragmentManager.FragmentLifecycleCallbacks的回调中,跟Activity类似,通过ActivityRefWatcher去检测Fragment和Fragment的View是否内存泄漏。
3-2、ActivityRefWatcher#watch()方法
ActivityRefWatcher类是继承与RefWatcher类的,在ActivityRefWatcher类中,没有实现watch()方法,在其父类RefWatcher中有具体的实现,RefWatcher类中的watch()方法:
代码语言:javascript复制public void watch(Object watchedReference) {
watch(watchedReference, "");
}
/**
* Watches the provided references and checks if it can be GCed. This method is non blocking,
* the check is done on the {@link WatchExecutor} this {@link RefWatcher} has been constructed
* with.
*
* @param referenceName An logical identifier for the watched object.
*/
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();// 1. 生成一个唯一的Key
retainedKeys.add(key);// 2.把生成的key保存在Set集合中
// 3. 用一个弱引用:KeyedWeakReference包裹监听对象(如:Activity),弱引用关联一个引用队列:queue
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
// 4. 创建一个字任务,分析是否有内存泄漏
ensureGoneAsync(watchStartNanoTime, reference);
}
RefWatcher的watch方法逻辑很简单,主要做了4件事:
- (1)、为监听对象生成一个唯一的key值
- (2)、保存key值到一个CopyOnWriteArraySet集合中
- (3)、创建一个KeyedWeakReference类型的弱引用,包裹Activity对象,并与一个引用队列进行关联
- (4)、创建一个字任务,分析是否有内存泄漏
这里我们在看一下自定义的弱引用:KeyedWeakReference类:
代码语言:javascript复制// KeyedWeakReference类
final class KeyedWeakReference extends WeakReference<Object> {
public final String key;// 保存对象的key值
public final String name;// 保存名称
KeyedWeakReference(Object referent, String key, String name,
ReferenceQueue<Object> referenceQueue) {
// 1
super(checkNotNull(referent, "referent"), checkNotNull(referenceQueue, "referenceQueue"));
this.key = checkNotNull(key, "key");
this.name = checkNotNull(name, "name");
}
}
可以看到,在KeyedWeakReference内部,使用了key和name标识了一个被检测的WeakReference对象。在注释1处,将弱引用和引用队列 ReferenceQueue 关联起来,如果弱引用referent持有的对象被GC回收,JVM就会把这个弱引用加入到与之关联的引用队列referenceQueue中。即 KeyedWeakReference 持有的 Activity 对象如果被GC回收,该弱引用对象就会加入到引用队列 referenceQueue 中。
3-3、RefWatcher#的ensureGoneAsync方法
代码语言:javascript复制private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}
在这个方法中,调用了watchExecutor对象的execute方法,执行了一个异步任务。watchExecutor对象是AndroidWatchExecutor类型。
3-4、AndroidWatchExecutor的execute方法:
代码语言:javascript复制public final class AndroidWatchExecutor implements WatchExecutor {
static final String LEAK_CANARY_THREAD_NAME = "LeakCanary-Heap-Dump";
private final Handler mainHandler;
private final Handler backgroundHandler;
private final long initialDelayMillis;
private final long maxBackoffFactor;
public AndroidWatchExecutor(long initialDelayMillis) {
// 1. 创建一个主现场的Handler对象
mainHandler = new Handler(Looper.getMainLooper());
// 2. 创建一个字线程
HandlerThread handlerThread = new HandlerThread(LEAK_CANARY_THREAD_NAME);
handlerThread.start();
// 3.基于字线程handlerThread创建一个关联的Handler对象backgroundHandler
backgroundHandler = new Handler(handlerThread.getLooper());
this.initialDelayMillis = initialDelayMillis;
maxBackoffFactor = Long.MAX_VALUE / initialDelayMillis;
}
@Override public void execute(@NonNull Retryable retryable) {
// 1. 如果是主现场,则调用waitForIdle方法
if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
waitForIdle(retryable, 0);
} else {
// 2. 通过mainHandler 执行一个异步任务
postWaitForIdle(retryable, 0);
}
}
}
private void postWaitForIdle(final Retryable retryable, final int failedAttempts) {
mainHandler.post(new Runnable() {
@Override public void run() {
waitForIdle(retryable, failedAttempts);
}
});
}
在execute()方法中,会进行主线程的判断,如果是主线程,则直接执行waitForIdle()方法,如果不是,则会通过handler发送一个消息,最终还是会调用waitForIdle()方法。
3-5、AndroidWatchExecutor的waitForIdle()方法:
代码语言:javascript复制private void waitForIdle(final Retryable retryable, final int failedAttempts) {
// This needs to be called from the main thread.
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override public boolean queueIdle() {
postToBackgroundWithDelay(retryable, failedAttempts);
return false;
}
});
}
这里的逻辑非常简单,就是往主线程的MessageQueue中,增加一个主线程任务都执行完成的回调。则在主线程任务都执行完成之后,即主线程空闲的时候,就会执行MessageQueue.IdleHandler的**queueIdle()**方法,从上代码可以看出,**queueIdle()方法执行了postToBackgroundWithDelay()**方法:
代码语言:javascript复制private void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) {
long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor);
long delayMillis = initialDelayMillis * exponentialBackoffFactor;
// 在主线程空闲的时候,开始分析内存,backgroundHandler是一个与字线程关联的Handler,所以通过backgroundHandler发出的任务,都运行在字线程中
backgroundHandler.postDelayed(new Runnable() {
@Override public void run() {
Retryable.Result result = retryable.run();// 字线程运行
if (result == RETRY) {
postWaitForIdle(retryable, failedAttempts 1);
}
}
}, delayMillis);// 延时处理,默认是5s钟
}
在这个方法中,通过backgroundHandler发送一个延时任务到子线程中,默认延时5s,最终执行Retryable的run方法。这就回到了:3-3、RefWatcher#的ensureGoneAsync方法的Retryable的run()方法。这里再次贴一下代码:
代码语言:javascript复制private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}
AndroidWatchExecutor的waitForIdle()方法,是在等主线程空闲的时候,开始分析内存泄漏,分析内存泄漏的逻辑又通过一个字线程的handler转到了字线程中,因此,内存泄漏的分析逻辑在字线程中运行。
在执行Retryable的**run()**方法的时候,会执行RefWatcher#ensureGone()方法。
3-6、RefWatcher#ensureGone()方法
代码语言:javascript复制Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime();
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
// 1. 先移除被回收的弱引用对象
removeWeaklyReachableReferences();
if (debuggerControl.isDebuggerAttached()) {
// The debugger can create false leaks.
return RETRY;
}
// 2. 如果没有内存泄漏
if (gone(reference)) {
return DONE;
}
// 3. 执行GC操作
gcTrigger.runGc();
// 4. 再次移除被回收的弱引用对象
removeWeaklyReachableReferences();
// 5. 如果有内存泄漏
if (!gone(reference)) {
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
// 6. 通过heapDumper对象,保存hprof文件
File heapDumpFile = heapDumper.dumpHeap();
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.
return RETRY;
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
// 7. 构建一个代表hprof文件的HeapDump对象
HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
.referenceName(reference.name)
.watchDurationMs(watchDurationMs)
.gcDurationMs(gcDurationMs)
.heapDumpDurationMs(heapDumpDurationMs)
.build();
// 8. 分析这个hprof文件
heapdumpListener.analyze(heapDump);
}
return DONE;
}
通过上面的代码注解,我们可以知道,ensureGone()方法分为8个步骤来处理业务:
- 先移除被回收的弱引用对象
- 判断是否有内存泄漏,没有内存泄漏就直接退出
- 如果有内存泄漏,则先执行GC操作
- 再次移除被回收的弱引用对象
- 再次判断是否有内存泄漏
- 如果有内存泄漏,则保存内存快照到hprof文件中
- 构建一个代表hprof文件的HeapDump对象
- 分析这个hprof文件
首先,我们来看看removeWeaklyReachableReferences方法的处理逻辑。
3-6-1、RefWatcher#removeWeaklyReachableReferences()方法
代码语言:javascript复制private void removeWeaklyReachableReferences() {
// WeakReferences are enqueued as soon as the object to which they point to becomes weakly
// reachable. This is before finalization or garbage collection has actually happened.
KeyedWeakReference ref;
while ((ref = (KeyedWeakReference) queue.poll()) != null) { // queue是与弱引用关联的引用队列
retainedKeys.remove(ref.key); // 在Set集合中移除代表对象的key值
}
}
在**# 3-2、ActivityRefWatcher#watch()方法**中,创建弱引用对象的时候,把弱引用与一个引用队列进行了关联,则被弱引用包裹的对象被回收的时候,这个弱引用对象就会被放入到这个引用队列中来。
在这里,我们遍历这个引用队列,遍历出来的弱引用对象,其引用的对象都是被回收了的对象,所以这里把代表被回收的对象的唯一key值从Set集合中移除。
3-6-2、RefWatcher#gone()方法
所以我们要判断一个对象是否内存泄漏了,就可以拿代表这个对象的唯一key值到Set集合中去找,看是否存在,如果存在,说明这个key的对象还没有被回收。下面,我们就看看gone()方法中的逻辑是否如我们猜想的这样:
代码语言:javascript复制/**
* 判断对象是否被回收
* @return true:对象已经被回收了,false:对象还没有被回收
*/
private boolean gone(KeyedWeakReference reference) {
return !retainedKeys.contains(reference.key); // 这里就是判断代表对象的唯一key值是否在Set集合中存在
}
若果key值在Set集合中存在,则gone方法返回false,则表示对象还没有被回收;如果key值不在集合中,则gone方法返回true,表示对象已经被回收了。
执行GC操作,再次从Set集合中移除被回收的对象的key值,再次判断对象是否被回收,如果对象还是没有被回收,则保存内存快照,生产hprof文件。
3-6-3、AndroidHeapDumper# dumpHeap()方法
代码语言:javascript复制public File dumpHeap() {
// 1. 创建一个用于保存hprof文件的file对象
// hprof文件保存的路径为sd卡下的:Download/leakcanary-包名
File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();
if (heapDumpFile == RETRY_LATER) {// 2.如果这个文件等于RETRY_LATER则表示生成失败,直接返回RETRY进行延时重试检测的操作
return RETRY_LATER;
}
FutureResult<Toast> waitingForToast = new FutureResult<>();
showToast(waitingForToast);
if (!waitingForToast.wait(5, SECONDS)) {
CanaryLog.d("Did not dump heap, too much time waiting for Toast.");
return RETRY_LATER;
}
Notification.Builder builder = new Notification.Builder(context)
.setContentTitle(context.getString(R.string.leak_canary_notification_dumping));
Notification notification = LeakCanaryInternals.buildNotification(context, builder);
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
int notificationId = (int) SystemClock.uptimeMillis();
notificationManager.notify(notificationId, notification);
Toast toast = waitingForToast.get();
try {
// 2. 保存hprof文件
Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
cancelToast(toast);
notificationManager.cancel(notificationId);
return heapDumpFile;
} catch (Exception e) {
CanaryLog.d(e, "Could not dump heap");
// Abort heap dump
return RETRY_LATER;
}
}
这个方法里面的代码有很多,我们只需要重点关注注释处的代码即可:
- (1)、创建一个用于保存hprof文件的file对象,保存路径为sd卡下的:“Download/leakcanary-包名” hprof文件的保存路径: 如果app有存储权限,则为:“Download/leakcanary-包名” 如果没有存储权限,则为:"data/data/包名/files/leakcanary"
- (2)、如果这个文件等于RETRY_LATER则表示生成失败,直接返回RETRY进行延时重试检测的操作
- (3)、调用系统api:Debug.dumpHprofData(),保存hprof文件到指定的目录
如果hprof文件生成成功,就会调用heapDumpBuilder建造者创建一个代表hprof文件的HeapDump对象。通过前面的分析我们知道,heapDumpBuilder对象是HeapDump.Builder类型,在RefWatcherBuilder的默认构造函数中创建。
3-6-4、通过建造者:HeapDump.Builder构建一个代表hprof文件的HeapDump对象
代码语言:javascript复制Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
// 。。。。。。。。。。。。。。。。。。。。。。。。
HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
.referenceName(reference.name)
.watchDurationMs(watchDurationMs)
.gcDurationMs(gcDurationMs)
.heapDumpDurationMs(heapDumpDurationMs)
.build();
heapdumpListener.analyze(heapDump);
}
return DONE;
}
// HeapDump.Builder的build方法
public HeapDump build() {
checkNotNull(excludedRefs, "excludedRefs");
checkNotNull(heapDumpFile, "heapDumpFile");
checkNotNull(referenceKey, "referenceKey");
checkNotNull(reachabilityInspectorClasses, "reachabilityInspectorClasses");
return new HeapDump(this); // 创建一个HeapDump对象
}
// HeapDump的构造函数
HeapDump(Builder builder) {
this.heapDumpFile = builder.heapDumpFile;
this.referenceKey = builder.referenceKey;
this.referenceName = builder.referenceName;
this.excludedRefs = builder.excludedRefs;
this.computeRetainedHeapSize = builder.computeRetainedHeapSize;
this.watchDurationMs = builder.watchDurationMs;
this.gcDurationMs = builder.gcDurationMs;
this.heapDumpDurationMs = builder.heapDumpDurationMs;
this.reachabilityInspectorClasses = builder.reachabilityInspectorClasses;
}
第六步执行完后,就生成了一个HeapDump对象。
最后会执行heapdumpListener的analyze()对新创建的HeapDump对象进行泄漏分析。由前面对AndroidRefWatcherBuilder的listenerServiceClass()的分析可知,heapdumpListener的实现就是ServiceHeapDumpListener,接着看到ServiceHeapDumpListener的**analyze()**方法。
3-6-4、ServiceHeapDumpListener#analyze()方法:
代码语言:javascript复制@Override public void analyze(@NonNull HeapDump heapDump) {
checkNotNull(heapDump, "heapDump");
HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
这里的逻辑很简单,就是把处理委托给了HeapAnalyzerService的runAnalysis方法去执行:
代码语言:javascript复制public static void runAnalysis(Context context, HeapDump heapDump,
Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
setEnabledBlocking(context, HeapAnalyzerService.class, true);
setEnabledBlocking(context, listenerServiceClass, true);
Intent intent = new Intent(context, HeapAnalyzerService.class);//
intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());// 携带DisplayLeakService的名字
intent.putExtra(HEAPDUMP_EXTRA, heapDump);
ContextCompat.startForegroundService(context, intent); // 启动服务:HeapAnalyzerService
}
HeapAnalyzerService的runAnalysis方法的逻辑也很简单,就是启动一个:HeapAnalyzerService服务。
3-7、HeapAnalyzerService服务启动
HeapAnalyzerService继承与ForegroundService服务,而ForegroundService服务又继承与IntentService,IntentService在启动的时候,会执行:onHandleIntent()方法。
ForegroundService中重写了onHandleIntent()方法:
代码语言:javascript复制@Override protected void onHandleIntent(@Nullable Intent intent) {
onHandleIntentInForeground(intent);
}
protected abstract void onHandleIntentInForeground(@Nullable Intent intent);
接下来看HeapAnalyzerService的onHandleIntentInForeground()方法:
代码语言:javascript复制protected void onHandleIntentInForeground(@Nullable Intent intent) {
if (intent == null) {
CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
return;
}
// listenerClassName就是DisplayLeakService的名字
String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
// 1. 首先会新建一个HeapAnalyzer对象,它会根据RefWatcher生成的heap dumps信息来分析被怀疑的泄漏是否是真的泄漏
HeapAnalyzer heapAnalyzer =
new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses);
// 2. 然后会调用它的checkForLeak()方法去使用haha库解析 hprof文件
AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,
heapDump.computeRetainedHeapSize);
// 3. 把分析结果显示出来
AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
}
从上面代码可以看出,注释1处,首先会新建一个HeapAnalyzer对象,顾名思义,它就是根据RefWatcher生成的heap dumps信息来分析被怀疑的泄漏是否是真的。在注释2处,然后会调用它的checkForLeak()方法去使用haha库解析 hprof文件。
3-8、HeapAnalyzer#checkForLeak()方法:
代码语言:javascript复制public @NonNull AnalysisResult checkForLeak(@NonNull File heapDumpFile,
@NonNull String referenceKey,
boolean computeRetainedSize) {
long analysisStartNanoTime = System.nanoTime();
if (!heapDumpFile.exists()) { // 判断hprof文件是否存在
Exception exception = new IllegalArgumentException("File does not exist: " heapDumpFile);
return failure(exception, since(analysisStartNanoTime));
}
try {
listener.onProgressUpdate(READING_HEAP_DUMP_FILE);
// 1. 新建一个内存映射缓存文件buffer
HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
// 2. 创建一个hprof文件解析器
HprofParser parser = new HprofParser(buffer);
listener.onProgressUpdate(PARSING_HEAP_DUMP);
Snapshot snapshot = parser.parse();
listener.onProgressUpdate(DEDUPLICATING_GC_ROOTS);
// 3. 删除重复的GC root对象
deduplicateGcRoots(snapshot);
listener.onProgressUpdate(FINDING_LEAKING_REF);
// 4. 查找泄漏的引用
Instance leakingRef = findLeakingReference(referenceKey, snapshot);
// False alarm, weak reference was cleared in between key check and heap dump.
// 5. 如果没有找到泄漏的引用
if (leakingRef == null) {
String className = leakingRef.getClassObj().getClassName();
return noLeak(className, since(analysisStartNanoTime)); // 返回一个没有泄漏的分析结果AnalysisResult对象
}
// 6. 返回一个有泄漏分析结果的AnalysisResult对象
return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, computeRetainedSize);
} catch (Throwable e) {
return failure(e, since(analysisStartNanoTime));
}
}
这个方法执行的逻辑有点复杂,主要分为如下6个步骤:
- 新建一个内存映射缓存文件buffer对象
- 根据第一步生成的buffer对象,创建一个hprof文件解析器。
- 删除重复的GC Root对象
- 查找泄漏的引用
- 如果没有找到泄漏的引用对象,则返回一个没有泄漏的AnalysisResult对象
- 如果有泄漏,则返回一个有泄漏的AnalysisResult对象。
最后,我们来分析下HeapAnalyzerService中onHandleIntentInForeground方法注释3处的AbstractAnalysisResultService.sendResultToListener()方法,很明显,这里AbstractAnalysisResultService的实现类就是我们刚开始分析的用于展示泄漏路径信息得DisplayLeakService对象。在里面直接创建一个由PendingIntent构建的泄漏通知用于供用户点击去展示详细的泄漏界面DisplayLeakActivity。
3-9、AbstractAnalysisResultService.sendResultToListener()方法:
代码语言:javascript复制public static void sendResultToListener(@NonNull Context context,
@NonNull String listenerServiceClassName,
@NonNull HeapDump heapDump,
@NonNull AnalysisResult result) {
Class<?> listenerServiceClass;
try {
// listenerServiceClassName是DisplayLeakService
listenerServiceClass = Class.forName(listenerServiceClassName);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
Intent intent = new Intent(context, listenerServiceClass);
File analyzedHeapFile = AnalyzedHeap.save(heapDump, result);
if (analyzedHeapFile != null) {
intent.putExtra(ANALYZED_HEAP_PATH_EXTRA, analyzedHeapFile.getAbsolutePath());
}
ContextCompat.startForegroundService(context, intent);// 这里就是启动DisplayLeakService
}
这里的处理逻辑很简单,就是启动DisplayLeakService服务。
3-10、DisplayLeakService启动之后,就会执行onHeapAnalyzed方法:
代码语言:javascript复制protected final void onHeapAnalyzed(@NonNull AnalyzedHeap analyzedHeap) {
HeapDump heapDump = analyzedHeap.heapDump;
AnalysisResult result = analyzedHeap.result;
String leakInfo = leakInfo(this, heapDump, result, true);
CanaryLog.d("%s", leakInfo);
heapDump = renameHeapdump(heapDump);
// 1. 保存HeapDump对象和内存泄漏分析结果对象保存到指定的文件
boolean resultSaved = saveResult(heapDump, result);
String contentTitle;
if (resultSaved) {// 保存成功
// 2. 创建一个跳转到DisplayLeakActivity的Intent对象
PendingIntent pendingIntent =
DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey);
if (result.failure != null) {
contentTitle = getString(R.string.leak_canary_analysis_failed);
} else {
String className = classSimpleName(result.className);
if (result.leakFound) {
if (result.retainedHeapSize == AnalysisResult.RETAINED_HEAP_SKIPPED) {
if (result.excludedLeak) {
contentTitle = getString(R.string.leak_canary_leak_excluded, className);
} else {
contentTitle = getString(R.string.leak_canary_class_has_leaked, className);
}
} else {
String size = formatShortFileSize(this, result.retainedHeapSize);
if (result.excludedLeak) {
contentTitle =
getString(R.string.leak_canary_leak_excluded_retaining, className, size);
} else {
contentTitle =
getString(R.string.leak_canary_class_has_leaked_retaining, className, size);
}
}
} else {
contentTitle = getString(R.string.leak_canary_class_no_leak, className);
}
}
String contentText = getString(R.string.leak_canary_notification_message);
// 3. 创建一个供用户点击跳转到DisplayLeakActivity的延时通知
showNotification(pendingIntent, contentTitle, contentText);
} else {
onAnalysisResultFailure(getString(R.string.leak_canary_could_not_save_text));
}
afterDefaultHandling(heapDump, result, leakInfo);
}
// DisplayLeakActivity的createPendingIntent方法
public static PendingIntent createPendingIntent(Context context, String referenceKey) {
setEnabledBlocking(context, DisplayLeakActivity.class, true);
Intent intent = new Intent(context, DisplayLeakActivity.class);
intent.putExtra(SHOW_LEAK_EXTRA, referenceKey);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
return PendingIntent.getActivity(context, 1, intent, FLAG_UPDATE_CURRENT);
}
到此,LeakCanary源码分析完成。