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

前一篇文章(LeakCanary 源码解析笔记整理(一))整理了有关 LeakCanary 检测 Activity 是否泄漏的相关源码以及原理的笔记。

然后这里再 继续对该库前期的实现 进行相关的笔记补充。且主要参考自看完这篇 LeakCanary 原理分析,又可以虐面试官了!


上一篇有说过,能够监测 Activity 是否泄漏的关键是:

private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
    new ActivityLifecycleCallbacksAdapter() {
      @Override public void onActivityDestroyed(Activity activity) {
     	// 检测内存泄漏的关键,实际上,该方法可以检测所有对象是否内存泄漏,而不仅仅是 activity
        refWatcher.watch(activity);
      }
    };

而实际上,不仅是监测 Activity,任何想要监测的对象,都是利用 refWatcher.watch(obj) 来实现的。

可以在自定义的 Application 中得到 RefWatcher 对象,然后通过该对象来实现监测某一对象的内存泄漏。(上一篇文章有提到 LeakCanary 库在 Android 4.0 之前是没法直接使用的,因此在 Android 4.0 之前需要手动在 Activity 的 onDestory() 方法中实现添加监听)

public class MyApplication extends Application {
    private RefWatcher refWatcher;
    @Override
    public void onCreate() {
        super.onCreate();
        refWatcher = setupLeakCanary();
    }
    private RefWatcher setupLeakCanary() {
        if (LeakCanary.isInAnalyzerProcess(this)) {
            return RefWatcher.DISABLED;
        }
        return LeakCanary.install(this);
    }
    public static RefWatcher getRefWatcher(Context context) {
        MyApplication leakApplication = (MyApplication) context.getApplicationContext();
        return leakApplication.refWatcher;
    }
}

在合适的时机调用即可。(合适的时机指的是某对象在程序中无需再使用了,应该被回收的时候)

MyApplication.getRefWatcher(context).watch(obj)

然后是对于 LeakCanary.install() 方法,也需要理一遍。

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

总共涉及了四个方法:

(1)refWatcher(application)

// LeakCanary.java
public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {
  return new AndroidRefWatcherBuilder(context);
}

// AndroidRefWatcherBuilder.java
public final class AndroidRefWatcherBuilder extends RefWatcherBuilder<AndroidRefWatcherBuilder> {
  private static final long DEFAULT_WATCH_DELAY_MILLIS = SECONDS.toMillis(5);
  private final Context context;
  private boolean watchActivities = true;
  private boolean watchFragments = true;
  private boolean enableDisplayLeakActivity = false;
  AndroidRefWatcherBuilder(@NonNull Context context) {
    // 仅仅是将传入的 Context 保存起来
    this.context = context.getApplicationContext();
  }

  ...
}

refWatcher(application) 会得到一个 AndroidRefWatcherBuilder 对象,显而易见,这里利用构建者模式对 RefWatcher 进行配置。AndroidRefWatcherBuilder 继承自 RefWatcherBuilder

public class RefWatcherBuilder<T extends RefWatcherBuilder<T>> {
  ...
  public RefWatcherBuilder() {
    // 新建了一个 HeapDump 的构造器对象。其中 HeapDump 就是一个保存 heap dump 信息的数据结构
    heapDumpBuilder = new HeapDump.Builder();
  }
}

(2)AndroidRefWatcherBuilder#listenerServiceClass()

public @NonNull AndroidRefWatcherBuilder listenerServiceClass(
    @NonNull Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
  enableDisplayLeakActivity = DisplayLeakService.class.isAssignableFrom(listenerServiceClass);
  return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
}

这里在第一篇笔记的后面有说过,目的是传入了一个 DisplayLeakService 的 Class 对象,此时会将 enableDisplayLeakActivity 置为 true(默认为 false,该属性用于控制是否能够展示用于显示泄露的界面 DisplayLeakActivity)。

