当前位置: 首页 > news >正文

网站内图片变换怎么做/武汉seo外包平台

网站内图片变换怎么做,武汉seo外包平台,东莞网站优化如何,手机怎么做3d短视频网站LeakCanary 的工作原理是什么?跟我一起揭开它的神秘面纱。 一、 什么是LeakCanary LeakCanary 是大名鼎鼎的 square 公司开源的内存泄漏检测工具。目前上大部分App在开发测试阶段都会接入此工具用于检测潜在的内存泄漏问题,做的好一点的可能会搭建一个服…

LeakCanary 的工作原理是什么?跟我一起揭开它的神秘面纱。

一、 什么是LeakCanary

LeakCanary 是大名鼎鼎的 square 公司开源的内存泄漏检测工具。目前上大部分App在开发测试阶段都会接入此工具用于检测潜在的内存泄漏问题,做的好一点的可能会搭建一个服务器用于保存各个设备上的内存泄漏问题再集中处理。

本文首发于我的微信公众号:Android开发实验室,欢迎大家关注和我一起学Android,掉节操。

二、 为什么要使用LeakCanary

我们知道内存泄漏问题的排查有很多种方法, 比如说,Android Studio 自带的 Profile 工具、MAT(Memory Analyzer Tool)、以及LeakCanary。 选择 LeakCanary 作为首选的内存泄漏检测工具主要是因为它能实时检测泄漏并以非常直观的调用链方式展示内存泄漏的原因。

三、 LeakCanary 做不到的(待定)

虽然 LeakCanary 有诸多优点,但是它也有做不到的地方,比如说检测申请大容量内存导致的OOM问题、Bitmap内存未释放问题,Service 中的内存泄漏可能无法检测等。

四、 LeakCanary 源码解析

本章内容前后依赖关系强烈,建议顺序阅读。

4.1 ActivityLifecycleCallbacks 与 FragmentLifeCycleCallbacks

在开始 LeakCanary 原理解析之前,有必要简单说下 ActivityLifecycleCallbacks 与 FragmentLifeCycleCallbacks。

// ActivityLifecycleCallbacks 接口
public interface ActivityLifecycleCallbacks {void onActivityCreated(Activity var1, Bundle var2);void onActivityStarted(Activity var1);void onActivityResumed(Activity var1);void onActivityPaused(Activity var1);void onActivityStopped(Activity var1);void onActivitySaveInstanceState(Activity var1, Bundle var2);void onActivityDestroyed(Activity var1);}
复制代码

Application 类提供了 registerActivityLifecycleCallbacksunregisterActivityLifecycleCallbacks 方法用于注册和反注册 Activity 的生命周期监听类,这样我们就能在 Application 中对所有的 Activity 生命周期回调中做一些统一处理。

public abstract static class FragmentLifecycleCallbacks {public void onFragmentCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) {}public void onFragmentViewDestroyed(FragmentManager fm, Fragment f) {}public void onFragmentDestroyed(FragmentManager fm, Fragment f) {}// 省略其他的生命周期 ...}
复制代码

FragmentManager 类提供了 registerFragmentLifecycleCallbacksunregisterFragmentLifecycleCallbacks 方法用户注册和反注册 Fragment 的生命周期监听类,这样我们对每一个 Activity 进行注册,就能获取所有的 Fragment 生命周期回调。

4.2 LeakCanary 的使用

4.2.1 使用方法

我们直接在 Application 类中,添加一下代码即可。

