Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
}
public final class ServiceHeapDumpListener implements HeapDump.Listener {
private final Context context;
private final Class<? extends AbstractAnalysisResultService> listenerServiceClass;
public ServiceHeapDumpListener(Context context,
Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
// 启动后台服务监听
setEnabled(context, listenerServiceClass, true);
// 启动 HeapAnalyzerService ,用来分析 dump 文件
setEnabled(context, HeapAnalyzerService.class, true);
this.listenerServiceClass = checkNotNull(listenerServiceClass, “listenerServiceClass”);
this.context = checkNotNull(context, “context”).getApplicationContext();
}
}
listenerServiceClass() 方法绑定了一个后台服务 DisplayLeakService,这个服务主要用来分析内存泄漏结果并发送通知。你可以继承并重写这个类来进行一些自定义操作,比如上传分析结果等。
RefWatcherBuilder.excludedRefs
public final T excludedRefs(ExcludedRefs excludedRefs) {
this.excludedRefs = excludedRefs;
return self();
}
AndroidExcludedRefs.java
/**
-
This returns the references in the leak path that can be ignored for app developers. This
-
doesn’t mean there is no memory leak, to the contrary. However, some leaks are caused by bugs
-
in AOSP or manufacturer forks of AOSP. In such cases, there is very little we can do as app
-
developers except by resorting to serious hacks, so we remove the noise caused by those leaks.
*/
public static ExcludedRefs.Builder createAppDefaults() {
return createBuilder(EnumSet.allOf(AndroidExcludedRefs.class));
}
public static ExcludedRefs.Builder createBuilder(EnumSet refs) {
ExcludedRefs.Builder excluded = ExcludedRefs.builder();
for (AndroidExcludedRefs ref : refs) {
if (ref.applies) {
ref.add(excluded);
((ExcludedRefs.BuilderWithParams) excluded).named(ref.name());
}
}
return excluded;
}
excludedRefs() 方法定义了一些对于开发者可以忽略的路径,意思就是即使这里发生了内存泄漏,LeakCanary 也不会弹出通知。这大多是系统 Bug 导致的,无需用户进行处理。
AndroidRefWatcherBuilder.buildAndInstall
buildAndInstall 所做的工作,调用 build 构建 refWatcher,判断 refWatcher 是否 DISABLED,若不是 DISABLED 状态,调用 install 方法,并将 refWatcher 返回回去
/**
- Creates a {@link RefWatcher} instance and starts watching activity references (on ICS+).
*/
public RefWatcher buildAndInstall() {
// 构建 refWatcher 对象
RefWatcher refWatcher = build();
// 判断是否 DISABLED,若不是 DISABLED 状态,调用
if (refWatcher != DISABLED) {
LeakCanary.enableDisplayLeakActivity(context);
ActivityRefWatcher.install((Application) context, refWatcher);
}
return refWatcher;
}
了解 build 方法 之前,我们先来看一下 RefWatcherBuilder 是什么东东?
RefWatcherBuilder
public class RefWatcherBuilder<T extends RefWatcherBuilder> {
private ExcludedRefs excludedRefs;
private HeapDump.Listener heapDumpListener;
private DebuggerControl debuggerControl;
private HeapDumper heapDumper;
private WatchExecutor watchExecutor;
private GcTrigger gcTrigger;
/** Creates a {@link RefWatcher}. */
public final RefWatcher build() {
if (isDisabled()) {
return RefWatcher.DISABLED;
}
ExcludedRefs excludedRefs = this.excludedRefs;
if (excludedRefs == null) {
excludedRefs = defaultExcludedRefs();
}
HeapDump.Listener heapDumpListener = this.heapDumpListener;
if (heapDumpListener == null) {
heapDumpListener = defaultHeapDumpListener();
}
DebuggerControl debuggerControl = this.debuggerControl;
if (debuggerControl == null) {
debuggerControl = defaultDebuggerControl();
}
HeapDumper heapDumper = this.heapDumper;
if (heapDumper == null) {
heapDumper = defaultHeapDumper();
}
WatchExecutor watchExecutor = this.watchExecutor;
if (watchExecutor == null) {
watchExecutor = defaultWatchExecutor();
}
GcTrigger gcTrigger = this.gcTrigger;
if (gcTrigger == null) {
gcTrigger = defaultGcTrigger();
}
return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener,
excludedRefs);
}
build 方法看到这里你是不是有一种很眼熟的感觉,没错,它运用了建造者模式,与我们 Android 中的 AlertDialog.build 同出一辙。 建造者模式(Builder)及其应用
RefWatcherBuilder 主要有几个重要的成员变量
-
watchExecutor : 线程控制器,在 onDestroy() 之后并且主线程空闲时执行内存泄漏检测
-
debuggerControl : 判断是否处于调试模式,调试模式中不会进行内存泄漏检测
-
gcTrigger : 用于 GC,watchExecutor 首次检测到可能的内存泄漏,会主动进行 GC,GC 之后会再检测一次,仍然泄漏的判定为内存泄漏,进行后续操作
-
heapDumper : dump 内存泄漏处的 heap 信息,写入 hprof 文件
-
heapDumpListener : 解析完 hprof 文件,进行回调,并通知 DisplayLeakService 弹出提醒
-
excludedRefs : 排除可以忽略的泄漏路径
接下来,我们一起来看一下 ActivityRefWatcher.install 方法
ActivityRefWatcher.install((Application) context, refWatcher);
public final class ActivityRefWatcher {
/** @deprecated Use {@link #install(Application, RefWatcher)}. */
@Deprecated
public static void installOnIcsPlus(Application application, RefWatcher refWatcher) {
install(application, refWatcher);
}
public static void install(Application application, RefWatcher refWatcher) {
new ActivityRefWatcher(application, refWatcher).watchActivities();
}
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);
}
};
private final Application application;
private final RefWatcher refWatcher;
/**
-
Constructs an {@link ActivityRefWatcher} that will make sure the activities are not leaking
-
after they have been destroyed.
*/
public ActivityRefWatcher(Application application, RefWatcher refWatcher) {
this.application = checkNotNull(application, “application”);
this.refWatcher = checkNotNull(refWatcher, “refWatcher”);
}
void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
public void watchActivities() {
// Make sure you don’t get installed twice.
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}
public void stopWatchingActivities() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);
}
}
install 来说,主要做以下事情
-
创建 ActivityRefWatcher,并调用 watchActivities 监听 activity 的生命周期
-
在 activity 被销毁的时候,会回调 lifecycleCallbacks 的 onActivityDestroyed 方法,这时候会调用 onActivityDestroyed 去分析,而 onActivityDestroyed 方法又会回调 refWatcher.watch(activity)
我们回到 refWatcher.watch 方法
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();
// 保证 key 的唯一性
String key = UUID.randomUUID().toString();
// 添加到 set 集合中
retainedKeys.add(key);
// 穿件 KeyedWeakReference 对象
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
ensureGoneAsync(watchStartNanoTime, reference);
}
-
retainedKeys : 一个 Set 集合,每个检测的对象都对应着一个唯一的 key,存储在 retainedKeys 中
-
KeyedWeakReference : 自定义的弱引用,持有检测对象和对用的 key 值
我们先来看一下 KeyedWeakReference ,可以看到 KeyedWeakReference 继承于 WeakReference,并定义了 key,name 字段
final class KeyedWeakReference extends WeakReference {
public final String key;
public final String name;
KeyedWeakReference(Object referent, String key, String name,
ReferenceQueue referenceQueue) {
super(checkNotNull(referent, “referent”), checkNotNull(referenceQueue, “referenceQueue”));
this.key = checkNotNull(key, “key”);
this.name = checkNotNull(name, “name”);
}
}
-
key 对应的 key 值名称
-
referenceQueue 引用队列,当结合 Refrence 使用的时候,垃圾回收器回收的时候,会把相应的对象加入到 refrenceQueue 中。
弱引用和引用队列 ReferenceQueue 联合使用时,如果弱引用持有的对象被垃圾回收,Java 虚拟机就会把这个弱引用加入到与之关联的引用队列中。即 KeyedWeakReference 持有的 Activity 对象如果被垃圾回收,该对象就会加入到引用队列 queue 中。具体的可以参考我的这一篇博客 java 源码系列 - 带你读懂 Reference 和 ReferenceQueue
ensureGoneAsync 方法
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}
ensureGoneAsync 这个方法,在 watchExecutor 的回调里面执行了 ensureGone 方法,watchExecutor 是 AndroidWatchExecutor 的实例。
接下来,我们一起来看一下 watchExecutor,主要关注 execute 方法
watchExecutor
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) {
mainHandler = new Handler(Looper.getMainLooper());
HandlerThread handlerThread = new HandlerThread(LEAK_CANARY_THREAD_NAME);
handlerThread.start();
backgroundHandler = new Handler(handlerThread.getLooper());
this.initialDelayMillis = initialDelayMillis;
maxBackoffFactor = Long.MAX_VALUE / initialDelayMillis;
}
@Override public void execute(Retryable retryable) {
// 当前线程是主线程
if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
waitForIdle(retryable, 0);
} else { // 当前线程不是主线程
postWaitForIdle(retryable, 0);
}
}
}
execute 方法,首先判断是否是主线程,如果是主线程,调用 waitForIdle 方法,等待空闲的时候执行,如果不是主线程,调用 postWaitForIdle 方法。我们一起来看一下 postWaitForIdle 和 waitForIdle 方法。
// 调用 mainHandler 的 post 方法,,确保在主线程中执行
void postWaitForIdle(final Retryable retryable, final int failedAttempts) {
mainHandler.post(new Runnable() {
@Override public void run() {
waitForIdle(retryable, failedAttempts);
}
});
}
// 当当前线程 looper 空闲的时候执行
void waitForIdle(final Retryable retryable, final int failedAttempts) {
// This needs to be called from the main thread.
// 当 looper 空闲的时候,会回调 queueIdle 方法
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override public boolean queueIdle() {
postToBackgroundWithDelay(retryable, failedAttempts);
return false;
}
});
}
可以看到 postWaitForIdle 方法其实是 调用 mainHandler 的 post 方法,,确保在主线程中执行,之后再 runnable 的 run 方法在调用 waitForIdle 方法。而 waitForIdle 方法是在等当前 looper 空闲之后,执行 postToBackgroundWithDelay 方法
void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) {
// 取 Math.pow(2, failedAttempts), maxBackoffFactor 的最小值,maxBackoffFactor = Long.MAX_VALUE / 5,
// 第一次执行的时候 failedAttempts 是 0 ,所以 exponentialBackoffFactor 是1
long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor);
// initialDelayMillis 的默认值是 5
long delayMillis = initialDelayMillis * exponentialBackoffFactor;
// 所以第一次延迟执行的时候是 5s,若
backgroundHandler.postDelayed(new Runnable() {
@Override public void run() {
Retryable.Result result = retryable.run();
// 过 result == RETRY,再次调用 postWaitForIdle,下一次的 delayMillis= 上一次的 delayMillis *2;
// 正常情况下,不会返回 RETRY,当 heapDumpFile == RETRY_LATER (即 dump heap 失败的时候),会返回 RETRY
if (result == RETRY) {
postWaitForIdle(retryable, failedAttempts + 1);
}
}
}, delayMillis);
}
postToBackgroundWithDelay 方法有点类似递归,正常情况下,若 retryable.run() 返回的结果不等于 RETRY,只会执行一次。若 retryable.run() 返回 RETRY,则会执行多次,退出的条件是 retryable.run() 返回结果不等于 RETRY;
delay 的时间 取 Math.pow(2, failedAttempts), maxBackoffFactor 两个数的最小值,maxBackoffFactor = Long.MAX_VALUE / 5,而,第一次执行的时候 failedAttempts 是 0 ,所以 exponentialBackoffFactor 是 1,即 delayMillis = initialDelayMillis * exponentialBackoffFactor= 5*1=5;
因此,综合上面的例子,第一次执行的时间是 activity destroy 之后 5s。
OK,我们回到 ensureGone 方法,这才是我们的重点
@SuppressWarnings(“ReferenceEquality”) // Explicitly checking for named null.
elayMillis *2;
// 正常情况下,不会返回 RETRY,当 heapDumpFile == RETRY_LATER (即 dump heap 失败的时候),会返回 RETRY
if (result == RETRY) {
postWaitForIdle(retryable, failedAttempts + 1);
}
}
}, delayMillis);
}
postToBackgroundWithDelay 方法有点类似递归,正常情况下,若 retryable.run() 返回的结果不等于 RETRY,只会执行一次。若 retryable.run() 返回 RETRY,则会执行多次,退出的条件是 retryable.run() 返回结果不等于 RETRY;
delay 的时间 取 Math.pow(2, failedAttempts), maxBackoffFactor 两个数的最小值,maxBackoffFactor = Long.MAX_VALUE / 5,而,第一次执行的时候 failedAttempts 是 0 ,所以 exponentialBackoffFactor 是 1,即 delayMillis = initialDelayMillis * exponentialBackoffFactor= 5*1=5;
因此,综合上面的例子,第一次执行的时间是 activity destroy 之后 5s。
OK,我们回到 ensureGone 方法,这才是我们的重点
@SuppressWarnings(“ReferenceEquality”) // Explicitly checking for named null.