然后利用 heapDumpListener() 传递新建的 ServiceHeapDumpListener 对象并赋值给 heapDumpListener 成员变量,而 heapDumpListener 在第一篇里面有说(其作用就是分析 heapDump 对象)。

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

(3)AndroidRefWatcherBuilder#excludedRefs(AndroidExcludedRefs.createAppDefaults().build())

// AndroidExcludedRefs.java
public static @NonNull ExcludedRefs.Builder createAppDefaults() {
  return createBuilder(EnumSet.allOf(AndroidExcludedRefs.class));
}
public static @NonNull ExcludedRefs.Builder createBuilder(EnumSet<AndroidExcludedRefs> refs) {
  ExcludedRefs.Builder excluded = ExcludedRefs.builder();
  for (AndroidExcludedRefs ref : refs) {
    if (ref.applies) {
      ref.add(excluded);
      ((ExcludedRefs.BuilderWithParams) excluded).named(ref.name());
    }
  }
  return excluded;
}

AndroidExcludedRefs 这个类,它是一个 enum 类,它声明了 Android SDK 和厂商定制的 SDK 中存在的内存泄露的 case,根据 AndroidExcludedRefs 这个类的类名就可看出这些 case 都会被 Leakcanary 的监测过滤掉。目前这个版本是有 46 种这样的 case 被包含在内,后续可能会一直增加。然后 EnumSet.allOf(AndroidExcludedRefs.class) 这个方法将会返回一个包含AndroidExcludedRefs元素类型的 EnumSetEnum 是一个抽象类,在这里具体的实现类是通用正规型的 RegularEnumSet,如果Enum里面的元素个数大于64,则会使用存储大数据量的JumboEnumSet。最后,在 createBuilder() 这个方法里面构建了一个排除引用的建造器 excluded,将各式各样的 case 分门别类地保存起来再返回出去。

(4)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) {
    if (enableDisplayLeakActivity) {
      // setEnabledAsync() 使用 AsyncTask 内部自带的 THREAD_POOL_EXECUTOR 线程池
      // 进行异步阻塞式地显示 DisplayLeakActivity
      LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
    }
    // 为 Application 注册 ActivityLifecycleCallbacks,这里就可以呼应文章开头说的那个关键之处
    // 同时也呼应第一篇文章的开头
    if (watchActivities) {
      ActivityRefWatcher.install(context, refWatcher);
    }
    // 这里会尝试对标准的 fragment 进行监听
    // 详细的说明见后面的内容
    if (watchFragments) {
      FragmentRefWatcher.Helper.install(context, refWatcher);
    }
  }
  LeakCanaryInternals.installedRefWatcher = refWatcher;
  return refWatcher;
}

// 建立 RefWatcher 实例并返回
public final RefWatcher build() {
  if (isDisabled()) {
    return RefWatcher.DISABLED;
  }
  // excludedRefs : 记录可以被忽略的泄漏路径
  if (heapDumpBuilder.excludedRefs == null) {
    heapDumpBuilder.excludedRefs(defaultExcludedRefs());
  }
  // heapDumpListener : 转储堆信息到hprof文件,并在解析完 hprof 文件后进行回调,
  // 	最后通知 DisplayLeakService 弹出泄漏提醒
  HeapDump.Listener heapDumpListener = this.heapDumpListener;
  if (heapDumpListener == null) {
    heapDumpListener = defaultHeapDumpListener();
  }
  // debuggerControl : 判断是否处于调试模式,调试模式中不会进行内存泄漏检测。
  //	因为在调试过程中可能会保留上一个引用从而导致错误信息上报
  DebuggerControl debuggerControl = this.debuggerControl;
  if (debuggerControl == null) {
    debuggerControl = defaultDebuggerControl();
  }
  // heapDumper : 堆信息转储者,dump 内存泄漏处的 heap 信息到 hprof 文件
  HeapDumper heapDumper = this.heapDumper;
  if (heapDumper == null) {
    heapDumper = defaultHeapDumper();
  }
  // watchExecutor : 用于在 Activity onDestroy() 之后并且主线程空闲时执行内存泄漏检测。
  // 	就是上一篇说过的利用 IdleHandler
  WatchExecutor watchExecutor = this.watchExecutor;
  if (watchExecutor == null) {
    watchExecutor = defaultWatchExecutor();
  }
  // gcTrigger : 用于 GC
  GcTrigger gcTrigger = this.gcTrigger;
  if (gcTrigger == null) {
    gcTrigger = defaultGcTrigger();
  }
  // reachabilityInspectorClasses : 用于要进行可达性检测的类列表
  if (heapDumpBuilder.reachabilityInspectorClasses == null) {
    heapDumpBuilder.reachabilityInspectorClasses(defaultReachabilityInspectorClasses());
  }
  return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener,
      heapDumpBuilder);
}
// LeakCanaryInternals.setEnabledAsync()
public static void setEnabledAsync(Context context, final Class<?> componentClass,
    final boolean enabled) {
  final Context appContext = context.getApplicationContext();
  AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
    @Override public void run() {
      setEnabledBlocking(appContext, componentClass, enabled);
    }
  });
}