public class ExampleApplication extends Application {@Override public void onCreate() {super.onCreate();setupLeakCanary();}protected void setupLeakCanary() {// 启用严格模式enabledStrictMode();// 判断是否是 HeapAnalyzerService 所属进程if (LeakCanary.isInAnalyzerProcess(this)) {// This process is dedicated to LeakCanary for heap analysis.// You should not init your app in this process.return;}// 注册 LeakCanaryLeakCanary.install(this);}private static void enabledStrictMode() {StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() //.detectAll() //.penaltyLog() //.penaltyDeath() //.build());}
}
复制代码
<serviceandroid:name=".internal.HeapAnalyzerService"android:process=":leakcanary"android:enabled="false"/>
复制代码

由于 LeakCanary 的核心 hropf 文件解析服务 HeapAnalyzerService 所属进程是与主进程独立的一个进程,所以在 setupLeakCanary中,我们需要排除其他进程,只对 leakcanary 进程注册 LeakCanary 监听处理。

android:enabled="false" 这是什么? 这里简单说下,AndroidManifest文件中的 enabled 属性,可以看到 HeapAnalyzerService 这个组件默认是不可用的,所以如果在代码中动态启用这个组件,可以使用以下方法:

public static void setEnabledBlocking(Context appContext, Class<?> componentClass,boolean enabled) {ComponentName component = new ComponentName(appContext, componentClass);PackageManager packageManager = appContext.getPackageManager();int newState = enabled ? COMPONENT_ENABLED_STATE_ENABLED : COMPONENT_ENABLED_STATE_DISABLED;// Blocks on IPC.packageManager.setComponentEnabledSetting(component, newState, DONT_KILL_APP);
}
复制代码

4.3 LeakCanary.install(this) 干了什么

LeakCanary 的 install 方法实际上构造了一个 RefWatcher,

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

我们一个个来看这个注册方法。首先是 refWatcher 方法构造了一个 AndroidRefWatcherBuilder, 传入参数是当前Application 的 Context.

public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {return new AndroidRefWatcherBuilder(context);
}
复制代码

listenerServiceClass 和 excludedRefs 方法是基于建造者模式传入分析Service 和 排除已知的泄漏问题 AndroidExcludedRefs,这里我就不贴代码了。

重点看下 buildAndInstall 方法,这个方法很形象的表示将要进行建造者模式的最后一步 build 和 注册一些监听器,下面我们来看具体代码:

public @NonNull RefWatcher buildAndInstall() {// 只允许 install 一次if (LeakCanaryInternals.installedRefWatcher != null) {throw new UnsupportedOperationException("buildAndInstall() should only be called once.");}// 建造者模式的最后一步,构造对象RefWatcher refWatcher = build();// 判断是否开启了 LeakCanary,没有开启默认会返回 DISABLED 对象if (refWatcher != DISABLED) {// 手动开启 DisplayLeakActivity 组件,会在桌面上显示一个查看内存泄漏结果的入口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;
}
复制代码

以上代码作用大部分都在代码中注释了,剩下 ActivityRefWatcher.install 和 FragmentRefWatcher.Helper.install 方法没有注释。下面我们就来具体看看这两个方法究竟干了什么。

(1). ActivityRefWatcher.install

ActivityRefWatcher 的静态方法 install 获取到了当前 Application,然后添加了一个生命周期监听器 ActivityLifecycleCallbacks,这里的 lifecycleCallbacks 仅仅关注了 Activity 销毁的回调 onActivityDestroyed,在这里将传入的对象 activity 监听起来, refWatcher.watch(activity); 的具体代码我们稍后分析。

public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) {Application application = (Application) context.getApplicationContext();ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
}private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =new ActivityLifecycleCallbacksAdapter() {@Override public void onActivityDestroyed(Activity activity) {refWatcher.watch(activity);}
};
复制代码

(2). FragmentRefWatcher.Helper.install FragmentRefWatcher.Helper 的静态方法 install 里同样会注册一个 ActivityLifecycleCallbacks 用于监听 Activity 生命周期中的 onActivityCreated 的创建完成的回调,在 Activity 创建完成后,会对这个 Activity 注册 Fragment 的生命周期监听器。install 方法首先会判断系统是否大于等于 Android O, 如果是那么会使用 android.app.FragmentManager 进行注册,如果需要兼容 Android O 以下需要自行在依赖中添加对 leakcanary-support-fragment 组件的依赖,然后通过反射构造出SupportFragmentRefWatcher; 然后将fragmentRefWatchers所有监听器取出,在 Activity 创建完成后,添加 Fragment 的生命监听,主要关注 Fragment 的 onFragmentViewDestroyedonFragmentDestroyed 方法。具体代码如下:

