LeakCanary 源码解析笔记整理(一)

主要参考文章:
1、看完这篇 LeakCanary 原理分析,又可以虐面试官了!
2、007 LeakCanary 内存泄漏原理完全解析(推荐,本笔记的主要摘抄对象)
3、Android 源码系列之从源码的角度深入理解LeakCanary的内存泄露检测机制(下)(这一篇比较老了)

另附:LeakCanary 源码解析笔记整理(二)

源码版本为:com.squareup.leakcanary:leakcanary-android:1.6.2


这里的笔记直接切入主题 —— LeakCanary 是怎么实现检测 Activity 的内存泄漏的

最根本的,就是 LeakCanary 会利用 Android 4.0 之后的 API,给 Application 添加了监听 Activity 生命周期的回调接口(利用
Application#registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) 方法),关键源码如下:

// 首先,需要在 Application 调用 LeakCanary.install(Context) 方法
// LeakCanary.install(Context)
public static @NonNull RefWatcher install(@NonNull Application application) {
  return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
      .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
      // 调用 AndroidRefWatcherBuilder#buildAndInstall()
      .buildAndInstall();
}

// 在 LeakCanary.install() 中最终会调用 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) {
    LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
    if (watchActivities) {
      // 然后进一步调用 ActivityRefWatcher.install()
      ActivityRefWatcher.install(context, refWatcher);
    }
    if (watchFragments) {
      FragmentRefWatcher.Helper.install(context, refWatcher);
    }
  }
  LeakCanaryInternals.installedRefWatcher = refWatcher;
  return refWatcher;
}

// 从而来到 ActivityRefWatcher.install() 方法中
// ActivityRefWatcher.java
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) {
     	// 检测内存泄漏的关键,实际上,该方法可以检测所有对象是否内存泄漏,而不仅仅是 activity
        refWatcher.watch(activity);
      }
    };

ActivityRefWatcher.install() 方法中,可以看到,lifecycleCallbacksApplication.ActivityLifecycleCallbacks 的具体实现,当 Activity Destroyed 的时候,会将该 Activity 的引用给进一步处理。

refWatcher.watch(activity) 这一行代码的执行逻辑为:

// RefWatcher.java
public void watch(Object watchedReference) {
  watch(watchedReference, "");
}

// watchedReference 即为前面传递过来的 activity 的引用
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();
  // 将 key 保存起来(retainedKeys 为 Set)
  retainedKeys.add(key);
  // 将 key 与 watchedReference 进一步包装到 KeyedWeakReference 中,
  // 从而将 watchedReference 与 key 对应起来
  // watchedReference 即被观察是否泄漏的对象
  final KeyedWeakReference reference =
      new KeyedWeakReference(watchedReference, key, referenceName, queue);
      
  ensureGoneAsync(watchStartNanoTime, reference);
}

可以看到,上面会将前面传递过来的 activity 引用给包装成一个 KeyedWeakReference 对象,而 KeyedWeakReference 继承自 WeakReference,即弱引用。

有关于 弱引用 WeakReference 和 引用队列 ReferenceQueue 可以着重看下 007 LeakCanary 内存泄漏原理完全解析 的 4.4.1 小节。

之后,就会执行 ensureGoneAsync(watchStartNanoTime, reference) 这一句代码,而这里就是实现检测内存泄漏的关键。

// RefWatcher.java
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
  // 通过 watchExecutor,实现给主线程添加 IdleHandler,当主线程空闲的时候,
  // 就会处理添加的 IdleHandler,从而利用通过 execute() 方法传递过去的 Retryable 的实例
  // 进一步的触发 ensureGone() 方法
  // 但 ensureGone() 方法又是在子线程中执行的,因为 Retryable 的 run() 方法是在子线程中执行的
  watchExecutor.execute(new Retryable() {
    @Override public Retryable.Result run() {
      return ensureGone(reference, watchStartNanoTime);
    }
  });
}

watchExecutorAndroidWatchExecutor 的实例

