带你学开源项目:LeakCanary-如何检测Activity是否泄漏

OOM 是 Android 开发中常见的问题,而内存泄漏往往是罪魁祸首。

创新互联制作网站网页找三站合一网站制作公司,专注于网页设计,网站制作、成都网站设计,网站设计,企业网站搭建,网站开发,建网站业务,680元做网站,已为上千多家服务,创新互联网站建设将一如既往的为我们的客户提供最优质的网站建设、网络营销推广服务!

为了简单方便的检测内存泄漏,Square 开源了 LeakCanary ,它可以实时监测 Activity 是否发生了泄漏,一旦发现就会自动弹出提示及相关的泄漏信息供分析。

本文的目的是试图通过分析 LeakCanary 源码来探讨它的 Activity 泄漏检测机制。

LeakCanary 使用方式

为了将 LeakCanary 引入到我们的项目里,我们只需要做以下两步:

 
 
 
 
  1. dependencies {
  2.  debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1'
  3.  releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
  4.  testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'}public class ExampleApplication extends Application {  @Override public void onCreate() {    super.onCreate();    if (LeakCanary.isInAnalyzerProcess(this)) {      // This process is dedicated to LeakCanary for heap analysis.
  5.       // You should not init your app in this process.
  6.       return;
  7.     }
  8.     LeakCanary.install(this);
  9.   }
  10. }

 可以看出,最关键的就是 LeakCanary.install(this); 这么一句话,正式开启了 LeakCanary 的大门,未来它就会自动帮我们检测内存泄漏,并在发生泄漏是弹出通知信息。

从 LeakCanary.install(this); 开始