public static void install(Context context, RefWatcher refWatcher) {List<FragmentRefWatcher> fragmentRefWatchers = new ArrayList<>();// 系统是否大于等于 Android O,如果是,添加 AndroidOFragmentRefWatcherif (SDK_INT >= O) {fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher));}// 如果添加了leakcanary-support-fragment的依赖,通过反射可以构造SupportFragmentRefWatchertry {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);// 先监听 Activity 的创建完成回调Application application = (Application) context.getApplicationContext();application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks);}private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks =new ActivityLifecycleCallbacksAdapter() {@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {// Activity 创建完成后,对Activity中的Fragment注册生命周期监听for (FragmentRefWatcher watcher : fragmentRefWatchers) {watcher.watchFragments(activity);}}};
复制代码
// AndroidOFragmentRefWatcher.javaprivate final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =new FragmentManager.FragmentLifecycleCallbacks() {// Fragment 中的View 视图销毁时@Override public void onFragmentViewDestroyed(FragmentManager fm, Fragment fragment) {View view = fragment.getView();if (view != null) {refWatcher.watch(view);}}// Fragment 销毁时@Overridepublic void onFragmentDestroyed(FragmentManager fm, Fragment fragment) {refWatcher.watch(fragment);}};@Override public void watchFragments(Activity activity) {// 通过FragmentManager 注册 FragmentLifecycleCallbacksFragmentManager fragmentManager = activity.getFragmentManager();fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
}
复制代码

上述流程我们已经完全搞清楚了,用一个流程图可以表示为:

4.4 LeakCanary 内存泄漏检测原理

从行文结构上,本小节应属于上一节后半部分内容,但是RefWatcher 的 watch 方法足够重要和复杂,所以有必要单独列一节仔细讲解内部原理。

4.4.1 基础知识——弱引用 WeakReference 和 引用队列 ReferenceQueue

关于引用类型和引用队列相关知识,读者可以参考白话 JVM——深入对象引用,这篇文章我认为讲解的比较清晰。

这里,我简单举个例子,弱引用在定义的时候可以指定引用对象和一个 ReferenceQueue,弱引用对象在垃圾回收器执行回收方法时,如果原对象只有这个弱引用对象引用着,那么会回收原对象,并将弱引用对象加入到 ReferenceQueue,通过 ReferenceQueue 的 poll 方法,可以取出这个弱引用对象,获取弱引用对象本身的一些信息。看下面这个例子。

mReferenceQueue = new ReferenceQueue<>();
// 定义一个对象
o = new Object();
// 定义一个弱引用对象引用 o,并指定引用队列为 mReferenceQueue
weakReference = new WeakReference<Object>(o, mReferenceQueue);
// 去掉强引用
o = null;
// 触发应用进行垃圾回收
Runtime.getRuntime().gc();
// hack: 延时100ms,等待gc完成
try {Thread.sleep(100);
} catch (InterruptedException e) {e.printStackTrace();
}
Reference ref = null;
// 遍历 mReferenceQueue,取出所有弱引用
while ((ref = mReferenceQueue.poll()) != null) {System.out.println("============ \n ref in queue");
}
复制代码

打印结果为:

============ ref in queue

4.4.2 基础知识——hprof文件

hprof 文件可以展示某一时刻java堆的使用情况,根据这个文件我们可以分析出哪些对象占用大量内存和未在合适时机释放,从而定位内存泄漏问题。

Android 生成 hprof 文件整体上有两种方式:

  1. 使用 adb 命令
adb shell am dumpheap <processname> <FileName>
复制代码
  1. 使用 android.os.Debug.dumpHprofData 方法 直接使用 Debug 类提供的 dumpHprofData 方法即可。
Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
复制代码

Android Studio 自带 Android Profiler 的 Memory 模块的 dump 操作使用的是方法一。这两种方法生成的 .hprof 文件都是 Dalvik 格式,需要使用 AndroidSDK 提供的 hprof-conv 工具转换成J2SE HPROF格式才能在MAT等标准 hprof 工具中查看。

hprof-conv dump.hprof converted-dump.hprof  
复制代码

至于hprof内部格式如何,本文不做具体介绍,以后有机会再单独写一篇文章来仔细讲解。LeakCanary 解析 .hprof 文件用的是 square 公司开源的另一项目:haha.

4.4.3 watch方法

终于到了 LeakCanary 关键部分了。我们从 watch 方法入手,前面的代码都是为了增强鲁棒性,我们直接从生成唯一id开始,LeakCanary 构造了一个带有 key 的弱引用对象,并且将 queue 设置为弱引用对象的引用队列。