// AndroidWatchExecutor.java

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(@NonNull Retryable retryable) {
  if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
    // 给主线程的 MessageQueue 添加 IdleHandler
    // IdleHandler 可以在主线程空闲的时候执行
    waitForIdle(retryable, 0);
  } else {
    // 如果当前线程不是主线程,则利用主线程的 handler 切换到主线程再执行 waitForIdle()
    postWaitForIdle(retryable, 0);
  }
}
private void postWaitForIdle(final Retryable retryable, final int failedAttempts) {
  mainHandler.post(new Runnable() {
    @Override public void run() {
      waitForIdle(retryable, failedAttempts);
    }
  });
}

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() {
      // 当主线程空闲的时候,就会通过添加的 IdleHandler 
      // 执行 postToBackgroundWithDelay() 方法
      postToBackgroundWithDelay(retryable, failedAttempts);
      // return false 表示执行完之后,就立即移除这个事件
      return false;
    }
  });
}

private void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) {
  long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor);
  // 计算 runnable 延迟执行的时间,该时间受到 failedAttempts 的影响
  // failedAttempts 则是失败尝试次数,即 retryable 每重新执行一次,
  // failedAttempts 就会加一,实际上即受 retryable.run() 中调用的 
  long delayMillis = initialDelayMillis * exponentialBackoffFactor;
  // 利用子线程 handler(AndroidWatchExecutor 从构造方法中开启的子线程)在子线程
  // 中执行 retryable.run(),即实际上执行 ensureGone() 方法调用
  // 的 RefWatcher#ensureGone() 方法的返回值影响(具体看后面的讲解)
  backgroundHandler.postDelayed(new Runnable() {
    @Override public void run() {
      // retryable.run() 返回的结果即为 ensureGone() 返回的结果
      // 如果返回为 RETRY,即表示要重试
      // 重试的情景会在后面有说到
      // 而 retryable 则是前面在 RefWatcher#ensureGoneAsync() 方法中
      // 通过 watchExecutor.execute() 传递过来的
      Retryable.Result result = retryable.run();
      if (result == RETRY) {
        postWaitForIdle(retryable, failedAttempts + 1);
      }
    }
  }, delayMillis);
}

接下来,就需要重点关注 ensureGone() 方法了
(本段主要摘抄自 007 LeakCanary 内存泄漏原理完全解析 — 4.4.5 ensureGone 方法)

// RefWatcher.java

// 从上面可以知道,该方法是在子线程中调用的
@SuppressWarnings("ReferenceEquality") // Explicitly checking for named null.
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
  long gcStartNanoTime = System.nanoTime();
  long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);

  // 移除所有弱引用可达对象,下面有解释
  removeWeaklyReachableReferences();

  // 判断当前是否正在开启 USB调试,LeakCanary 的解释是调试时可能会触发不正确的内存泄漏
  if (debuggerControl.isDebuggerAttached()) {
    // The debugger can create false leaks.
    return RETRY;
  }

   // 上面执行 removeWeaklyReachableReferences 方法,判断是不是监视对象已经被回收了,
   // 如果被回收了,那么说明没有发生内存泄漏,直接结束
  if (gone(reference)) {
    return DONE;
  }
  
  // 触发一次 GC
  // 没有使用 System.gc() 方法进行回收,因为 System.gc() 并不会每次都执行。
  // 而是从 AOSP 中拷贝一段 GC 回收的代码,从而相比 System.gc() 更能够保证进行垃圾回收的工作。
  // (从源码可知,不展开了)
  gcTrigger.runGc();
  
  // 再次移除所有弱引用可达对象
  removeWeaklyReachableReferences();
  
  // 如果对象没有被回收
  if (!gone(reference)) {
    long startDumpHeap = System.nanoTime();
    long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
    // 使用 Debug 类 dump 当前堆内存中对象使用情况
    File heapDumpFile = heapDumper.dumpHeap();// (1)
    // dumpHeap 失败的话,会走重试机制
    if (heapDumpFile == RETRY_LATER) {
      // Could not dump the heap.
      return RETRY;
    }
    long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
    // 将hprof文件、key等属性构造一个 HeapDump 对象
    HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
        .referenceName(reference.name)
        .watchDurationMs(watchDurationMs)
        .gcDurationMs(gcDurationMs)
        .heapDumpDurationMs(heapDumpDurationMs)
        .build();
    // heapdumpListener 分析 heapDump 对象
    heapdumpListener.analyze(heapDump); // (2)
  }
  return DONE;
}

// 在 refWatcher.watch() 方法保存了当前监视对象的 ref.key 了,如果这个对象被回收了,
// 那么对应的弱引用对象会在回收时被添加到queue中,通过 poll 操作就可以取出这个弱引用,
// 这时候我们从 retainedKeys中移除这个 key, 代表这个对象已经被正常回收,不需要再被监视了。
private void removeWeaklyReachableReferences() {
  KeyedWeakReference ref;
  while ((ref = (KeyedWeakReference) queue.poll()) != null) {
    retainedKeys.remove(ref.key);
  }
}

