剖析LeakCanary—— 下篇

不要小看那些信靠上帝的人,祂能借着他们成就大事

本篇文章就是为了说明LeakCanary是如何打造Android平台上的内存泄漏监测过程。按照剖析LeakCanary—— 中篇的思路进行展开论述。

1. 总览LeakCanary的Android实现过程

这里先罗列一些涉及Android平台的具体实现类

  1. AndroidWatchExecutor —> WatchExecutor
  2. AndroidRefWatcherBuilder —> RefWatcherBuilder
  3. LeakCanaryInternals : 可以理解为Android平台的工具类
  4. AndroidExcludedRefs:枚举(过滤Android SDK中的内存泄漏)
  5. HeapAnalyzerService -->ForegroundService —> IntentService
  6. DisplayLeakService —> AbstractAnalysisResultService —> ForegroundService —> IntentService
  7. ServiceHeapDumpListener —> HeapDump.Listener 监听分析过程及结果。(HeapAnalyzerService,DisplayLeakService就是从这里启动的)

HeapAnalyzerService:分析堆内容肯定是个耗时操作,故LeakCanary采用开启IntentService的方式处理;
同理,
DisplayLeakService :在使用LeakCanary的过程中,我们会看到一些通知(Notification) 以及展示泄漏情况的DisplayLeakActivity

具体我们可以看到:
HeapAnalyzerService 在自己的runAnalysis()中启动;
DisplayLeakServiceHeapAnalyzerServiceonHandleIntentInForeground() 需要返回分析结果时才启动(具体可以看 :AbstractAnalysisResultService)。

在分析结果没有出来之前,DisplayLeakService的服务是不会启动的


2. AndroidWatchExecutor

AndroidWatchExecutorrLeakCanaryAndroid系统中的监测者。

这里利用**IdleHandler且是主线程的MessageQueue的IdleHandler **充分了体现LeakCanary的设计之巧妙啊…

关于IdleHandler可以查看 Handler的前世今生5 —— MessageQueue

通过MessageQueue的next()中我们可以知道,IdleHandler 只有在消息队列为空 或者 第一个消息在队列中但还没有到执行时间 的情况下会被执行。即:线程阻塞时会执行IdleHandler。

public final class AndroidWatchExecutor implements WatchExecutor {
  private final Handler mainHandler;
  private final Handler backgroundHandler;

  ...
	
	@Override public void execute(@NonNull Retryable retryable) {
    if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
      waitForIdle(retryable, 0);
    } else {
      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() {
        postToBackgroundWithDelay(retryable, failedAttempts);
        return false;
      }
    });
  }

  private void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) {
    long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor);
    long delayMillis = initialDelayMillis * exponentialBackoffFactor;
    backgroundHandler.postDelayed(new Runnable() {
      @Override public void run() {
        Retryable.Result result = retryable.run();
        if (result == RETRY) {
          postWaitForIdle(retryable, failedAttempts + 1);
        }
      }
    }, delayMillis);
  }

}

3. AndroidRefWatcherBuilder

AndroidRefWatcherBuilder才是LeakCanaryAndroid系统中真正执行者。

为了更好滴了解 AndroidRefWatcherBuilder ,我们可以从实际出发,根据我们在Android的使用来探索其精髓。


3.1 LeakCanary在Android上的使用

LeakCanary在Android开发中,我们一般都是使用下面的代码进行初始化的。

public class MainApp extends Application {
  @Override public void onCreate() {
    super.onCreate();
    if (LeakCanary.isInAnalyzerProcess(this)) {
      // This process is dedicated to LeakCanary for heap analysis.
      // You should not init your app in this process.
      return;
    }
    LeakCanary.install(this);
    // Normal app init code...
  }
}

我相信绝大多数地Android开发者对于上述代码都不会陌生。

3.2 走进LeakCanary
  1. install()