这里解释一下,为什么需要创建一个带有 key 的弱引用对象,不能直接使用 WeakReference 么? 举个例子,假设 OneActivity 发生了内存泄漏,那么执行 GC 操作时,肯定不会回收 Activity 对象,这样 WeakReference 对象也不会被回收。假设当前启动了 N 个 OneActivity,Dump内存时我们可以获取到内存中的所有 OneActivity,但是当我们准备去检测其中某一个 Activity 的泄漏问题时,我们就无法匹配。但是如果使用了带有 key 的 WeakReference 对象,发生泄露时泄漏时,key 的值也会 dump 保存下来,这样我们根据 key 的一一对应关系就能映射到某一个 Activity。

然后,LeakCanary 调用了 ensureGoneAsync 方法去检测内存泄漏。

public void watch(Object watchedReference, String referenceName) {if (this == DISABLED) {return;}checkNotNull(watchedReference, "watchedReference");checkNotNull(referenceName, "referenceName");final long watchStartNanoTime = System.nanoTime();// 对当前监视对象设置一个唯一 idString key = UUID.randomUUID().toString();// 添加到 Set<String> 中retainedKeys.add(key);// 构造一个带有id 的 WeakReference 对象final KeyedWeakReference reference =new KeyedWeakReference(watchedReference, key, referenceName, queue);// 检测对象是否被回收了ensureGoneAsync(watchStartNanoTime, reference);
}
复制代码

4.4.4 ensureGoneAsync 方法

ensureGoneAsync 方法构造了一个 Retryable 对象,并将它传给 watchExecutor 的 execute 方法。

private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {// watchExecutor 是 AndroidWatchExecutor的一个实例watchExecutor.execute(new Retryable() {@Override public Retryable.Result run() {return ensureGone(reference, watchStartNanoTime);}});
}
复制代码

watchExecutor 是 AndroidWatchExecutor 的一个实例, AndroidWatchExecutor 的 execute 方法的作用就是判断当前线程是否是主线程,如果是主线程,那么直接执行 waitForIdle 方法,否则通过 Handler 的 post 方法切换到主线程再执行 waitForIdle 方法。

@Override public void execute(@NonNull Retryable retryable) {// 判断当前线程是否是主线程if (Looper.getMainLooper().getThread() == Thread.currentThread()) {waitForIdle(retryable, 0);} else {postWaitForIdle(retryable, 0);}
}
复制代码

waitForIdle 方法通过调用 addIdleHandler 方法,指定当主进程中没有需要处理的事件时,在这个空闲期间执行 postToBackgroundWithDelay 方法。

private void waitForIdle(final Retryable retryable, final int failedAttempts) {// 由于上面的 execute 方法,已经保证了此方法在主线程中执行,所以Looper.myQueue()获取的主线程的消息队列Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {@Override public boolean queueIdle() {postToBackgroundWithDelay(retryable, failedAttempts);// return false 表示执行完之后,就立即移除这个事件return false;}});
}
复制代码

postToBackgroundWithDelay 方法首先会计算延迟时间 delayMillis,这个延时是有 exponentialBackoffFactor(指数因子) 乘以初始延时时间得到的, exponentialBackoffFactor(指数因子)会在2^n 和 Long.MAX_VALUE / initialDelayMillis 中取较小值,也就说延迟时间delayMillis = initialDelayMillis * 2^n,且不能超过 Long.MAX_VALUE。

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 里的 run 方法Retryable.Result result = retryable.run();// 如果需要重试,那么再添加到主线程的空闲期间执行if (result == RETRY) {postWaitForIdle(retryable, failedAttempts + 1);}}}, delayMillis);
}
复制代码

postToBackgroundWithDelay 方法每次执行会指数级增加延时时间,延时时间到了后,会执行 Retryable 里的方法,如果返回为重试,那么会增加延时时间并执行下一次。

retryable.run() 的run 方法又执行了什么呢?别忘了我们ensureGoneAsync中的代码,一直在重试的代码正式 ensureGone 方法。

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

4.4.5 ensureGone 方法