// 判断是不是被回收了,根据 removeWeaklyReachableReferences() 的逻辑
// 如果不存在于 retainedKeys 中就表示被回收了
private boolean gone(KeyedWeakReference reference) {
  return !retainedKeys.contains(reference.key);
}

上面就是检测内存泄漏的主体流程了。

(1) heapDumper.dumpHeap()

// AndroidHeapDumper.java

@SuppressWarnings("ReferenceEquality") // Explicitly checking for named null.
@Override @Nullable
public File dumpHeap() {
  // 生成一个存储 hprof 的文件
  File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();
  // 文件创建失败
  if (heapDumpFile == RETRY_LATER) {
    return RETRY_LATER;
  }
  // FutureResult 内部有一个 CountDownLatch,用于倒计时
  FutureResult<Toast> waitingForToast = new FutureResult<>();
  // 切换到主线程显示 toast
  showToast(waitingForToast);
  // 等待 5 秒,确保 toast 已完成显示,
  // 如果超过 5 秒还没有因后面说的 “countDown() 一次就会使闭锁上等待的线程就可以恢复执行任务”
  // 则表示 showToast() 里面出了问题导致超时了,则抛出异常
  // 前面说过 ensureGone() 方法是在子线程中执行,则这里则也处于该子线程中
  // 因此等待 5 秒不会影响主线程
  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 {
    // 开始 dump 内存到指定文件,借助系统提供的 Debug 类
    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;
  }
}

这里需要着重讲下下面的这段逻辑,因为用到了一个新的东西 CountDownLatch,之后我也是看了半天才整明白其具体的实现原理,所以这里写出来,做下具体的说明。

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;
}

这一段的作用就是为了在主线程显示一个 toast 用于提示内存泄漏了。

private void showToast(final FutureResult<Toast> waitingForToast) {
  mainHandler.post(new Runnable() {
    @Override public void run() {
      // 如果 activity 已经 paused 的话,直接返回
      if (resumedActivity == null) {
      	// set 方法会间接调用 latch.countDown(),就会使调用 latch.await() 线程就会从阻塞中恢复
        waitingForToast.set(null);
        return;
      }
      final Toast toast = new Toast(resumedActivity);
      toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
      toast.setDuration(Toast.LENGTH_LONG);
      LayoutInflater inflater = LayoutInflater.from(resumedActivity);
      toast.setView(inflater.inflate(R.layout.leak_canary_heap_dump_toast, null));
      // 先调用 toast.show() 来显示 toast
      // 所有的 Toast 对象,并不是一调用 show 方法就立即显示的。
      // 而是 NotificationServiceManager 会从 mToastQueue 中轮询去除 Toast 对象进行显示
      toast.show();
      
      // Waiting for Idle to make sure Toast gets rendered.
      
      // 我们如何知道 Toast 是否已经显示完成了呢?我们在 Toast 调用 show 方法后
      // 调用 addIdleHandler, 在主进程空闲时执行 CountDownLatch 的减一操作。
      // 由于我们知道我们顺序加入到主线程的消息队列中的操作:先显示Toast,再执行 CountDownLatch 减一操作(即间接调用 latch.countDown())
      // 所以当间接调用 latch.countDown() 的时候,就会使调用 latch.await() 线程就会从阻塞中恢复
      // (因为 latch 在初始化的时候设置为 1 )
      Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
        @Override public boolean queueIdle() {
          // set 方法会间接调用 latch.countDown()
          waitingForToast.set(toast);
          return false;
        }
      });
    }
  });
}
public final class FutureResult<T> {
  private final AtomicReference<T> resultHolder;
  private final CountDownLatch latch;
  public FutureResult() {
    resultHolder = new AtomicReference<>();
    // latch 在初始化的时候设置为 1
    // 因此只要 countDown() 一次就会使闭锁上等待的线程就可以恢复执行任务
    latch = new CountDownLatch(1);
  }