这里需要详细说一下 FragmentRefWatcher.Helper.install(context, refWatcher) 的实现。

public static void install(Context context, RefWatcher refWatcher) {
  List<FragmentRefWatcher> fragmentRefWatchers = new ArrayList<>();
  // 如果为 Android O 则添加一个特殊的 AndroidOFragmentRefWatcher
  if (SDK_INT >= O) {
    fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher));
  }
  // 利用反射机制尝试添加 SupportFragmentRefWatcher
  // 利用反射是因为如果要监测 android.support.v4.app.Fragment
  // 还需要依赖额外的库 com.squareup.leakcanary:leakcanary-support-fragment
  try {
    Class<?> fragmentRefWatcherClass = Class.forName(SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME);
    Constructor<?> constructor =
        fragmentRefWatcherClass.getDeclaredConstructor(RefWatcher.class);
    FragmentRefWatcher supportFragmentRefWatcher =
        (FragmentRefWatcher) constructor.newInstance(refWatcher);
    fragmentRefWatchers.add(supportFragmentRefWatcher);
  } catch (Exception ignored) {
  }
  if (fragmentRefWatchers.size() == 0) {
    return;
  }
  Helper helper = new Helper(fragmentRefWatchers);
  Application application = (Application) context.getApplicationContext();
  // 利用 Application 再添加一个 ActivityLifecycleCallbacks
  application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks);
}

private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks =
    new ActivityLifecycleCallbacksAdapter() {
      @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        // 当 Activity Created 的时候,利用该 Activity 以及前面添加的多种类型的 
        // FragmentRefWatcher 实现对 Fragment 的监测
        for (FragmentRefWatcher watcher : fragmentRefWatchers) {
          // 具体实现即通过调用 FragmentRefWatcher#watchFragments(Activity)
          watcher.watchFragments(activity);
        }
      }
    };

先看 AndroidOFragmentRefWatcher 类型的

@RequiresApi(Build.VERSION_CODES.O) 
class AndroidOFragmentRefWatcher implements FragmentRefWatcher {
  private final RefWatcher refWatcher;
  AndroidOFragmentRefWatcher(RefWatcher refWatcher) {
    this.refWatcher = refWatcher;
  }
  private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
      new FragmentManager.FragmentLifecycleCallbacks() {
        @Override public void onFragmentViewDestroyed(FragmentManager fm, android.app.Fragment fragment) {
          View view = fragment.getView();
          if (view != null) {
            refWatcher.watch(view);
          }
        }
        @Override
        public void onFragmentDestroyed(FragmentManager fm, android.app.Fragment fragment) {
          refWatcher.watch(fragment);
        }
      };
      