我现在讲ensureGone方法的完整代码贴出来,我们逐行分析:

Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {long gcStartNanoTime = System.nanoTime();// 前面不是有一个重试的机制么,这里会计下这次重试距离第一次执行花了多长时间long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);// 移除所有弱引用可达对象,后面细讲removeWeaklyReachableReferences();// 判断当前是否正在开启USB调试,LeakCanary 的解释是调试时可能会触发不正确的内存泄漏if (debuggerControl.isDebuggerAttached()) {// The debugger can create false leaks.return RETRY;}// 上面执行 removeWeaklyReachableReferences 方法,判断是不是监视对象已经被回收了,如果被回收了,那么说明没有发生内存泄漏,直接结束if (gone(reference)) {return DONE;}// 手动触发一次 GC 垃圾回收gcTrigger.runGc();// 再次移除所有弱引用可达对象removeWeaklyReachableReferences();// 如果对象没有被回收if (!gone(reference)) {long startDumpHeap = System.nanoTime();long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);// 使用 Debug 类 dump 当前堆内存中对象使用情况File heapDumpFile = heapDumper.dumpHeap();// dumpHeap 失败的话,会走重试机制if (heapDumpFile == RETRY_LATER) {// Could not dump the heap.return RETRY;}long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);// 将hprof文件、key等属性构造一个 HeapDump 对象HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key).referenceName(reference.name).watchDurationMs(watchDurationMs).gcDurationMs(gcDurationMs).heapDumpDurationMs(heapDumpDurationMs).build();// heapdumpListener 分析 heapDump 对象heapdumpListener.analyze(heapDump);}return DONE;
}
复制代码

看完上述代码,基本把检测泄漏的大致过程走了一遍,下面我们来看一些具体的细节。

(1). removeWeaklyReachableReferences 方法

removeWeaklyReachableReferences 移除所有弱引用可达对象是怎么工作的?

private void removeWeaklyReachableReferences() {KeyedWeakReference ref;while ((ref = (KeyedWeakReference) queue.poll()) != null) {retainedKeys.remove(ref.key);}
}
复制代码

还记得我们在 refWatcher.watch 方法保存了当前监视对象的 ref.key 了么,如果这个对象被回收了,那么对应的弱引用对象会在回收时被添加到queue中,通过 poll 操作就可以取出这个弱引用,这时候我们从retainedKeys中移除这个 key, 代表这个对象已经被正常回收,不需要再被监视了。

那么现在来看,判断这个对象是否被回收就比较简单了?

private boolean gone(KeyedWeakReference reference) {// retainedKeys 中不包含 reference.key 的话,就代表这个对象已经被回收了return !retainedKeys.contains(reference.key);
}
复制代码

(2). dumpHeap 方法

heapDumper.dumpHeap() 是执行生成hprof的方法,heapDumper 是 AndroidHeapDumper 的一个对象,我们来具体看看它的 dump 方法。

public File dumpHeap() {// 生成一个存储 hprof 的文件File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();// 文件创建失败if (heapDumpFile == RETRY_LATER) {return RETRY_LATER;}// FutureResult 内部有一个 CountDownLatch,用于倒计时FutureResult<Toast> waitingForToast = new FutureResult<>();// 切换到主线程显示 toastshowToast(waitingForToast);// 等待5秒,确保 toast 已完成显示if (!waitingForToast.wait(5, SECONDS)) {CanaryLog.d("Did not dump heap, too much time waiting for Toast.");return RETRY_LATER;}// 创建一个通知Notification.Builder builder = new Notification.Builder(context).setContentTitle(context.getString(R.string.leak_canary_notification_dumping));Notification notification = LeakCanaryInternals.buildNotification(context, builder);NotificationManager notificationManager =(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);int notificationId = (int) SystemClock.uptimeMillis();notificationManager.notify(notificationId, notification);Toast toast = waitingForToast.get();try {// 开始 dump 内存到指定文件Debug.dumpHprofData(heapDumpFile.getAbsolutePath());cancelToast(toast);notificationManager.cancel(notificationId);return heapDumpFile;} catch (Exception e) {CanaryLog.d(e, "Could not dump heap");// Abort heap dumpreturn RETRY_LATER;}
}
复制代码

这段代码里我们需要看看 showToast() 方法,以及它是如何确保 toast 已完成显示(有点黑科技的感觉)。