  // 等待 timeout 时间
  public boolean wait(long timeout, TimeUnit unit) {
    try {
      // 利用 CountDownLatch#await() 来实现的
      return latch.await(timeout, unit);
    } catch (InterruptedException e) {
      throw new RuntimeException("Did not expect thread to be interrupted", e);
    }
  }
  public T get() {
    if (latch.getCount() > 0) {
      throw new IllegalStateException("Call wait() and check its result");
    }
    return resultHolder.get();
  }
  public void set(T result) {
    resultHolder.set(result);
    latch.countDown();
  }
}

(有关 CountDownLatch是什么

(2)heapdumpListener.analyze(heapDump)

heapdumpListener 实际上为 ServiceHeapDumpListener 的实例

// ServiceHeapDumpListener#analyze()
@Override public void analyze(@NonNull HeapDump heapDump) {
  checkNotNull(heapDump, "heapDump");
  HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}

HeapAnalyzerService 继承自 ForegroundService,其处于一个单独的进程,以避免减慢 App 进程或占用内存。当调用 startForegroundService() 的时候就会回调 onHandleIntentInForeground() 方法。

/**
 * This service runs in a separate process to avoid slowing down the app process or making it run
 * out of memory.
 */
public final class HeapAnalyzerService extends ForegroundService
    implements AnalyzerProgressListener {

  private static final String LISTENER_CLASS_EXTRA = "listener_class_extra";
  private static final String HEAPDUMP_EXTRA = "heapdump_extra";

  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());
    intent.putExtra(HEAPDUMP_EXTRA, heapDump);
    ContextCompat.startForegroundService(context, intent);
  }

  public HeapAnalyzerService() {
    super(HeapAnalyzerService.class.getSimpleName(), R.string.leak_canary_notification_analysing);
  }

  @Override protected void onHandleIntentInForeground(@Nullable Intent intent) {
    if (intent == null) {
      CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
      return;
    }

	// 用于监听 hprof 文件分析结果的类类名
    String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
    // 	获取前面传递过来的 hprof 文件类
    HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);

    // 新建一个 HeapAnalyzer 对象,其根据 RefWatcher 生成的 heap dumps 信息来分析被怀疑的泄漏是否是真的
    HeapAnalyzer heapAnalyzer =
        new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses);
    // checkForLeak 会调用 haha 组件中的工具,分析 hprof 文件(下面会讲 checkForLeak() 方法)
    AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,
        heapDump.computeRetainedHeapSize);
    // 将分析结果发送给监听器 listenerClassName( listenerClassName 实际上是 DisplayLeakService,原因在后面会讲)
    // sendResultToListener() 会启动 listenerClassName,即 DisplayLeakService
    // (下面会讲 DisplayLeakService)
    AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
  }

  @Override public void onProgressUpdate(Step step) {
    int percent = (int) ((100f * step.ordinal()) / Step.values().length);
    CanaryLog.d("Analysis in progress, working on: %s", step.name());
    String lowercase = step.name().replace("_", " ").toLowerCase();
    String message = lowercase.substring(0, 1).toUpperCase() + lowercase.substring(1);
    showForegroundNotification(100, percent, false, message);
  }
}

(1)有关 checkForLeak() 方法

// HeapAnalyzer.java
/**
 * Searches the heap dump for a {@link KeyedWeakReference} instance with the corresponding key,
 * and then computes the shortest strong reference path from that instance to the GC roots.
 */
public @NonNull AnalysisResult checkForLeak(@NonNull File heapDumpFile,
    @NonNull String referenceKey,
    boolean computeRetainedSize) {
  long analysisStartNanoTime = System.nanoTime();
  // 文件不存在的话,直接返回
  if (!heapDumpFile.exists()) {
    Exception exception = new IllegalArgumentException("File does not exist: " + heapDumpFile);
    return failure(exception, since(analysisStartNanoTime));
  }
  try {
    // 更新进度回调
    listener.onProgressUpdate(READING_HEAP_DUMP_FILE);
    // 新建一个内存映射缓存文件 buffer
    HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
    // 会使用 buffer 新建一个 HprofParser 解析器去解析出对应的引用内存快照文件 snapshot
    HprofParser parser = new HprofParser(buffer);
    listener.onProgressUpdate(PARSING_HEAP_DUMP);
    // 将 hprof 文件解析成 Snapshot(Snapshot 是快照的意思,hprof 文件的解析是由开源项目 haha 完成的)
    Snapshot snapshot = parser.parse();
    listener.onProgressUpdate(DEDUPLICATING_GC_ROOTS);
    // 为了减少在 Android 6.0 版本中重复 GCRoots 带来的内存压力的影响,
    // 使用deduplicateGcRoots() 删除了 gcRoots 中重复的根对象 RootObj
    deduplicateGcRoots(snapshot);
    listener.onProgressUpdate(FINDING_LEAKING_REF);
    // 调用了 findLeakingReference() 方法将传入的 referenceKey 和 snapshot 对象
    // 里面所有类实例的字段值对应的 keyCandidate 进行比较,如果没有相等的,则表示没有发生内存泄漏
    Instance leakingRef = findLeakingReference(referenceKey, snapshot);
    // 没有内存泄漏,直接返回一个没有泄漏的分析结果 AnalysisResult 对象
    if (leakingRef == null) {
      String className = leakingRef.getClassObj().getClassName();
      return noLeak(className, since(analysisStartNanoTime));
    }
    // 有内存泄漏,则返回一个有泄漏分析结果的 AnalysisResult 对象
    return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, computeRetainedSize);
  } catch (Throwable e) {
    return failure(e, since(analysisStartNanoTime));
  }
}

