Android:主流三方库源码教你快速上手Leakcanary,斗鱼直播Android开发二面被刷
2022/1/17 17:07:26
本文主要是介绍Android:主流三方库源码教你快速上手Leakcanary,斗鱼直播Android开发二面被刷,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
首先在你项目app下的build.gradle中配置:
dependencies {
debugImplementation ‘com.squareup.leakcanary:leakcanary-android:1.6.2’
releaseImplementation ‘com.squareup.leakcanary:leakcanary-android-no-op:1.6.2’
// 可选,如果你使用支持库的fragments的话
debugImplementation ‘com.squareup.leakcanary:leakcanary-support-fragment:1.6.2’
}
复制代码
然后在你的Application中配置:
public class WanAndroidApp extends Application {
private RefWatcher refWatcher;
public static RefWatcher getRefWatcher(Context context) {
WanAndroidApp application = (WanAndroidApp) context.getApplicationContext();
return application.refWatcher;
}
@Override public void onCreate() {
super.onCreate();
if (LeakCanary.isInAnalyzerProcess(this)) {
// 1
return;
}
// 2
refWatcher = LeakCanary.install(this);
}
}
复制代码
在注释1处,会首先判断当前进程是否是Leakcanary专门用于分析heap内存的而创建的那个进程,即HeapAnalyzerService所在的进程,如果是的话,则不进行Application中的初始化功能。如果是当前应用所处的主进程的话,则会执行注释2处的LeakCanary.install(this)进行LeakCanary的安装。只需这样简单的几行代码,我们就可以在应用中检测是否产生了内存泄露了。当然,这样使用只会检测Activity和标准Fragment是否发生内存泄漏,如果要检测V4包的Fragment在执行完onDestroy()之后是否发生内存泄露的话,则需要在Fragment的onDestroy()方法中加上如下两行代码去监视当前的Fragment:
RefWatcher refWatcher = WanAndroidApp.getRefWatcher(_mActivity);
refWatcher.watch(this);
复制代码
上面的RefWatcher其实就是一个引用观察者对象,是用于监测当前实例对象的引用状态的。从以上的分析可以了解到,核心代码就是LeakCanary.install(this)这行代码,接下来,就从这里出发将LeakCanary一步一步进行拆解。
三、源码分析
=================================================================
1、LeakCanary#install()
public static @NonNull RefWatcher install(@NonNull Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
复制代码
在install()方法中的处理,可以分解为如下四步:
-
1、refWatcher(application)
-
2、链式调用listenerServiceClass(DisplayLeakService.class)
-
3、链式调用excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
-
4、链式调用buildAndInstall()
首先,我们来看下第一步,这里调用了LeakCanary类的refWatcher方法,如下所示:
public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {
return new AndroidRefWatcherBuilder(context);
}
复制代码
然后新建了一个AndroidRefWatcherBuilder对象,再看看AndroidRefWatcherBuilder这个类。
2、AndroidRefWatcherBuilder
/** A {@link RefWatcherBuilder} with appropriate Android defaults. */
public final class AndroidRefWatcherBuilder extends RefWatcherBuilder {
…
AndroidRefWatcherBuilder(@NonNull Context context) {
this.context = context.getApplicationContext();
}
…
}
复制代码
在AndroidRefWatcherBuilder的构造方法中仅仅是将外部传入的applicationContext对象保存起来了。AndroidRefWatcherBuilder是一个适配Android平台的引用观察者构造器对象,它继承了RefWatcherBuilder,RefWatcherBuilder是一个负责建立引用观察者RefWatcher实例的基类构造器。继续看看RefWatcherBuilder这个类。
3、RefWatcherBuilder
public class RefWatcherBuilder<T extends RefWatcherBuilder> {
…
public RefWatcherBuilder() {
heapDumpBuilder = new HeapDump.Builder();
}
…
}
复制代码
在RefWatcher的基类构造器RefWatcherBuilder的构造方法中新建了一个HeapDump的构造器对象。其中HeapDump就是一个保存heap dump信息的数据结构。
接着来分析下install()方法中的链式调用的listenerServiceClass(DisplayLeakService.class)这部分逻辑。
4、AndroidRefWatcherBuilder#listenerServiceClass()
public @NonNull AndroidRefWatcherBuilder listenerServiceClass(
@NonNull Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
}
复制代码
在这里,传入了一个DisplayLeakService的Class对象,它的作用是展示泄露分析的结果日志,然后会展示一个用于跳转到显示泄露界面DisplayLeakActivity的通知。在listenerServiceClass()这个方法中新建了一个ServiceHeapDumpListener对象,下面看看它内部的操作。
5、ServiceHeapDumpListener
public final class ServiceHeapDumpListener implements HeapDump.Listener {
…
public ServiceHeapDumpListener(@NonNull final Context context,
@NonNull final Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
this.listenerServiceClass = checkNotNull(listenerServiceClass, “listenerServiceClass”);
this.context = checkNotNull(context, “context”).getApplicationContext();
}
…
}
复制代码
可以看到这里仅仅是在ServiceHeapDumpListener中保存了DisplayLeakService的Class对象和application对象。它的作用就是接收一个heap dump去分析。
然后我们继续看install()方法链式调用.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())的这部分代码。先看AndroidExcludedRefs.createAppDefaults()。
6、AndroidExcludedRefs#createAppDefaults()
public enum AndroidExcludedRefs {
…
public static @NonNull ExcludedRefs.Builder createAppDefaults() {
return createBuilder(EnumSet.allOf(AndroidExcludedRefs.class));
}
public static @NonNull ExcludedRefs.Builder createBuilder(EnumSet 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元素类型的EnumSet。Enum是一个抽象类,在这里具体的实现类是通用正规型的RegularEnumSet,如果Enum里面的元素个数大于64,则会使用存储大数据量的JumboEnumSet。最后,在createBuilder这个方法里面构建了一个排除引用的建造器excluded,将各式各样的case分门别类地保存起来再返回出去。
最后,我们看到链式调用的最后一步buildAndInstall()。
7、AndroidRefWatcherBuilder#buildAndInstall()
private boolean watchActivities = true;
private boolean watchFragments = true;
public @NonNull RefWatcher buildAndInstall() {
// 1
if (LeakCanaryInternals.installedRefWatcher != null) {
throw new UnsupportedOperationException(“buildAndInstall() should only be called once.”);
}
// 2
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
// 3
LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
if (watchActivities) {
// 4
ActivityRefWatcher.install(context, refWatcher);
}
if (watchFragments) {
// 5
FragmentRefWatcher.Helper.install(context, refWatcher);
}
}
// 6
LeakCanaryInternals.installedRefWatcher = refWatcher;
return refWatcher;
}
复制代码
首先,在注释1处,会判断LeakCanaryInternals.installedRefWatcher是否已经被赋值,如果被赋值了,则会抛出异常,警告 buildAndInstall()这个方法应该仅仅只调用一次,在此方法结束时,即在注释6处,该LeakCanaryInternals.installedRefWatcher才会被赋值。再来看注释2处,调用了AndroidRefWatcherBuilder其基类RefWatcherBuilder的build()方法,我们它是如何建造的。
8、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(defa ultReachabilityInspectorClasses());
}
return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener,
heapDumpBuilder);
}
复制代码
可以看到,RefWatcherBuilder包含了以下7个组成部分:
-
1、excludedRefs : 记录可以被忽略的泄漏路径。
-
2、heapDumpListener : 转储堆信息到hprof文件,并在解析完 hprof 文件后进行回调,最后通知 DisplayLeakService 弹出泄漏提醒。
-
3、debuggerControl : 判断是否处于调试模式,调试模式中不会进行内存泄漏检测。为什么呢?因为在调试过程中可能会保留上一个引用从而导致错误信息上报。
-
4、heapDumper : 堆信息转储者,负责dump 内存泄漏处的 heap 信息到 hprof 文件。
-
5、watchExecutor : 线程控制器,在 onDestroy() 之后并且在主线程空闲时执行内存泄漏检测。
-
6、gcTrigger : 用于 GC,watchExecutor 首次检测到可能的内存泄漏,会主动进行 GC,GC 之后会再检测一次,仍然泄漏的判定为内存泄漏,最后根据heapDump信息生成相应的泄漏引用链。
-
7、reachabilityInspectorClasses : 用于要进行可达性检测的类列表。
最后,会使用建造者模式将这些组成部分构建成一个新的RefWatcher并将其返回。
我们继续看回到AndroidRefWatcherBuilder的注释3处的 LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true)这行代码。
9、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);
}
});
}
复制代码
在这里直接使用了AsyncTask内部自带的THREAD_POOL_EXECUTOR线程池进行阻塞式地显示DisplayLeakActivity。
然后我们再继续看AndroidRefWatcherBuilder的注释4处的代码。
10、ActivityRefWatcher#install()
public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) {
Application application = (Application) context.getApplicationContext();
// 1
ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
// 2
application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
}
复制代码
可以看到,在注释1处创建一个自己的activityRefWatcher实例,并在注释2处调用了application的registerActivityLifecycleCallbacks()方法,这样就能够监听activity对应的生命周期事件了。继续看看activityRefWatcher.lifecycleCallbacks里面的操作。
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
};
public abstract class ActivityLifecycleCallbacksAdapter
implements Application.ActivityLifecycleCallbacks {
}
复制代码
很明显,这里实现并重写了Application的ActivityLifecycleCallbacks的onActivityDestroyed()方法,这样便能在所有Activity执行完onDestroyed()方法之后调用 refWatcher.watch(activity)这行代码进行内存泄漏的检测了。
我们再看到注释5处的FragmentRefWatcher.Helper.install(context, refWatcher)这行代码,
11、FragmentRefWatcher.Helper#install()
public interface FragmentRefWatcher {
void watchFragments(Activity activity);
final class Helper {
private static final String SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME =
“com.squareup.leakcanary.internal.SupportFragmentRefWatcher”;
public static void install(Context context, RefWatcher refWatcher) {
List fragmentRefWatchers = new ArrayList<>();
// 1
if (SDK_INT >= O) {
fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher));
}
// 2
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);
// 3
Application application = (Application) context.getApplicationContext();
application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks);
}
…
}
复制代码
这里面的逻辑很简单,首先在注释1处将Android标准的Fragment的RefWatcher类,即AndroidOfFragmentRefWatcher添加到新创建的fragmentRefWatchers中。在注释2处使用反射将leakcanary-support-fragment包下面的SupportFragmentRefWatcher添加进来,如果你在app的build.gradle下没有添加下面这行引用的话,则会拿不到此类,即LeakCanary只会检测Activity和标准Fragment这两种情况。
debugImplementation ‘com.squareup.leakcanary:leakcanary-support-fragment:1.6.2’
复制代码
继续看到注释3处helper.activityLifecycleCallbacks里面的代码。
private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
for (FragmentRefWatcher watcher : fragmentRefWatchers) {
watcher.watchFragments(activity);
}
}
};
复制代码
可以看到,在Activity执行完onActivityCreated()方法之后,会调用指定watcher的watchFragments()方法,注意,这里的watcher可能有两种,但不管是哪一种,都会使用当前传入的activity获取到对应的FragmentManager/SupportFragmentManager对象,调用它的registerFragmentLifecycleCallbacks()方法,在对应的onDestroyView()和onDestoryed()方法执行完后,分别使用refWatcher.watch(view)和refWatcher.watch(fragment)进行内存泄漏的检测,代码如下所示。
@Override public void onFragmentViewDestroyed(FragmentManager fm, Fragment fragment) {
View view = fragment.getView();
if (view != null) {
refWatcher.watch(view);
}
}
@Override
public void onFragmentDestroyed(FragmentManagerfm, Fragment fragment) {
refWatcher.watch(fragment);
}
复制代码
注意,下面到真正关键的地方了,接下来分析refWatcher.watch()这行代码。
12、RefWatcher#watch()
public void watch(Object watchedReference, String referenceName) {
if (this == DISABLED) {
return;
}
checkNotNull(watchedReference, “watchedReference”);
checkNotNull(referenceName, “referenceName”);
final long watchStartNanoTime = System.nanoTime();
// 1
String key = UUID.randomUUID().toString();
// 2
retainedKeys.add(key);
// 3
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
// 4
ensureGoneAsync(watchStartNanoTime, reference);
}
复制代码
注意到在注释1处使用随机的UUID保证了每个检测对象对应 key 的唯一性。在注释2处将生成的key添加到类型为CopyOnWriteArraySet的Set集合中。在注释3处新建了一个自定义的弱引用KeyedWea
kReference,看看它内部的实现。
13、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标识了一个被检测的WeakReference对象。在注释1处,将弱引用和引用队列 ReferenceQueue 关联起来,如果弱引用reference持有的对象被GC回收,JVM就会把这个弱引用加入到与之关联的引用队列referenceQueue中。即 KeyedWeakReference 持有的 Activity 对象如果被GC回收,该对象就会加入到引用队列 referenceQueue 中。
接着我们回到RefWatcher.watch()里注释4处的ensureGoneAsync()方法。
14、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);
}
});
}
复制代码
在ensureGoneAsync()方法中,在注释1处使用 watchExecutor 执行了注释2处的 ensureGone 方法,watchExecutor 是 AndroidWatchExecutor 的实例。
下面看看watchExecutor内部的逻辑。
15、AndroidWatchExecutor
public final class AndroidWatchExecutor implements WatchExecutor {
…
public AndroidWatchExecutor(long initialDelayMillis) {
mainHandler = new Handler(Looper.getMainLooper());
HandlerThread handlerThread = new HandlerThread(LEAK_CANARY_THREAD_NAME);
handlerThread.start();
// 1
backgroundHandler = new Handler(handlerThread.getLooper());
this.initialDelayMillis = initialDelayMillis;
maxBackoffFactor = Long.MAX_VALUE / initialDelayMillis;
}
@Override public void execute(@NonNull Retryable retryable) {
// 2
if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
waitForIdle(retryable, 0);
} else {
postWaitForIdle(retryable, 0);
}
}
…
}
复制代码
在注释1处AndroidWatchExecutor的构造方法中,注意到这里使用HandlerThread的looper新建了一个backgroundHandler,后面会用到。在注释2处,会判断当前线程是否是主线程,如果是,则直接调用waitForIdle()方法,如果不是,则调用postWaitForIdle(),来看看这个方法。
private void postWaitForIdle(final Retryable retryable, final int failedAttempts) {
mainHandler.post(new Runnable() {
@Override public void run() {
AndroidWatchExecutor implements WatchExecutor {
…
public AndroidWatchExecutor(long initialDelayMillis) {
mainHandler = new Handler(Looper.getMainLooper());
HandlerThread handlerThread = new HandlerThread(LEAK_CANARY_THREAD_NAME);
handlerThread.start();
// 1
backgroundHandler = new Handler(handlerThread.getLooper());
this.initialDelayMillis = initialDelayMillis;
maxBackoffFactor = Long.MAX_VALUE / initialDelayMillis;
}
@Override public void execute(@NonNull Retryable retryable) {
// 2
if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
waitForIdle(retryable, 0);
} else {
postWaitForIdle(retryable, 0);
}
}
…
}
复制代码
在注释1处AndroidWatchExecutor的构造方法中,注意到这里使用HandlerThread的looper新建了一个backgroundHandler,后面会用到。在注释2处,会判断当前线程是否是主线程,如果是,则直接调用waitForIdle()方法,如果不是,则调用postWaitForIdle(),来看看这个方法。
private void postWaitForIdle(final Retryable retryable, final int failedAttempts) {
mainHandler.post(new Runnable() {
@Override public void run() {
这篇关于Android:主流三方库源码教你快速上手Leakcanary,斗鱼直播Android开发二面被刷的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-24怎么切换 Git 项目的远程仓库地址?-icode9专业技术文章分享
- 2024-12-24怎么更改 Git 远程仓库的名称?-icode9专业技术文章分享
- 2024-12-24更改 Git 本地分支关联的远程分支是什么命令?-icode9专业技术文章分享
- 2024-12-24uniapp 连接之后会被立马断开是什么原因?-icode9专业技术文章分享
- 2024-12-24cdn 路径可以指定规则映射吗?-icode9专业技术文章分享
- 2024-12-24CAP:Serverless?+AI?让应用开发更简单
- 2024-12-23新能源车企如何通过CRM工具优化客户关系管理,增强客户忠诚度与品牌影响力
- 2024-12-23原创tauri2.1+vite6.0+rust+arco客户端os平台系统|tauri2+rust桌面os管理
- 2024-12-23DevExpress 怎么实现右键菜单(Context Menu)显示中文?-icode9专业技术文章分享
- 2024-12-22怎么通过控制台去看我的页面渲染的内容在哪个文件中呢-icode9专业技术文章分享