private void showToast(final FutureResult<Toast> waitingForToast) {mainHandler.post(new Runnable() {@Override public void run() {// 当前 Activity 已经 paused的话,直接返回if (resumedActivity == null) {waitingForToast.set(null);return;}// 构建一个toast 对象final Toast toast = new Toast(resumedActivity);toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);toast.setDuration(Toast.LENGTH_LONG);LayoutInflater inflater = LayoutInflater.from(resumedActivity);toast.setView(inflater.inflate(R.layout.leak_canary_heap_dump_toast, null));// 将toast加入显示队列toast.show();// Waiting for Idle to make sure Toast gets rendered.// 主线程中添加空闲时操作,如果主线程是空闲的,会将CountDownLatch执行 countDown 操作Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {@Override public boolean queueIdle() {waitingForToast.set(toast);return false;}});}});
}
复制代码

首先我们需要知道所有的Toast对象,并不是我们一调用 show 方法就立即显示的。NotificationServiceManager会从mToastQueue中轮询去除Toast对象进行显示。如果Toast的显示不是实时的,那么我们如何知道Toast是否已经显示完成了呢?我们在 Toast 调用 show 方法后调用 addIdleHandler, 在主进程空闲时执行 CountDownLatch 的减一操作。由于我们知道我们顺序加入到主线程的消息队列中的操作:先显示Toast,再执行 CountDownLatch 减一操作。所以在 if (!waitingForToast.wait(5, SECONDS)) 的判断中,我们最多等待5秒,如果超时会走重试机制,如果我们的 CountDownLatch 已经执行了减一操作,则会正常走后续流程,同时我们也能推理出它前面 toast 肯定已经显示完成了。

Debug.dumpHprofData(heapDumpFile.getAbsolutePath());是系统Debug类提供的方法,我就不做具体分析了。

(3). heapdumpListener.analyze(heapDump) 方法

heapdumpListener 是 ServiceHeapDumpListener 的一个对象,最终执行了HeapAnalyzerService.runAnalysis方法。

@Override public void analyze(@NonNull HeapDump heapDump) {checkNotNull(heapDump, "heapDump");HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
复制代码
// 启动前台服务
public static void runAnalysis(Context context, HeapDump heapDump,Class<? extends AbstractAnalysisResultService> listenerServiceClass) {setEnabledBlocking(context, HeapAnalyzerService.class, true);setEnabledBlocking(context, listenerServiceClass, true);Intent intent = new Intent(context, HeapAnalyzerService.class);intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());intent.putExtra(HEAPDUMP_EXTRA, heapDump);ContextCompat.startForegroundService(context, intent);
}
复制代码

HeapAnalyzerService 继承自 IntentService,IntentService的具体原理我就不多做解释了。IntentService会将所有并发的启动服务操作,变成顺序执行 onHandleIntent 方法。

@Override protected void onHandleIntentInForeground(@Nullable Intent intent) {if (intent == null) {CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");return;}// 监听 hprof 文件分析结果的类String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);// hprof 文件类HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);HeapAnalyzer heapAnalyzer =new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses);// checkForLeak 会调用 haha 组件中的工具,分析 hprof 文件AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,heapDump.computeRetainedHeapSize);// 将分析结果发送给监听器 listenerClassNameAbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
}
复制代码

我们来看下 checkForLeak 方法,我们一起来看下吧。

public @NonNull AnalysisResult checkForLeak(@NonNull File heapDumpFile,@NonNull String referenceKey,boolean computeRetainedSize) {long analysisStartNanoTime = System.nanoTime();// 文件不存在的话,直接返回if (!heapDumpFile.exists()) {Exception exception = new IllegalArgumentException("File does not exist: " + heapDumpFile);return failure(exception, since(analysisStartNanoTime));}try {// 更新进度回调listener.onProgressUpdate(READING_HEAP_DUMP_FILE);// 将 hprof 文件解析成 SnapshotHprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);HprofParser parser = new HprofParser(buffer);listener.onProgressUpdate(PARSING_HEAP_DUMP);Snapshot snapshot = parser.parse();listener.onProgressUpdate(DEDUPLICATING_GC_ROOTS);// 移除相同 GC root项deduplicateGcRoots(snapshot);listener.onProgressUpdate(FINDING_LEAKING_REF);// 查找内存泄漏项Instance leakingRef = findLeakingReference(referenceKey, snapshot);// False alarm, weak reference was cleared in between key check and heap dump.// 没有找到,就代表没有泄漏if (leakingRef == null) {return noLeak(since(analysisStartNanoTime));}// 找到泄漏处的引用关系链return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, computeRetainedSize);} catch (Throwable e) {return failure(e, since(analysisStartNanoTime));}
}
复制代码

hprof 文件的解析是由开源项目 haha 完成的,我这里不做过多展开。

findLeakingReference 方法是查找泄漏的引用处,我们看下代码:

private Instance findLeakingReference(String key, Snapshot snapshot) {// 从 hprof 文件保存的对象中找到所有 KeyedWeakReference 的实例ClassObj refClass = snapshot.findClass(KeyedWeakReference.class.getName());if (refClass == null) {throw new IllegalStateException("Could not find the " + KeyedWeakReference.class.getName() + " class in the heap dump.");}List<String> keysFound = new ArrayList<>();// 对 KeyedWeakReference 实例列表进行遍历for (Instance instance : refClass.getInstancesList()) {// 获取每个实例里的所有字段List<ClassInstance.FieldValue> values = classInstanceValues(instance);// 找到 key 字段对应的值Object keyFieldValue = fieldValue(values, "key");if (keyFieldValue == null) {keysFound.add(null);continue;}// 将 keyFieldValue 转为 String 对象String keyCandidate = asString(keyFieldValue);// 如果这个对象的 key 和 我们查找的 key 相同,那么返回这个弱对象持有的原对象if (keyCandidate.equals(key)) {return fieldValue(values, "referent");}keysFound.add(keyCandidate);}throw new IllegalStateException("Could not find weak reference with key " + key + " in " + keysFound);
}
复制代码

到现在为止,我们已经把 LeakCanary 检测内存泄漏的全部过程的源码看完了。个人认为 LeakCanary 源码写的不错,可读性很高,查找调用关系也比较方便(这里黑一下 bilibili 的 DanmakusFlameMaster)。

五. 版权声明

本文首发于我的微信公众号:Android开发实验室,欢迎大家关注和我一起学Android,掉节操。未经允许,不得转载。

http://www.lbrq.cn/news/968869.html

相关文章:

  • 零基础网络工程师培训/正规seo排名多少钱
  • 杨永生的设计风格/万词霸屏百度推广seo
  • 企业信息系统架构/如何做好搜索引擎优化工作
  • 广东政府网站集约化平台/希爱力
  • 网站建设 电话营销/小说风云榜
  • 广东 建设银行社会招聘网站/快速建站教程
  • 张家港普通网站建设/软文推广去哪个平台好
  • phpcmsv9 网站搬家/优化服务公司
  • 进出石家庄最新通知/seo排名规则
  • app免费下载网站地址进入/怎么优化标题和关键词排名
  • 湛江网站建设皆选小罗24专业/百度搜索一下就知道
  • 深圳龙岗做网站公司/打开百度app
  • 做网站找哪个公司好/百度seo排名点击器app
  • 说一说网站建设的含义/百度网盘免费下载
  • 购物网站开发背景及目的/拓客渠道有哪些
  • 做网站公司 营销/深圳正规seo
  • 青岛专业网站建设公司/超级搜索引擎
  • 甘肃疫情最新消息2021/怎么关闭seo综合查询
  • 通江县建设局网站/开网站需要什么流程
  • 做网络教育录播网站/google排名
  • 梦幻创意网站建设/百度竞价关键词价格查询
  • 正常网站 月均ip pv/创建网站免费
  • 网站开发 兼职项目/安卓优化大师手机版下载
  • wordpress远程上传媒体文件夹/seo查询优化
  • wordpress中文目录/网络优化大师
  • 网站建设销售ppt模板/网站seo标题优化技巧
  • 中国软件是外包公司吗/百度搜索优化关键词排名
  • 黄山购物网站建设/友情链接交换的作用在于
  • 网站建设技术架构/房地产销售怎么找客户
  • 服务器网站配置/百度网盘搜索引擎入口官网
  • 初试Spring AI实现聊天功能
  • SQL学习记录01
  • 工业软件加密锁复制:一场技术与安全的博弈
  • Linux:1_Linux下基本指令
  • 闲庭信步使用图像验证平台加速FPGA的开发:第十二课——图像增强的FPGA实现
  • 应用层协议和JSON的使用