创建一个AndroidRefWatcherBuilder对象并使其开始工作(过滤一些Android不必要的分析内容)
PS : AndroidExcludedRefs 是个枚举,并不是ExcludedRefs的子类

 public static @NonNull RefWatcher install(@NonNull Application application) {
    return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
        .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
        .buildAndInstall();
  }
  1. isInAnalyzerProcess()

作用:判断监测内存泄漏的进程是否已经创建,从而避免重复创建。
实现:本质上 调用的是 LeakCanaryInternalsisInServiceProcess()方法。感兴趣的可以深入了解一下 LeakCanaryInternals 这个类。

这里判断的是HeapAnalyzerService,也证明了DisplayLeakService 并不是一直存在的。

public static boolean isInAnalyzerProcess(@NonNull Context context) {
    Boolean isInAnalyzerProcess = LeakCanaryInternals.isInAnalyzerProcess;
    // This only needs to be computed once per process.
    if (isInAnalyzerProcess == null) {
      isInAnalyzerProcess = isInServiceProcess(context, HeapAnalyzerService.class);
      LeakCanaryInternals.isInAnalyzerProcess = isInAnalyzerProcess;
    }
    return isInAnalyzerProcess;
  }
3.3 AndroidRefWatcherBuilder真面目

isAssignableFrom() 是从类继承的角度判断从属关系的,调用者与参数都必须是C lass对象。

用法:父类.class.isAssignableFrom(子类.class)
返回值:false —— 调用者是参数的子类;否则返回true;
具体可以参考:java中isAssignableFrom()方法

public final class AndroidRefWatcherBuilder extends RefWatcherBuilder<AndroidRefWatcherBuilder> {
// 注意是默认观察的延时时间 5s
private static final long DEFAULT_WATCH_DELAY_MILLIS = SECONDS.toMillis(5);

  private final Context context;
  private boolean watchActivities = true;
  private boolean watchFragments = true;
  // 这里默认是false,意不意外?
  private boolean enableDisplayLeakActivity = false;

 // 这里是涉及分析堆内存信息的服务
 public @NonNull AndroidRefWatcherBuilder listenerServiceClass(
      @NonNull Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
     // 注意这里:isAssignableFrom()方法
     // 只要传入的参数是DisplayLeakService或者其子类,就返回true.
    enableDisplayLeakActivity = DisplayLeakService.class.isAssignableFrom(listenerServiceClass);
    return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
  }
	
 // 真正的监听操作在这里
 public @NonNull RefWatcher buildAndInstall() {
    if (LeakCanaryInternals.installedRefWatcher != null) {
      throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
    }
    RefWatcher refWatcher = build();
    if (refWatcher != DISABLED) {
       // 是否要显示 DisplayLeakActivity ,为我们展示泄漏情况
      if (enableDisplayLeakActivity) {
        LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
      }
      // 观察Activity的泄漏情况
      if (watchActivities) {
        ActivityRefWatcher.install(context, refWatcher);
      }
      // 观察Fragment的泄漏情况
      if (watchFragments) {
        FragmentRefWatcher.Helper.install(context, refWatcher);
      }
    }
    LeakCanaryInternals.installedRefWatcher = refWatcher;
    return refWatcher;
  }
	
	...
	
}

相信你已经注意到ActivityRefWatcherFragmentRefWatcher

简要看一下:

  1. ActivityRefWatcher:在Activity销毁的时候进行监听
 private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
      new ActivityLifecycleCallbacksAdapter() {
        @Override public void onActivityDestroyed(Activity activity) {
          refWatcher.watch(activity);
        }
      };
  1. FragmentRefWatcher是接口,其子类:
    1. SupportFragmentRefWatcher
    2. AndroidOFragmentRefWatcher (SDK >= 26)
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);
        }
      };

4. 总结

LeakCanary 充分利用了Android系统的特点来达到监测并分析内存泄漏的目的。这里需要我们 重点回味的技巧:

  1. 使用IdleHandler来监测;
  2. 使用服务HeapAnalyzerService进行堆内存分析,若没有返回分析结果,则不会启动DisplayLeakService。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章