private Instance findLeakingReference(String key, Snapshot snapshot) {
  // 从 hprof 文件保存的对象中找到所有 KeyedWeakReference 的实例
  // (KeyedWeakReference 即前面说过的用户包装的弱引用类)
  ClassObj refClass = snapshot.findClass(KeyedWeakReference.class.getName());
  if (refClass == null) {
    throw new IllegalStateException(
        "Could not find the " + KeyedWeakReference.class.getName() + " class in the heap dump.");
  }
  List<String> keysFound = new ArrayList<>();
  // 对 KeyedWeakReference 实例列表进行遍历
  for (Instance instance : refClass.getInstancesList()) {
    // 获取每个实例里的所有字段
    List<ClassInstance.FieldValue> values = classInstanceValues(instance);
    // 找到 key 字段对应的值
    Object keyFieldValue = fieldValue(values, "key");
    if (keyFieldValue == null) {
      keysFound.add(null);
      continue;
    }
    // 将 keyFieldValue 转为 String 对象
    String keyCandidate = asString(keyFieldValue);
    if (keyCandidate.equals(key)) {
      // 如果这个对象的 key 和 我们查找的 key 相同,那么返回这个弱对象持有的原对象
      return fieldValue(values, "referent");
    }
    keysFound.add(keyCandidate);
  }
  throw new IllegalStateException(
      "Could not find weak reference with key " + key + " in " + keysFound);
}

(2)有关 AbstractAnalysisResultService.sendResultToListener()AbstractAnalysisResultService 的实现类为 DisplayLeakService,在里面直接创建一个由 PendingIntent 构建的泄漏通知用于供用户点击去展示详细的泄漏界面 DisplayLeakActivity

从注释可知,我们也可以继承这个类并且覆盖 afterDefaultHandling() 方法去实现自己的行为。

/**
 * Logs leak analysis results, and then shows a notification which will start {@link
 * DisplayLeakActivity}.
 * <p>
 * You can extend this class and override {@link #afterDefaultHandling(HeapDump, AnalysisResult,
 * String)} to add custom behavior, e.g. uploading the heap dump.
 */
public class DisplayLeakService extends AbstractAnalysisResultService {
  @Override
  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);
    // 当分析的堆信息文件保存成功之后才会继续
    boolean resultSaved = saveResult(heapDump, result);
    String contentTitle;
    if (resultSaved) {
      // 创建一个供用户点击跳转到DisplayLeakActivity的延时通知
      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);
      showNotification(pendingIntent, contentTitle, contentText);
    } else {
      onAnalysisResultFailure(getString(R.string.leak_canary_could_not_save_text));
    }
    afterDefaultHandling(heapDump, result, leakInfo);
  }
}

(3)HeapAnalyzerService.runAnalysis() 中的 listenerClassName 实际上为 DisplayLeakService 是因为 AndroidRefWatcherBuilder.listenerServiceClass()传入了一个 DisplayLeakService 的 Class 对象,它的作用是展示泄露分析的结果日志,然后会展示一个用于跳转到显示泄露界面DisplayLeakActivity 的通知。

// LeakCanary.java
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);
}

// AndroidRefWatcherBuilder.java
public @NonNull AndroidRefWatcherBuilder listenerServiceClass(
    @NonNull Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
  enableDisplayLeakActivity = DisplayLeakService.class.isAssignableFrom(listenerServiceClass);
  return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值