  @Override public void watchFragments(Activity activity) {
    FragmentManager fragmentManager = activity.getFragmentManager();
    fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
  }
}

关键是在 AndroidOFragmentRefWatcher#watchFragments(Activity),会利用 activity 获取到对应的 FragmentManager 并利用其 registerFragmentLifecycleCallbacks() 方法实现对 android.app.Fragment 生命周期的监听,从而实现对 Fragment 和其 view 的内存泄漏的监测。

然后是 SupportFragmentRefWatcher 类型的。

class SupportFragmentRefWatcher implements FragmentRefWatcher {
  private final RefWatcher refWatcher;
  SupportFragmentRefWatcher(RefWatcher refWatcher) {
    this.refWatcher = refWatcher;
  }
  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);
        }
      };
      
  @Override public void watchFragments(Activity activity) {
    if (activity instanceof FragmentActivity) {
      FragmentManager supportFragmentManager =
          ((FragmentActivity) activity).getSupportFragmentManager();
      supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
    }
  }
}

而对于 SupportFragmentRefWatcher,其实现与 AndroidOFragmentRefWatcher 类似 ,只不过是针对 android.support.v4.app.Fragment 的。

总的来说,这里的 watcher 可能有两种,但不管是哪一种,都会使用当前传入的 activity 获取到对应的 FragmentManager/SupportFragmentManager 对象,调用它的registerFragmentLifecycleCallbacks()方法,在对应的onDestroyView()onDestoryed()方法执行完后,分别使用refWatcher.watch(view)refWatcher.watch(fragment)进行内存泄漏的检测。


总结,该框架的实现主要分为如下 7 个步骤:

1、RefWatcher.watch()创建了一个KeyedWeakReference用于去观察对象。
2、然后,在后台线程中,它会检测引用是否被清除了,并且有触发GC。
3、如果引用仍然没有被清除,那么它将会把堆栈信息保存在文件系统中的.hprof文件里。
4、HeapAnalyzerService被开启在一个独立的进程中,并且HeapAnalyzer使用了HAHA开源库解析了指定时刻的堆栈快照文件heap
dump。
5、从heap dump中,HeapAnalyzer根据一个独特的引用key找到了KeyedWeakReference,并且定位了泄露的引用。
6、HeapAnalyzer为了确定是否有泄露,计算了到GC Roots的最短强引用路径,然后建立了导致泄露的链式引用。
7、这个结果被传回到app进程中的DisplayLeakService,然后一个泄露通知便展现出来了。

简单来说就是:

在一个Activity执行完onDestroy()之后,将它放入WeakReference中,然后将这个WeakReference类型的Activity对象与ReferenceQueque关联。这时再从ReferenceQueque中查看是否有该对象,如果没有,执行gc,再次查看,还是没有的话则判断发生内存泄露了。最后用HAHA这个开源库去分析dump之后的heap内存(主要就是创建一个HprofParser解析器去解析出对应的引用内存快照文件snapshot)。

摘抄自:https://github.com/JsonChao/Awesome-Android-Interview/blob/master/Android%E7%9B%B8%E5%85%B3/Android%E9%AB%98%E7%BA%A7%E9%9D%A2%E8%AF%95%E9%A2%98.md#%E8%BF%99%E4%B8%AA%E5%BA%93%E7%9A%84%E6%A0%B8%E5%BF%83%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86%E6%98%AF%E4%BB%80%E4%B9%88%E5%A6%82%E6%9E%9C%E8%AE%A9%E4%BD%A0%E5%AE%9E%E7%8E%B0%E8%BF%99%E4%B8%AA%E5%BA%93%E7%9A%84%E6%9F%90%E4%BA%9B%E6%A0%B8%E5%BF%83%E5%8A%9F%E8%83%BD%E4%BD%A0%E4%BC%9A%E8%80%83%E8%99%91%E6%80%8E%E4%B9%88%E5%8E%BB%E5%AE%9E%E7%8E%B0-3

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值