LeakCanary不用多说,学过Android的同学都知道它。LeakCanary2.x相对于LeakCanary1.x有两个比较明显的改变:(1)源码全部使用了Kotlin进行编写(2)不需要再Application中进行install操作。今天我就从LeakCanary的启动以及各个对象的回收监听两个方面来解析这个框架
上面我们说了,2.x的版本,我们不用在Application中调用install方法了,那LeakCanary是如何启动的呢?
首先,我们先找到AppWatcherInstaller.kt这个文件,代码如下:
internal sealed class AppWatcherInstaller : ContentProvider() {
internal class LeakCanaryProcess : AppWatcherInstaller()
override fun onCreate(): Boolean {
val application = context!!.applicationContext as Application
AppWatcher.manualInstall(application)
return true
}
....
}
我们发现,AppWatcherInstaller这个类继承自ContentProvider。了解ContentProvider的同学都知道,ContentProvider注册即执行。而AppWatcherInstaller的注册就是在leakcanary-object-watcher-android文件夹的AndroidManifest.xml中,我们找到如下的代码:
<application>
<provider
android:name="leakcanary.internal.AppWatcherInstaller$MainProcess"
android:authorities="${applicationId}.leakcanary-installer"
android:enabled="@bool/leak_canary_watcher_auto_install"
android:exported="false" />
</application>
当执行打包指令时,所有的AndroidManifest.xml会合并到一起。所以我们不需要再Application中调用install方法了
目前,LeakCanary默认可以监听Activity,Fragment,ViewModel,Service。接下来我们会一一介绍
我们先回到AppWatcherInstaller的onCreate方法里,这里调用了AppWatcher的manualInstall方法,我们看一下:
@JvmOverloads
fun manualInstall(
application: Application,
retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5),
watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)
) {
checkMainThread()
......
LeakCanaryDelegate.loadLeakCanary(application)
watchersToInstall.forEach {
it.install()
}
}
第7行:主线程的校验,没什么说的
第9行:我们先点击loadLeakCanary,进入看看
val loadLeakCanary by lazy {
try {
val leakCanaryListener = Class.forName("leakcanary.internal.InternalLeakCanary")
leakCanaryListener.getDeclaredField("INSTANCE")
.get(null) as (Application) -> Unit
} catch (ignored: Throwable) {
NoLeakCanary
}
}
首先进行了反射,拿到了InternalLeakCanary对象,然后调用了get方法,最后强转为一个lamda表达式。
我们先回到AppWatcher的manualInstall方法中,我们现在只需要记住,执行了LeakCanaryDelegate.loadLeakCanary,就执行了InternalLeakCanary的invoke方法就可以了,引用另一篇博客的解释:
我们创建InternalAppWatcher时init方法就会调用,onAppWatcherInstalled是kotlin中invoke约定的应用可以将invoke函数的lambda表达式赋值给一个变量,
Kotlin的约定有很多种,而比如使用便捷的get操作,以及重载运算符等等,invoke约定也仅仅是一种约定而已;
我们可以把lambda表达式或者函数直接保存在一个变量中,然后就像执行函数一样直接执行这个变量,
这样的变量通常声明的时候都被我们赋值了已经直接定义好的lambda,或者通过成员引用而获取到的函数;
但是别忘了,在面向对象编程中,一个对象在通常情况下都有自己对应的类,那我们能不能定义一个类,
然后通过构造方法来产生一个对象,然后直接执行它呢?这正是invoke约定发挥作用的地方。
我们只需要在一个类中使用operator来修饰invoke函数,这样的类的对象就可以直接像一个保存lambda表达式的变量一样直接调用,而调用后执行的函数就是invoke函数。
我们还有另一种方式来实现可调用的对象,即让类继承自函数类型,然后重写invoke方法。
————————————————
版权声明:本文为CSDN博主「Darksiderl」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u011148116/article/details/106762665/
第5行:这里执行了appDefaultWatchers方法,然后将返回值赋值给了watchersToInstall,在第11行中,对这个变量进行了遍历,执行了每个元素的install方法。我们看一下appDefaultWatchers方法:
fun appDefaultWatchers(
application: Application,
reachabilityWatcher: ReachabilityWatcher = objectWatcher
): List<InstallableWatcher> {
return listOf(
ActivityWatcher(application, reachabilityWatcher),
FragmentAndViewModelWatcher(application, reachabilityWatcher),
RootViewWatcher(reachabilityWatcher),
ServiceWatcher(reachabilityWatcher)
)
}
在这里,我们可以看见了很多眼熟的单词:Activity,Fragment,ViewModel,RootView,Service。这些就是LeakCanary默认监听的对象,我们一个一个来介绍:
我们先看一下ActivityWatcher
class ActivityWatcher(
private val application: Application,
private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {
private val lifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityDestroyed(activity: Activity) {
reachabilityWatcher.expectWeaklyReachable(
activity, "${activity::class.java.name} received Activity#onDestroy() callback"
)
}
}
override fun install() {
application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
}
override fun uninstall() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
}
}
lifecycleCallbacks就是Application.ActivityLifecycleCallbacks,主要用来进行进行Activity生命周期监听的。install和uninstall方法分别进行了注册和解注册。
在lifecycleCallbacks创建过程中,只实现了onActivityDestroyed方法,当Activity销毁的时候,会回调到这里。然后执行了ReachabilityWatcher对象的expectWeaklyReachable方法,这个方法的具体实现是在ObjectWatcher中,我们看一下:
@Synchronized override fun expectWeaklyReachable(
watchedObject: Any,
description: String
) {
if (!isEnabled()) {
return
}
removeWeaklyReachableObjects()
val key = UUID.randomUUID()
.toString()
val watchUptimeMillis = clock.uptimeMillis()
val reference =
KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
watchedObjects[key] = reference
checkRetainedExecutor.execute {
moveToRetained(key)
}
}
第8行:我们先看一下这个方法
private fun removeWeaklyReachableObjects() {
var ref: KeyedWeakReference?
do {
ref = queue.poll() as KeyedWeakReference?
if (ref != null) {
watchedObjects.remove(ref.key)
}
} while (ref != null)
}
所表达的意思是:从引用队里中取数据,如果有数据,表示这个数据被回收了,那么从被观察的集合中移除,此时ref不为空,所以还会循环;如果ref等于空,表示没有对象回收,则直接跳出循环。watchedObjects是Map,里面的数据就是被观察的对象。
在回到ReachabilityWatcher的expectWeaklyReachable方法:
第9~13行:根据uuid创建了key,拿到时间,然后创建一个弱引用对象
第15行:以uuid创建的key为map的键,弱引用对象为值,进行存储
第17行:在子线程中,执行了moveToRetained方法,我们进去看看:
@Synchronized private fun moveToRetained(key: String) {
removeWeaklyReachableObjects()
val retainedRef = watchedObjects[key]
if (retainedRef != null) {
retainedRef.retainedUptimeMillis = clock.uptimeMillis()
onObjectRetainedListeners.forEach { it.onObjectRetained() }
}
}
第2行:这里有再次执行了removeWeaklyReachableObjects方法,重新对阻塞队列里的值进行确认,看看有没有被回收的对象
第3~7行:根据key拿到相应的弱引用,然后遍历onObjectRetainedListeners集合,执行每个元素的onObjectRetained方法。
这里可能有些同学不知道这里集合的数据是什么时候添加的,上面我们说过InternalLeakCanary的invoke方法,这个方法里有如下一句代码:
AppWatcher.objectWatcher.addOnObjectRetainedListener(this)
其实就是在invoke方法中添加的,而集合中的元素也就是InternalLeakCanary,所以执行的onObjectRetained方法也是在这个类中,我们查看这个方法:
override fun onObjectRetained() = scheduleRetainedObjectCheck()
fun scheduleRetainedObjectCheck() {
if (this::heapDumpTrigger.isInitialized) {
heapDumpTrigger.scheduleRetainedObjectCheck()
}
}
heapDumpTrigger是HeapDumpTrigger类,我们查看它的scheduleRetainedObjectCheck方法
fun scheduleRetainedObjectCheck(
delayMillis: Long = 0L
) {
val checkCurrentlyScheduledAt = checkScheduledAt
if (checkCurrentlyScheduledAt > 0) {
return
}
checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
backgroundHandler.postDelayed({
checkScheduledAt = 0
checkRetainedObjects()
}, delayMillis)
}
最关键的是第11行代码,我们查看这个方法
private fun checkRetainedObjects() {
....
var retainedReferenceCount = objectWatcher.retainedObjectCount
if (retainedReferenceCount > 0) {
gcTrigger.runGc()
retainedReferenceCount = objectWatcher.retainedObjectCount
}
if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return
val now = SystemClock.uptimeMillis()
val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
if (elapsedSinceLastDumpMillis < WAIT_BETWEEN_HEAP_DUMPS_MILLIS) {
onRetainInstanceListener.onEvent(DumpHappenedRecently)
showRetainedCountNotification(
objectCount = retainedReferenceCount,
contentText = application.getString(R.string.leak_canary_notification_retained_dump_wait)
)
scheduleRetainedObjectCheck(
delayMillis = WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis
)
return
}
dismissRetainedCountNotification()
val visibility = if (applicationVisible) "visible" else "not visible"
dumpHeap(
retainedReferenceCount = retainedReferenceCount,
retry = true,
reason = "$retainedReferenceCount retained objects, app is $visibility"
)
}
代码篇幅不少,我就挑重点介绍一下:
第4~9行:根据被观察的数量,主动进行一次GC
第11行:这个方法其实很重要,但是我们就不进入看了,总结一下主要逻辑就可以了:判断被观察者的数量是否大于0,并且小于5,如果符合这个条件,则弹出提示通知,并且这个方法返回true。如果返回true的话,下面的代码就不执行了。
第13~15行:判断上次弹出通知的时间和现在的差值,如果时间差小于60s,则弹出提示通知,并且下面的内容不再执行
第27~29行:如果上述的条件都没有满足,那么将提示通知关闭,直接进行结果分析。
Activity的监听过程,还是比较简单的
在AppWatcher的代码中(上面说过),我们知道Fragment和ViewModel的对象监听是在FragmentAndViewModelWatcher.kt这个类中,我们看一下:
class FragmentAndViewModelWatcher(
private val application: Application,
private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {
private val fragmentDestroyWatchers: List<(Activity) -> Unit> = run {
val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()
if (SDK_INT >= O) {
fragmentDestroyWatchers.add(
AndroidOFragmentDestroyWatcher(reachabilityWatcher)
)
}
getWatcherIfAvailable(
ANDROIDX_FRAGMENT_CLASS_NAME,
ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
reachabilityWatcher
)?.let {
fragmentDestroyWatchers.add(it)
}
getWatcherIfAvailable(
ANDROID_SUPPORT_FRAGMENT_CLASS_NAME,
ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
reachabilityWatcher
)?.let {
fragmentDestroyWatchers.add(it)
}
fragmentDestroyWatchers
}
private val lifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityCreated(
activity: Activity,
savedInstanceState: Bundle?
) {
for (watcher in fragmentDestroyWatchers) {
watcher(activity)
}
}
}
.....
}
有一些非关键的方法我没贴出来,比如说install,uninstall方法,进行注册和解注册,和ActivityWatcher中的代码一样;getWatcherIfAvailable和classAvailable方法,主要进行了反射相关的操作;
第33行:我们先看一下lifecycleCallbacks,和ActivityWatcher中有些不一样,ActivityWatcher中的lifecycleCallbacks实现的是onActivityDestroyed方法,而这里实现的是onActivityCreated方法。其实很好理解:ActivityWatcher监听的是Activity对象,并且LifecycleCallback监听的也是Activity的生命周期,当Activity销毁的时候,需要判断Activity对象是否释放了;而FragmentAndViewModeWatcher中监听的是Fragment和ViewModel,它们的宿主是Activity,LifecycleCallback监听的也是它们的宿主,所以只有当宿主创建了,才开始Fragment和ViewModel的监听。而Fragment和ViewModel销毁的时候,对象有没有释放的判断,并不是在这里
第39行:我们看到循环了fragmentDestryWatchers,这个列表中的元素是对不同类型的Fragment的回收监听,Fragment有三种类型
(1)非AndroidX中的Fragment,如果SDK的版本大于等于26,那么对Fragment生命周期的监听是Activity的fragmentManager.registerFragmentLifecycleCallbacks方法
(2)非AndroidX中的Fragment,如果SDK的版本小于26,那么对Fragment生命周期的监听是FragmentActivity的supportFragmentManager.registerFragmentLifecycleCallbacks方法
(3)如果是AndroidX的Fragment,那么对Fragment生命周期的监听是AndroidX包下FragmentActivity的supportFragmentManager.registerFragmentLifecycleCallbacks方法
而这三个Fragment的监听者分别是:AndroidOFragmentDestroyWatcher.kt,AndroidXFragmentDestoryWatcher.kt,AndroidSupportFragmentDestroyWatcher.kt。其实这三个关于Fragment的监听没什么区别,只是AndroidXFragmentDestoryWatcher.kt中多了对ViewModel对象的监听,我们主要讲一下AndroidXFragmentDestoryWatcher,其他两个就略过了
internal class AndroidXFragmentDestroyWatcher(
private val reachabilityWatcher: ReachabilityWatcher
) : (Activity) -> Unit {
private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {
override fun onFragmentCreated(
fm: FragmentManager,
fragment: Fragment,
savedInstanceState: Bundle?
) {
ViewModelClearedWatcher.install(fragment, reachabilityWatcher)
}
override fun onFragmentViewDestroyed(
fm: FragmentManager,
fragment: Fragment
) {
val view = fragment.view
if (view != null) {
reachabilityWatcher.expectWeaklyReachable(
view, "${fragment::class.java.name} received Fragment#onDestroyView() callback " +
"(references to its views should be cleared to prevent leaks)"
)
}
}
override fun onFragmentDestroyed(
fm: FragmentManager,
fragment: Fragment
) {
reachabilityWatcher.expectWeaklyReachable(
fragment, "${fragment::class.java.name} received Fragment#onDestroy() callback"
)
}
}
override fun invoke(activity: Activity) {
if (activity is FragmentActivity) {
val supportFragmentManager = activity.supportFragmentManager
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
ViewModelClearedWatcher.install(activity, reachabilityWatcher)
}
}
}
第5行:fragmentLifecycleCallbacks就是Fragment的生命周期回调,
onFragmentDestroyed是对Fragment对象的回收监听,当Fragment销毁的时候,会调用reachabilityWatcher的expectWeaklyReachable方法,和ActivityWatcher中调用的方法一样,具体的逻辑我就不说了,ActivityWatcher的时候已经说过了
onFragmentViewDestroyed是对Fragment中RootView对象的回收监听,当RootView销毁的时候,同样会执行reachabilityWatcher的expectWeaklyReachable方法。这个回调方法在其他两个FragmentWatcher中也是有的。
onFragmentCreated是对ViewModel对象的回收监听,ViewModelClearedWatcher是具体的监听者,我们发现在第12行和第42行都调用了ViewModelClearedWatcher的install方法,但是第一个参数不一样,这表示ViewModel的宿主不一样。一个是Activity,一个是Fragment。
我们看一下ViewModelClearedWatcher的代码:
internal class ViewModelClearedWatcher(
storeOwner: ViewModelStoreOwner,
private val reachabilityWatcher: ReachabilityWatcher
) : ViewModel() {
private val viewModelMap: Map<String, ViewModel>?
init {
// We could call ViewModelStore#keys with a package spy in androidx.lifecycle instead,
// however that was added in 2.1.0 and we support AndroidX first stable release. viewmodel-2.0.0
// does not have ViewModelStore#keys. All versions currently have the mMap field.
viewModelMap = try {
val mMapField = ViewModelStore::class.java.getDeclaredField("mMap")
mMapField.isAccessible = true
@Suppress("UNCHECKED_CAST")
mMapField[storeOwner.viewModelStore] as Map<String, ViewModel>
} catch (ignored: Exception) {
null
}
}
override fun onCleared() {
viewModelMap?.values?.forEach { viewModel ->
reachabilityWatcher.expectWeaklyReachable(
viewModel, "${viewModel::class.java.name} received ViewModel#onCleared() callback"
)
}
}
companion object {
fun install(
storeOwner: ViewModelStoreOwner,
reachabilityWatcher: ReachabilityWatcher
) {
val provider = ViewModelProvider(storeOwner, object : Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
ViewModelClearedWatcher(storeOwner, reachabilityWatcher) as T
})
provider.get(ViewModelClearedWatcher::class.java)
}
}
}
有一个比较关键的点:我们发现ViewModelClearedWatcher继承子ViewModel,也就是说它本身是一个ViewModel。并且实现了onCleared方法,这个方法在ViewModel销毁的时候会执行,和Activity的onDestroy作用一样。
第31行:这里是install方法的具体内容。如果使用过ViewModel的话,这里很容易理解,就是ViewModel和宿主的绑定。而ViewModel就是ViewModelClearedWatcher。
第8~20行:当ViewModelClearedWatcher创建的时候,通过反射拿到宿主的mMap,得到宿主中所有的ViewModel。
第22行:当ViewModelClearedWatcher被销毁时,会执行到onCleared方法,这时候会遍历宿主中所有的ViewModel,执行reachabilityWatcher的expectWeaklyReachable方法,判断ViewModel是否都已经释放了。
这就是Fragment和ViewModel的对象监听过程
Android中Service,没有类似Activity和Fragment的生命周期回调监听,所以LeakCanary采用的是Hook的方式,而这里需要我们对Android的源码有一定的了解。首先是ActivityThread,这里面有一个内部类H,继承自Handler,如下:
class H extends Handler {
.....
}
很多系统指令都会传递到这个类中,其中有一个指令是STOP_SERVICE,也就是说当我们执行stopService时,类H会接收到STOP_SERVICE指令进行Service关闭,如下:
case STOP_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStop");
handleStopService((IBinder)msg.obj);
schedulePurgeIdler();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
查看handleStopService方法:
private void handleStopService(IBinder token) {
Service s = mServices.remove(token);
if (s != null) {
try {
if (localLOGV) Slog.v(TAG, "Destroying service " + s);
s.onDestroy();
s.detachAndCleanUp();
Context context = s.getBaseContext();
if (context instanceof ContextImpl) {
final String who = s.getClassName();
((ContextImpl) context).scheduleFinalCleanup(who, "Service");
}
QueuedWork.waitToFinish();
try {
ActivityManager.getService().serviceDoneExecuting(
token, SERVICE_DONE_EXECUTING_STOP, 0, 0);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
......
}
......
}
我们可以看到第二行,拿到了Service对象,那么我们拿到这个Service对象,不就可以了吗?
其实现在还不行!因为下面还有Service方法的调用,需要调用第6行到第15行一系列的代码,才能真正的将Service关闭。
所以LeakCanary做了两步:第一步:监听STOP_SERVICE指令,拿到被监听的Service;第二步:通过动态代理,获取第17行的serviceDoneExecuting,在此方法执行之前,进行回收的判断。
我们要注意一点,这里是RootView,而不是所有的View,LeakCanary默认只会监听所有的根View。代码我就不贴了,我主要介绍一下思路,个人觉得都是大同小异
鸣谢: