2020-12-09

LeakCanary工作原理和源码解析

内存泄漏一直以来就是Android APP需要着重解决的点,而LeakCanary是一个开源的内存泄漏检测库,通过简单的配置就可以帮我们快速的获取和定位到内存泄漏的位置,这极大的提高了开发的效率和APP的性能。为了能更加好的理解Android中的内存管理机制,我们还是有必要了解下LeakCanary的工作原理的,本篇文章就来对LeakCanary的源码进行解析。

一、简单配置

在build.gradle中添加配置:
支持库的fragments
implementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.2'
public class LeakApplication extends Application {

    private RefWatcher refWatcher;

    @Override
    public void onCreate() {
        super.onCreate();
        refWatcher = setupLeakCanary();
        //        refWatcher.watch(this);
    }

    private RefWatcher setupLeakCanary() {
        // 判断当前进程是否是LeakCanary用于分析heap内存的而创建的HeapAnalyzerService所在进程,
        // 如果是的话,则不进行Application中的初始化功能。
        if (LeakCanary.isInAnalyzerProcess(this)) {
            return RefWatcher.DISABLED;
        }
        // 进行LeakCanary的安装配置
        return LeakCanary.install(this);
    }

    public static RefWatcher getRefWatcher(Context context) {
        LeakApplication leakApplication = (LeakApplication) context.getApplicationContext();
        return leakApplication.refWatcher;
    }
}

二、源码解析

1、先从LeakCanary#install开始看起:

/**
 * Creates a {@link RefWatcher} that works out of the box, and starts watching activity
 * references (on ICS+).
 */
public static @NonNull RefWatcher install(@NonNull Application application) {
  return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
      .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
      .buildAndInstall();
}

调用install方法,里面是链式调用,我们一个一个来分析

2、LeakCanary#refWatcher

public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {
  return new AndroidRefWatcherBuilder(context);
}

AndroidRefWatcherBuilder(@NonNull Context context) {
    // 这里只是保存了ApplicationContext的引用
  this.context = context.getApplicationContext();
}

refWatcher方法就是简单创建了一个AndroidRefWatcherBuilder对象。

3、AndroidRefWatcherBuilder#listenerServiceClass

public @NonNull AndroidRefWatcherBuilder listenerServiceClass(
    @NonNull Class listenerServiceClass) {
  return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
}

/** @see HeapDump.Listener */
public final T heapDumpListener(HeapDump.Listener heapDumpListener) {
    this.heapDumpListener = heapDumpListener;
    return self();
}

public ServiceHeapDumpListener(@NonNull final Context context,
      @NonNull final Class listenerServiceClass) {
    this.listenerServiceClass = checkNotNull(listenerServiceClass, "listenerServiceClass");
    this.context = checkNotNull(context, "context").getApplicationContext();
}

创建了ServiceHeapDumpListener对象并把DisplayLeakService传递给它的一个成员变量,然后把创建的ServiceHeapDumpListener赋值给AndroidRefWatcherBuilder的父类RefWatcherBuilder的成员变量listenerServiceClass,ServiceHeapDumpListener作用是接收一个head dump文件并启动服务(位于独立进程)对文件使用haha库进行解析,接下来可以看得到,这里先说下它的作用。

4、RefWatcherBuilder#excludedRefs

/** @see ExcludedRefs */
public final T excludedRefs(ExcludedRefs excludedRefs) {
  heapDumpBuilder.excludedRefs(excludedRefs);
  return self();
}

// com.squareup.leakcanary.HeapDump.Builder#excludedRefs
public Builder excludedRefs(ExcludedRefs excludedRefs) {
  this.excludedRefs = checkNotNull(excludedRefs, "excludedRefs");
  return this;
}

这个方法是用来记录Android SDK和一些手机厂商定制SDK的时候存在的内存泄漏情况,这些都会被LeakCanary的检测给过滤掉。

5、AndroidRefWatcherBuilder#buildAndInstall

public @NonNull RefWatcher buildAndInstall() {
  if (LeakCanaryInternals.installedRefWatcher != null) {
    throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
  }
  // 1
  RefWatcher refWatcher = build();
  if (refWatcher != DISABLED) {
    LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
    if (watchActivities) {
        // 2
      ActivityRefWatcher.install(context, refWatcher);
    }
    if (watchFragments) {
        // 3
      FragmentRefWatcher.Helper.install(context, refWatcher);
    }
  }
  LeakCanaryInternals.installedRefWatcher = refWatcher;
  return refWatcher;
}

1、RefWatcherBuilder#build

public final RefWatcher build() {
  if (isDisabled()) {
    return RefWatcher.DISABLED;
  }

  if (heapDumpBuilder.excludedRefs == null) {
    heapDumpBuilder.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();
  }

  if (heapDumpBuilder.reachabilityInspectorClasses == null) {
    heapDumpBuilder.reachabilityInspectorClasses(defaultReachabilityInspectorClasses());
  }

  return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener,
      heapDumpBuilder);
}

可以看到,build方法是通过建造者模式构建RefWatcher,在RefWatcher的构造器中传递了多个参数:

  • watchExecutor:实现类是AndroidWatchExecutor,线程控制器,在onDestroy之后且主线程是空闲状态时执行内存泄漏检测。
  • debuggerControl:判断是否处于调试模式,调试模式中不会进行内存泄漏检测。
  • gcTrigger:用来触发gc操作。
  • heapDumper:实现类是AndroidHeapDumper,用来生成hprof文件。
  • heapDumpListener:这个字段就是上面说到的ServiceHeapDumpListener,封装了DisplayLeakService,主要用来开启一个HeapAnalyzerService服务对hprof文件使用haha库进行解析,最后通知会DisplayLeakService弹出泄漏提醒弹窗。
  • heapDumpBuilder.excludedRefs:就是我们上面说到的LeakCanary检测时可以忽略的泄漏路径。
  • heapDumpBuilder.reachabilityInspectorClasses:用于进行可达性检测的类列表。

2、leakcanary.ActivityRefWatcher#install

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) {
          refWatcher.watch(activity);
        }
      };

这里主要实现了Application中的ActivityLifecycleCallbacks对Activity的生命周期进行监听,这样在Activity执行onDestroy之后调用refWatcher.watch(Activity)对Activity进行内存泄漏检测。

3、FragmentRefWatcher.Helper#install

public static void install(Context context, RefWatcher refWatcher) {
  List fragmentRefWatchers = new ArrayList<>();

  if (SDK_INT >= O) {
    // 1
    fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher));
  }

  try {
    Class fragmentRefWatcherClass = Class.forName(SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME);
    Constructor constructor =
        fragmentRefWatcherClass.getDeclaredConstructor(RefWatcher.class);
    FragmentRefWatcher supportFragmentRefWatcher =
        (FragmentRefWatcher) constructor.newInstance(refWatcher);
    // 2
    fragmentRefWatchers.add(supportFragmentRefWatcher);
  } catch (Exception ignored) {
  }

  if (fragmentRefWatchers.size() == 0) {
    return;
  }

    // 3
  Helper helper = new Helper(fragmentRefWatchers);

  Application application = (Application) context.getApplicationContext();
  // 4
  application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks);
}

这个方法主要用来监视fragment的泄漏,在注释1中把标准的Fragment的RefWatcher类AndroidOFragmentRefWatcher添加到fragmentRefWatchers中,注释2是通过反射获取SupportFragmentRefWatcher添加到fragmentRefWatchers中,在注释3中创建helper,在注释4中调用了helper.activityLifecycleCallbacks。

private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks =
    new ActivityLifecycleCallbacksAdapter() {
      @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        for (FragmentRefWatcher watcher : fragmentRefWatchers) {
          watcher.watchFragments(activity);
        }
      }
    };

这里调用了watcher.watchFragments,这个watcher是AndroidOFragmentRefWatcher或者SupportFragmentRefWatcher,我们来看看AndroidOFragmentRefWatcher中watchFragments方法。

@Override public void watchFragments(Activity activity) {
  FragmentManager fragmentManager = activity.getFragmentManager();
  fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
}

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

通过当前传入的activity获取到FragmentManager,并调用registerFragmentLifecycleCallbacks进行监听检测,也就是在onFragmentViewDestroyed和onFragmentDestroyed分别使用refWatcher.watch(view)和refWatcher.watch(fragment)进行内存泄漏的检测。


6、RefWatcher#watch(java.lang.Object)

public void watch(Object watchedReference) {
  watch(watchedReference, "");
}

public void watch(Object watchedReference, String referenceName) {
  if (this == DISABLED) {
  return;
  }
  checkNotNull(watchedReference, "watchedReference");
  checkNotNull(referenceName, "referenceName");
  final long watchStartNanoTime = System.nanoTime();
  // 使用随机的UUID保证了每个检测对象对应的key的唯一性
  String key = UUID.randomUUID().toString();
  // 将key加入到set集合retainedKeys中,在检测对象是否泄漏的情况下会用的上
  retainedKeys.add(key);
  // 1
  final KeyedWeakReference reference = 
                    new KeyedWeakReference(watchedReference, key, referenceName, queue);
  // 2
  ensureGoneAsync(watchStartNanoTime, reference);
}

1、com.squareup.leakcanary.KeyedWeakReference

final class KeyedWeakReference extends WeakReference {
  public final String key;
  public final String name;

  KeyedWeakReference(Object referent, String key, String name,
      ReferenceQueue referenceQueue) {
    // 1
    super(checkNotNull(referent, "referent"), checkNotNull(referenceQueue, "referenceQueue"));
    this.key = checkNotNull(key, "key");
    this.name = checkNotNull(name, "name");
  }
}

这是一个自定义的弱引用KeyedWeakReference,里面绑定了检测对象具有的唯一key和name,在注释1中会将弱引用和引用队列ReferenceQueue关联起来,这里有个弱引用的特性,当弱引用referent持有的对象被GC回收之后,JVM就会把这个弱引用加入到与之关联的引用队列referenceQueue中。

2、RefWatcher#ensureGoneAsync

private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
    // 1
  watchExecutor.execute(new Retryable() {
    @Override public Retryable.Result run() {
        // 2
      return ensureGone(reference, watchStartNanoTime);
    }
  });
}

在注释1中的watchExecutor就是我们上面在构建RefWatcher传入的参数,它的实现是AndroidWatchExecutor。

AndroidWatchExecutor#execute

@Override public void execute(@NonNull Retryable retryable) {
  if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
    waitForIdle(retryable, 0);
  } else {
    postWaitForIdle(retryable, 0);
  }
}

// postWaitForIdle方法也很清晰,还是切回到主线程去执行waitForIdle方法
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.
  // 当主线程空闲的时候才执行queueIdle
  Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
    @Override public boolean queueIdle() {
      postToBackgroundWithDelay(retryable, failedAttempts);
      return false;
    }
  });
}

private void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) {
  long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor);
  // initialDelayMillis的初始值是5
  long delayMillis = initialDelayMillis * exponentialBackoffFactor;
  // 切换的HandlerThread线程执行
  backgroundHandler.postDelayed(new Runnable() {
    @Override public void run() {
      // 执行retryable.run,最终就会执行上面的注释2中的ensureGone方法
      Retryable.Result result = retryable.run();
      // 如果返回的是RETRY,则递归继续执行方法
      if (result == RETRY) {
        postWaitForIdle(retryable, failedAttempts + 1);
      }
    }
  }, delayMillis);
}

RefWatcher#ensureGone

Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
  long gcStartNanoTime = System.nanoTime();
  long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
    // 从引用队列查找已经回收的reference,并把reference对应的key从retainedKeys中移除
  removeWeaklyReachableReferences();

    // 如果是调试模式,直接返回RETRY,不进行内存泄漏检测
  if (debuggerControl.isDebuggerAttached()) {
    // The debugger can create false leaks.
    return RETRY;
  }
  // 判断当前引用reference是否已经被回收(retainedKeys是否含有reference.key,有则说明泄漏)
  if (gone(reference)) {
    return DONE;
  }
  // 触发系统进行gc
  gcTrigger.runGc();
  // 再度从引用队列查找已经回收的reference,并把reference对应的key从retainedKeys中移除
  removeWeaklyReachableReferences();
  // 再度判断当前引用reference是否已经被回收(retainedKeys是否含有reference.key,有则说明泄漏)
  if (!gone(reference)) {
    // 没有回收,判定为泄漏
    long startDumpHeap = System.nanoTime();
    long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
        // 1、 调用heapDumper也就是AndroidHeapDumper的dumpHeap方法生成hprof文件
    File heapDumpFile = heapDumper.dumpHeap();
    if (heapDumpFile == RETRY_LATER) {
      // Could not dump the heap.
      return RETRY;
    }
    long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
        // 将hprof文件heapDumpFile和reference.key等信息封装成heapDump
    HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
        .referenceName(reference.name)
        .watchDurationMs(watchDurationMs)
        .gcDurationMs(gcDurationMs)
        .heapDumpDurationMs(heapDumpDurationMs)
        .build();
        // 2、 这个heapdumpListener就是上面说到的ServiceHeapDumpListener
    heapdumpListener.analyze(heapDump);
  }
  return DONE;
}
private boolean gone(KeyedWeakReference reference) {
    // 如果retainedKeys还包含当前reference.key,说明reference泄漏
  return !retainedKeys.contains(reference.key);
}
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包含某个reference,说明这个reference已经被回收,则调用retainedKeys移除掉reference对应的key
    retainedKeys.remove(ref.key);
  }
}

AndroidHeapDumper#dumpHeap

public File dumpHeap() {
  File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();
  ...
  try {
    // 这里是调用Android SDK的API来生成 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;
  }
}

ServiceHeapDumpListener#analyze

@Override 
public void analyze(@NonNull HeapDump heapDump) {
  checkNotNull(heapDump, "heapDump");
  // 这个listenerServiceClass就是一开始创建ServiceHeapDumpListener传入的DisplayLeakService
  // 方法的作用就是开启一个位于独立进程的HeapAnalyzerService服务使用haha库对hprof文件进行分析,然后把解析结果通过
  // 启动DisplayLeakService服务发送一个通知出去
  HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
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 listenerServiceClass) {
    setEnabledBlocking(context, HeapAnalyzerService.class, true);
    setEnabledBlocking(context, listenerServiceClass, true);
    // 启动了HeapAnalyzerService服务,为了避免减慢APP进程或占用内存,这个服务是开启在一个独立进程中的
    Intent intent = new Intent(context, HeapAnalyzerService.class);
    intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());
    intent.putExtra(HEAPDUMP_EXTRA, heapDump);
    ContextCompat.startForegroundService(context, intent);
  }

    // HeapAnalyzerService继承了ForegroundService,而ForegroundService是继承了IntentService的,当启动服务的时候
    // 则会调用onHandleIntent方法,在而ForegroundService的onHandleIntent中又执行了这个onHandleIntentInForeground方法
  @Override protected void onHandleIntentInForeground(@Nullable Intent intent) {
    if (intent == null) {
      CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
      return;
    }
    String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
    HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);

        // 1
    HeapAnalyzer heapAnalyzer =
        new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses);
    AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,
        heapDump.computeRetainedHeapSize);
    // 2    
    AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
  }
}

@Override 
protected void onHandleIntent(@Nullable Intent intent) {
  onHandleIntentInForeground(intent);
}

在注释1中新建了一个对象heapAnalyzer,顾名思义,它就是用来分析hprof文件信息的,这里调用了它的checkForLeak方法使用haha库来解析hprof文件。

public @NonNull AnalysisResult checkForLeak(@NonNull File heapDumpFile,
    @NonNull String referenceKey,
    boolean computeRetainedSize) {
  ...

  try {
    listener.onProgressUpdate(READING_HEAP_DUMP_FILE);
    // 新建一个内存映射缓存文件buffer
    HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
    // 通过buffer新建一个HprofParser解析器
    HprofParser parser = new HprofParser(buffer);
    listener.onProgressUpdate(PARSING_HEAP_DUMP);
    // 使用HprofParser解析器解析出对应的引用内存快照文件snapshot
    Snapshot snapshot = parser.parse();
    listener.onProgressUpdate(DEDUPLICATING_GC_ROOTS);
    // 删除了gcRoots中重复的根对象
    deduplicateGcRoots(snapshot);
    listener.onProgressUpdate(FINDING_LEAKING_REF);
    // 找到泄漏引用对象
    Instance leakingRef = findLeakingReference(referenceKey, snapshot);

    // False alarm, weak reference was cleared in between key check and heap dump.
    if (leakingRef == null) {
      return noLeak(since(analysisStartNanoTime));
    }
    // 找到泄漏对象的最短强引用链
    return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, computeRetainedSize);
  } catch (Throwable e) {
    return failure(e, since(analysisStartNanoTime));
  }
}
private Instance findLeakingReference(String key, Snapshot snapshot) {
    // 在内存快照中找到所有我们所监测的内存泄漏对象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 keysFound = new ArrayList<>();
  for (Instance instance : refClass.getInstancesList()) {
    List values = classInstanceValues(instance);
    // 通过反射获取与内存泄漏对象KeyedWeakReference绑定的唯一key字段
    Object keyFieldValue = fieldValue(values, "key");
    if (keyFieldValue == null) {
      keysFound.add(null);
      continue;
    }
    String keyCandidate = asString(keyFieldValue);
    // 判断泄漏对象中的key是否和当前所检测的泄漏引用对象的key相等,相等则返回这个泄漏对象。
    if (keyCandidate.equals(key)) {
      return fieldValue(values, "referent");
    }
    keysFound.add(keyCandidate);
  }
  throw new IllegalStateException(
      "Could not find weak reference with key " + key + " in " + keysFound);
}

再来看注释2,当找到当前检测泄漏对象的最短强引用链之后执行AbstractAnalysisResultService#sendResultToListener。

public static void sendResultToListener(@NonNull Context context,
    @NonNull String listenerServiceClassName,
    @NonNull HeapDump heapDump,
    @NonNull AnalysisResult result) {
  Class listenerServiceClass;
  try {
    // 这个listenerServiceClass指的就是DisplayLeakService
    listenerServiceClass = Class.forName(listenerServiceClassName);
  } catch (ClassNotFoundException e) {
    throw new RuntimeException(e);
  }
  Intent intent = new Intent(context, listenerServiceClass);

    // heapDump包含了hprof文件等信息,result对象是上面提到的泄漏对象引用链AnalysisResult
  File analyzedHeapFile = AnalyzedHeap.save(heapDump, result);
  if (analyzedHeapFile != null) {
    intent.putExtra(ANALYZED_HEAP_PATH_EXTRA, analyzedHeapFile.getAbsolutePath());
  }
  // 启动DisplayLeakService服务,这个服务的作用主要是发出一个泄漏通知
  ContextCompat.startForegroundService(context, intent);
}

DisplayLeakService#onHeapAnalyzed

public class DisplayLeakService extends AbstractAnalysisResultService {

    // DisplayLeakService也继承了IntentService,当启动DisplayLeakService时会调用它的
    // onHandleIntent方法,onHandleIntent又会执行onHeapAnalyzed方法
  @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);

    boolean resultSaved = false;
    boolean shouldSaveResult = result.leakFound || result.failure != null;
    if (shouldSaveResult) {
      heapDump = renameHeapdump(heapDump);
      // 保存文件信息和泄漏信息
      resultSaved = saveResult(heapDump, result);
    }

    if (!shouldSaveResult) {
      String contentTitle = getString(R.string.leak_canary_no_leak_title);
      String contentText = getString(R.string.leak_canary_no_leak_text);
      showNotification(null, contentTitle, contentText);
    } else if (resultSaved) {
      String contentTitle;
      String contentText;
      PendingIntent pendingIntent =
          DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey);

      if (result.failure == null) {
        if (result.retainedHeapSize == AnalysisResult.RETAINED_HEAP_SKIPPED) {
          String className = classSimpleName(result.className);
          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);
          String className = classSimpleName(result.className);
          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_analysis_failed);
      }
      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);
  }
    ...
}

DisplayLeakService是一个服务,当保存hprof文件信息和引用泄漏信息成功之后就会发出一个通知供开发者跳转到DisplayLeakActivity中查看泄漏详情。

三、工作机制

  1. RefWatcher.watch()中创建一个自定义弱引用KeyedWeakReference绑定被监控的对象,里面也记录了当前监控对象对应的唯一key。
  2. 然后在主线程空闲的状态下启动后台线程检查弱引用是否被清除,如果没有,调用系统GC,然后再进行检查。
  3. 如果引用还是未被清除,调用系统API把heap内存dump到APP对应的文件系统中的一个hprof文件中,也就是创建一个hprof文件。
  4. 启动HeapAnalyzerService服务,为了避免减慢APP进程和占用内存,这个服务是位于独立进程的,在这个服务会创建一个HeapAnalyzer对象去使用HAHA库解析这个hprof文件。
  5. 在解析这个hprof文件的过程中,HeapAnalyzer会根据唯一的reference key找到这个被监测的泄漏对象KeyedWeakReference,并定位到具体的泄漏引用。
  6. HeapAnalyzer会获取泄漏引用到GC Roots的最短强引用路径,建立导致泄漏的引用链并返回。
  7. 将引用链传递到APP进程的DisplayLeakService服务中,在这个服务会以通知的形式告知开发者发生内存泄漏,开发者可以点击跳转到DisplayLeakActivity中查看具体的泄漏详情。

最后给出两张流程图进行总结

1、LeakCanary#install


2、RefWatcher#watch

你可能感兴趣的:(2020-12-09)