LeakCanary源码分析

使用LeakCanary 只需要在Application中调用:

LeakCanary.install(this);

那么就从这里作为入口,进去看个究竟。

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

refWatcher()将application 传进去返回AndroidRefWatcherBuilder,listenerServiceClass是设置泄漏发送显示泄漏情况的通知栏的Service不用关注。excludedRefs()设置泄漏白名单,在enum AndroidExcludedRefs 已经枚举了一些系统的bug,这里AndroidExcludedRefs.createAppDefaults()会将所有预置的枚举都添加进去,我们也可以根据需要自己添加不想被报内存泄漏的对象。

public interface Builder {
    BuilderWithParams instanceField(String className, String fieldName);

    BuilderWithParams staticField(String className, String fieldName);

    BuilderWithParams thread(String threadName);

    BuilderWithParams clazz(String className);

    ExcludedRefs build();
  }

可以看到,可以设置白名单的可以是成员对象、静态对象、线程、某个类。

那么我们要自己添加白名单的话,就可以把Application中LeakCanary.install(this)方法改为:

private void initLeakCanary(){
        ExcludedRefs.Builder appDefaults = AndroidExcludedRefs.createAppDefaults();
        //比如添加自定义线程到白名单,其他的类似
        appDefaults.thread("test thread");

        RefWatcher refWatcher = LeakCanary.refWatcher(getApplication()).listenerServiceClass(DisplayLeakService.class)
                .excludedRefs(appDefaults.build())
                .buildAndInstall();
    }

回到前面,继续看buildAndInstall():

public RefWatcher buildAndInstall() {
    RefWatcher refWatcher = build();
    if (refWatcher != DISABLED) {
      LeakCanary.enableDisplayLeakActivity(context);
      ActivityRefWatcher.installOnIcsPlus((Application) context, refWatcher);
    }
    return refWatcher;
  }

可以看到这里就是生成RefWatcher然后,ActivityRefWatcher.installOnIcsPlus((Application) context, refWatcher);
里面其实是application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
然后在lifecycleCallbacks的onActivityDestroyed中将activity传递给ActivityRefWatcher

@Override 
public void onActivityDestroyed(Activity activity) {
	ActivityRefWatcher.this.onActivityDestroyed(activity);
}

ActivityRefWatcher#onActivityDestroyed()中就其实就是用RefWatcher监控activity

void onActivityDestroyed(Activity activity) {
    refWatcher.watch(activity);
}

那接着就看watch()是怎么监控activity内存泄漏的。

public void watch(Object watchedReference, String referenceName) {
    if (this == DISABLED) {
      return;
    }
    checkNotNull(watchedReference, "watchedReference");
    checkNotNull(referenceName, "referenceName");
    final long watchStartNanoTime = System.nanoTime();
    String key = UUID.randomUUID().toString();
    retainedKeys.add(key);
    final KeyedWeakReference reference =
        new KeyedWeakReference(watchedReference, key, referenceName, queue);

    ensureGoneAsync(watchStartNanoTime, reference);
  }

现生成以个UUID来作为这个监控对象(也就是activity)的key,添加到retainedKeys中去,这个retainedKeys是个CopyOnWriteArraySet。然后把这个监控对象还有一个队列生成一个KeyedWeakReference,这个是弱引用,当这个监控对象被回收的时候,这个对象就会被添加到这个队列里。
然后ensureGoneAsync(watchStartNanoTime, reference);

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

这个watchExecutor是AndroidWatchExecutor,会把要执行的runable都post到主线程去执行,并且是在主线程handler空闲时执行,

Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
      @Override public boolean queueIdle() {
        postToBackgroundWithDelay(retryable, failedAttempts);
        return false;
      }
    });

回过头来看ensureGone()方法

 Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
    long gcStartNanoTime = System.nanoTime();
    long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);

    removeWeaklyReachableReferences();

    if (debuggerControl.isDebuggerAttached()) {
      // The debugger can create false leaks.
      return RETRY;
    }
    if (gone(reference)) {
      return DONE;
    }
    gcTrigger.runGc();
    removeWeaklyReachableReferences();
    if (!gone(reference)) {
      long startDumpHeap = System.nanoTime();
      long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);

      File heapDumpFile = heapDumper.dumpHeap();
      if (heapDumpFile == RETRY_LATER) {
        // Could not dump the heap.
        return RETRY;
      }
      long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
      heapdumpListener.analyze(
          new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
              gcDurationMs, heapDumpDurationMs));
    }
    return DONE;
  }

先调用了removeWeaklyReachableReferences();

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) {
      retainedKeys.remove(ref.key);
    }
  }

先遍历弱引用队列,然后拿到监控对象的key,去set中匹配remove。然后调用

private boolean gone(KeyedWeakReference reference) {
    return !retainedKeys.contains(reference.key);
 }

我们刚才说监控对象如果被GC了会被添加到队列里来,即如果这个activity是有被回收的,那么就会被添加到队列里,然后就可以成功remove,再后面判断gone的时候,如果retainedKeys里已经没有这个activity的key了那么就是gone了,即已经被回收没有内存泄漏。
如果没有gone,那么gcTrigger.runGc();

GcTrigger DEFAULT = new GcTrigger() {
    @Override public void runGc() {
      // Code taken from AOSP FinalizationTest:
      // https://android.googlesource.com/platform/libcore/+/master/support/src/test/java/libcore/
      // java/lang/ref/FinalizationTester.java
      // System.gc() does not garbage collect every time. Runtime.gc() is
      // more likely to perfom a gc.
      Runtime.getRuntime().gc();
      enqueueReferences();
      System.runFinalization();
    }

    private void enqueueReferences() {
      // Hack. We don't have a programmatic way to wait for the reference queue daemon to move
      // references to the appropriate queues.
      try {
        Thread.sleep(100);
      } catch (InterruptedException e) {
        throw new AssertionError();
      }
    }
  };

手动触发GC, 等待100毫秒后继续执行removeWeaklyReachableReferences(),就是看GC过后这个activity有没有成功被回收然后放入到queue里,如果这时候被回收了那就是没有泄露,如果还没有被回收就判定发生了内存泄漏,然后调用square的haha 库dump内存信息 去分析引用连。

你可能感兴趣的:(安卓笔记,android)