下面我们来看下它做了些什么?

 
 
 
 
  1. public static RefWatcher install(Application application) {  return install(application, DisplayLeakService.class,
  2.       AndroidExcludedRefs.createAppDefaults().build());
  3. }public static RefWatcher install(Application application,
  4.     Class listenerServiceClass,
  5.     ExcludedRefs excludedRefs) {  if (isInAnalyzerProcess(application)) {    return RefWatcher.DISABLED;
  6.   }
  7.   enableDisplayLeakActivity(application);
  8.   HeapDump.Listener heapDumpListener =      new ServiceHeapDumpListener(application, listenerServiceClass);
  9.   RefWatcher refWatcher = androidWatcher(application, heapDumpListener, excludedRefs);
  10.   ActivityRefWatcher.installOnIcsPlus(application, refWatcher);  return refWatcher;

首先,我们先看最重要的部分,就是:

 
 
 
 
  1. RefWatcher refWatcher = androidWatcher(application, heapDumpListener, excludedRefs);
  2. ActivityRefWatcher.installOnIcsPlus(application, refWatcher); 

先生成了一个 RefWatcher ,这个东西非常关键,从名字可以看出,它是用来 watch Reference 的,也就是用来一个监控引用的工具。然后再把 refWatcher 和我们自己提供的 application 传入到 ActivityRefWatcher.installOnIcsPlus(application, refWatcher); 这句里面,继续看。

 
 
 
 
  1. public static void installOnIcsPlus(Application application, RefWatcher refWatcher) {
  2.     ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
  3.     activityRefWatcher.watchActivities();

创建了一个 ActivityRefWatcher ,大家应该能感受到,这个东西就是用来监控我们的 Activity 泄漏状况的,它调用 watchActivities() 方法,就可以开始进行监控了。下面就是它监控的核心原理:

 
 
 
 
  1. public void watchActivities() {
  2.   application.registerActivityLifecycleCallbacks(lifecycleCallbacks);

它向 application 里注册了一个 ActivitylifecycleCallbacks 的回调函数,可以用来监听 Application 整个生命周期所有 Activity 的 lifecycle 事件。再看下这个 lifecycleCallbacks 是什么?

 
 
 
 
  1. private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =    new Application.ActivityLifecycleCallbacks() {      @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
  2.       }      @Override public void onActivityStarted(Activity activity) {
  3.       }      @Override public void onActivityResumed(Activity activity) {
  4.       }      @Override public void onActivityPaused(Activity activity) {
  5.       }      @Override public void onActivityStopped(Activity activity) {
  6.       }      @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
  7.       }      @Override public void onActivityDestroyed(Activity activity) {
  8.         ActivityRefWatcher.this.onActivityDestroyed(activity);
  9.       }
  10.     };

 原来它只监听了所有 Activity 的 onActivityDestroyed 事件,当 Activity 被 Destory 时,调用 ActivityRefWatcher.this.onActivityDestroyed(activity); 函数。

猜测下,正常情况下,当一个这个函数应该 activity 被 Destory 时,那这个 activity 对象应该变成 null 才是正确的。如果没有变成null,那么就意味着发生了内存泄漏。

因此我们向,这个函数 ActivityRefWatcher.this.onActivityDestroyed(activity); 应该是用来监听 activity 对象是否变成了 null。继续看。

 
 
 
 
  1. void onActivityDestroyed(Activity activity) {
  2.   refWatcher.watch(activity);
  3. }
  4. RefWatcher refWatcher = androidWatcher(application, heapDumpListener, excludedRefs); 

可以看出,这个函数把目标 activity 对象传给了 RefWatcher ,让它去监控这个 activity 是否被正常回收了,若未被回收,则意味着发生了内存泄漏。

RefWatcher 如何监控 activity 是否被正常回收呢?

我们先来看看这个 RefWatcher 究竟是个什么东西?

 
 
 
 
  1. public static RefWatcher androidWatcher(Context context, HeapDump.Listener heapDumpListener,
  2.     ExcludedRefs excludedRefs) {
  3.   AndroidHeapDumper heapDumper = new AndroidHeapDumper(context, leakDirectoryProvider);
  4.   heapDumper.cleanup();  int watchDelayMillis = 5000;
  5.   AndroidWatchExecutor executor = new AndroidWatchExecutor(watchDelayMillis);  return new RefWatcher(executor, debuggerControl, GcTrigger.DEFAULT, heapDumper,
  6.       heapDumpListener, excludedRefs);

这里面涉及到两个新的对象: AndroidHeapDumper 和 AndroidWatchExecutor ,前者用来 dump 堆内存状态的,后者则是用来 watch 一个引用的监听器。具体原理后面再看。总之,这里已经生成好了一个 RefWatcher 对象了。

现在再看上面 onActivityDestroyed(Activity activity) 里调用的 refWatcher.watch(activity); ,下面来看下这个最为核心的 watch(activity) 方法,了解它是如何监控 activity 是否被回收的。

 
 
 
 
  1. private final Set retainedKeys;public void watch(Object activity, String referenceName) {
  2.   String key = UUID.randomUUID().toString();
  3.   retainedKeys.add(key);  final KeyedWeakReference reference =      new KeyedWeakReference(activity, key, referenceName, queue);
  4.   watchExecutor.execute(new Runnable() {    @Override public void run() {
  5.       ensureGone(reference, watchStartNanoTime);
  6.     }
  7.   });
  8. }final class KeyedWeakReference extends WeakReference {  public final String key;  public final String name;
  9. 可以看到,它首先把我们传入的 activity 包装成了一个 KeyedWeakReference (可以暂时看成一个普通的 WeakReference),然后 watchExecutor 会去执行一个 Runnable,这个 Runnable 会调用 ensureGone(reference, watchStartNanoTime) 函数。

    看这个函数之前猜测下,我们知道 watch 函数本身就是用来监听 activity 是否被正常回收,这就涉及到两个问题:

    1. 何时去检查它是否回收?
    2. 如何有效地检查它真的被回收?

    所以我们觉得 ensureGone 函数本身要做的事正如它的名字,就是确保 reference 被回收掉了,否则就意味着内存泄漏。

    核心函数:ensureGone(reference) 检测回收

    下面来看这个函数实现:

     
     
     
     
    1. void ensureGone(KeyedWeakReference reference, long watchStartNanoTime) {
    2.   removeWeaklyReachableReferences();  if (gone(reference) || debuggerControl.isDebuggerAttached()) {    return;
    3.   }
    4.   gcTrigger.runGc();
    5.   removeWeaklyReachableReferences();  if (!gone(reference)) {
    6.     File heapDumpFile = heapDumper.dumpHeap();
    7.     heapdumpListener.analyze(        new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
    8.             gcDurationMs, heapDumpDurationMs));
    9.   }
    10. }private boolean gone(KeyedWeakReference reference) {  return !retainedKeys.contains(reference.key);
    11. }private void removeWeaklyReachableReferences() {
    12.   KeyedWeakReference ref;  while ((ref = (KeyedWeakReference) queue.poll()) != null) {
    13.     retainedKeys.remove(ref.key);
    14.   }

    这里先来解释下 WeakReference 和 ReferenceQueue 的工作原理。

         1.弱引用 WeakReference

     被强引用的对象就算发生 OOM 也永远不会被垃圾回收机回收;被弱引用的对象,只要被垃圾回收器发现就会立即被回收;被软引用的对象,具备内存敏感性,只有内存不足时才会被回收,常用来做内存敏感缓存器;虚引用则任意时刻都可能被回收,使用较少。

         2.引用队列 ReferenceQueue

    我们常用一个 WeakReference reference = new WeakReference(activity); ,这里我们创建了一个 reference 来弱引用到某个 activity ,当这个 activity 被垃圾回收器回收后,这个 reference 会被放入内部的 ReferenceQueue 中。也就是说,从队列 ReferenceQueue 取出来的所有 reference ,它们指向的真实对象都已经成功被回收了。

    然后再回到上面的代码。

    在一个 activity 传给 RefWatcher 时会创建一个***的 key 对应这个 activity,该key存入一个集合 retainedKeys 中。也就是说,所有我们想要观测的 activity 对应的*** key 都会被放入 retainedKeys 集合中。

    基于我们对 ReferenceQueue 的了解,只要把队列中所有的 reference 取出来,并把对应 retainedKeys 里的key移除,剩下的 key 对应的对象都没有被回收。

    1. ensureGone 首先调用 removeWeaklyReachableReferences 把已被回收的对象的 key 从 retainedKeys 移除,剩下的 key 都是未被回收的对象;
    2. if

      本文标题:带你学开源项目:LeakCanary-如何检测Activity是否泄漏
      当前链接:http://www.shufengxianlan.com/qtweb/news13/515013.html

      网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

      广告

      声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联