LeakCanary可能被你忽略的点
Posted 涂程
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeakCanary可能被你忽略的点相关的知识,希望对你有一定的参考价值。
好文推荐:
作者:giswangsj
说起leakcanary
大家应该都很熟悉,问起原理应该都知道在对象被销毁时通过WeakReference+ReferenceQueue检测对象是否被回收,延迟二次检测后还没被回收则认为是嫌疑对象,然后dump heap并对其进行分析…
但是你知道leakcanary
可以检测哪些对象吗?又是如何获取这些即将销毁的对象呢?
先说问题1的结论:
leakcanary
2.6版本之前只能对Activity,Fragment进行监控。
leakcanary
2.6版本以后增加了对ViewModel,RootView,Service的监控。
至于如何检测这些对象的销毁时机,下面以leakcanary-android:2.7
代码为例做简单的探讨。
1,初始化
众所周知,leakcanary
从2.0版本开始就不需要手动初始化了,其主要是通过ContentProvider
来实现免初始化:
<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>
在其onCreate()中进行了具体的初始化工作:
override fun onCreate(): Boolean
val application = context!!.applicationContext as Application
AppWatcher.manualInstall(application)
return true
接着看AppWatcher.manualInstall()
中做了什么:
fun manualInstall(
application: Application,
retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5),
watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)
)
...
watchersToInstall.forEach
it.install()
删繁就简,这里主要是遍历了watchersToInstall
并调用了每个item的install()
,那么watchersToInstall
是什么呢?看它的默认实现appDefaultWatchers(application)
:
fun appDefaultWatchers(
application: Application,
reachabilityWatcher: ReachabilityWatcher = objectWatcher
): List<InstallableWatcher>
return listOf(
// 监控Activity
ActivityWatcher(application, reachabilityWatcher),
// 监控Fragment和ViewModel
FragmentAndViewModelWatcher(application, reachabilityWatcher),
// 监控RootView
RootViewWatcher(reachabilityWatcher),
// 监控Service
ServiceWatcher(reachabilityWatcher)
)
这里就是返回了一个包含四个Watcher组成的List
,分别对Activity,Fragment,ViewModel,RootView,Service的销毁进行监控,拿到即将销毁的对象通过WeakReference和ReferenceQueue方式进行内存泄漏的初步判断,最后Dump HeapProfile进行具体分析。
下面就看看这些Watcher是如何实现监控对象销毁过程的。
2,ActivityWatcher
ActivityWatcher
非常简单,通过Application注册Activity的生命周期回调,来监控每一个Activity的销毁,在Activity销毁时通过reachabilityWatcher
将当前Activity对象添加到监控队列,然后进行具体分析。
class ActivityWatcher(
private val application: Application,
private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher
private val lifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks by noOpDelegate()
override fun onActivityDestroyed(activity: Activity)
// 监控activity对象
reachabilityWatcher.expectWeaklyReachable(
activity, "$activity::class.java.name received Activity#onDestroy() callback"
)
override fun install()
// 注册Activity的生命周期回调
application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
3,FragmentAndViewModelWatcher
这个Watcher实现了对Fragment和ViewModel的销毁监控,首先看一下对Fragment的销毁监控:
3.1 监控Fragment销毁
Fragment有三种:framework自带的,supportv4包中的和androidx中的。因此需要对这三种情况分别处理,不过思路都是一样的,差别就在于导包。那么就看一下framework自带的Fragment如何监控。
class FragmentAndViewModelWatcher(
private val application: Application,
private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher
private val fragmentDestroyWatchers: List<(Activity) -> Unit> = run
val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()
// 添加对三种Fragment处理的Watcher
if (SDK_INT >= O)
fragmentDestroyWatchers.add(
AndroidOFragmentDestroyWatcher(reachabilityWatcher)
)
...
fragmentDestroyWatchers
private val lifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks by noOpDelegate()
override fun onActivityCreated(
activity: Activity,
savedInstanceState: Bundle?
)
// 在ActivityCreate时调用fragmentDestroyWatchers中的每个Watcher
for (watcher in fragmentDestroyWatchers)
watcher(activity)
override fun install()
// 注册Activity的生命周期
application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
接着看一下AndroidOFragmentDestroyWatcher
中如何处理framework自带的Fragment。
internal class AndroidOFragmentDestroyWatcher(
private val reachabilityWatcher: ReachabilityWatcher
) : (Activity) -> Unit
private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks()
override fun onFragmentViewDestroyed(
fm: FragmentManager,
fragment: Fragment
)
val view = fragment.view
if (view != null)
// 将Fragment中的view加入监控队列
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
)
// 将Fragment加入监控队列
reachabilityWatcher.expectWeaklyReachable(
fragment, "$fragment::class.java.name received Fragment#onDestroy() callback"
)
override fun invoke(activity: Activity)
val fragmentManager = activity.fragmentManager
// 通过fragmentManager注册Fragment的生命周期回调
fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
使用的高阶函数,直接看invoke()
,在其中获取Activity的fragmentManager,并注册Fragment的生命周期回调。
在回调onFragmentViewDestroyed
中获取Fragment中的view,将view加入监控队列。
在回调onFragmentDestroyed
中将Fragment加入监控队列。
3.2 监控ViewModel销毁
在对AndroidX中Fragment监控时实现了对ViewModel的监控,因为只有AndroidX中才提供了ViewModel。代码如下:
internal class AndroidXFragmentDestroyWatcher(
private val reachabilityWatcher: ReachabilityWatcher
) : (Activity) -> Unit
private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks()
override fun onFragmentCreated(
fm: FragmentManager,
fragment: Fragment,
savedInstanceState: Bundle?
)
// 将Fragment中的ViewModel加入监控队列
ViewModelClearedWatcher.install(fragment, reachabilityWatcher)
...
override fun invoke(activity: Activity)
if (activity is FragmentActivity)
val supportFragmentManager = activity.supportFragmentManager
// 注册Fragment生命周期回调
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
// 将activity中的ViewModel加入监控队列
ViewModelClearedWatcher.install(activity, reachabilityWatcher)
ViewModel存在于Activity和Fragment中,因此需要对两者的ViewModel进行监控,首先在invoke()
中注册了Fragment生命周期回调,并在回调的onFragmentCreated
通过ViewModelClearedWatcher
对该Fragment中的ViewModel进行监控,然后直接通过ViewModelClearedWatcher
对当前Activity进行了监控。
接着看一下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
// 反射获取ViewModelStore实例中的mMap对象
mMapField[storeOwner.viewModelStore] as Map<String, ViewModel>
catch (ignored: Exception)
null
override fun onCleared()
// 遍历mMap对象将其中的每个ViewModel对象加入监控队列
viewModelMap?.values?.forEach viewModel ->
reachabilityWatcher.expectWeaklyReachable(
viewModel, "$viewModel::class.java.name received ViewModel#onCleared() callback"
)
companion object
fun install(
storeOwner: ViewModelStoreOwner,
reachabilityWatcher: ReachabilityWatcher
)
// 创建ViewModelProvider并设置一个Factory,
val provider = ViewModelProvider(storeOwner, object : Factory
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
// 创建ViewModelClearedWatcher并传递参数
ViewModelClearedWatcher(storeOwner, reachabilityWatcher) as T
)
provider.get(ViewModelClearedWatcher::class.java)
注意ViewModelClearedWatcher
是一个ViewModel,在install()
中创建了ViewModelClearedWatcher
的实例,在其初始化时反射获取当前ViewModelStoreOwner
对象的用于保存ViewModel的mMap
对象,然后在其onCleared
时遍历mMap
将其中的每个ViewModel对象加入监控队列。
3.3 小结:
1,对于Fragment的监控分了三种情况,分别是framework自带的,supportv4包中的和androidx中的。
2,Fragment的监控是在Activity创建时,获取当前Activity的fragmentManager,通过fragmentManager添加Fragment的生命周期回调,在回调中分别将Fragment对象以及其中的View添加到监控队列。
3,对于ViewModel的监控需要对Activity和Fragment中的ViewModel分别进行。
4,通过当前ViewModelStoreOwner
实例创建ViewModel对象,则该ViewModel对象会跟随ViewModelStoreOwner
实例一起销毁。
5,通过反射获取当前ViewModelStoreOwner
中用于存放ViewModel的集合mMap
,在4中创建的ViewModel销毁时遍历该mMap
,将其每个对象都添加到监控队列中。
4,RootViewWatcher
RootViewWatcher监测的是DecorView
,在Activity,Dialog,ToolTip和Toast等创建过程中都涉及到DecorView
的创建,那怎么获取到这玩意的添加和销毁呢?
熟悉View相关流程的应该知道,在ActivityThread中执行完Activity的onResume后会将其DecorView
添加到WindowManagerGlobal
的一个集合中,可以通过反射获取到这个集合,对这个集合进行代理即可监听DecorView
的添加和删除,给DecorView
设置AttachStateChangeListener
即可监听DecorView的Attached
和Detached
状态。
LeakCanary的代码过于复杂,下面用简单的代码实现其大体流程:
class RootViewSpy
fun install()
val windowManagerClass = Class.forName("android.view.WindowManagerGlobal")
// 1,反射获取WindowManagerGlobal实例对象
val windowManagerInstance = windowManagerClass.getMethod("getInstance").invoke(null)
// 2,反射获取WindowManagerGlobal实例对象中的mViews集合
val mViewsField =
windowManagerClass.getDeclaredField("mViews").apply isAccessible = true
val mViews = mViewsField.get(windowManagerInstance) as ArrayList<View>
// 3,将mViews中的内容存入代理集合中
delegatingViewList.apply addAll(mViews)
// 4,用代理集合替换原始mViews对象
mViewsField.set(windowManagerInstance, delegatingViewList)
// 代理对象
private val delegatingViewList = object : ArrayList<View>()
override fun add(rootView: View): Boolean
// 给DecorView添加AttachStateChange状态监听
rootView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener
override fun onViewAttachedToWindow(v: View)
LogUtil.e("onViewAttachedToWindow")
override fun onViewDetachedFromWindow(v: View)
LogUtil.e("onViewDetachedFromWindow")
)
return super.add(rootView)
对WindowManagerImpl进行动态代理也可以监听DecorView的添加和删除
小结
1,反射获取WindowManagerGlobal中用于存DecorView的集合
2,对这个集合进行代理,监控DecorView的添加过程
3,对每个DecorView添加AttachStateChangeListener
,监测其Attached
和Detached
过程
4,由于前面已经有了Activity的检测,RootViewWatcher主要会对Toast,ToolTip及Dialog(默认不检测)的RootView进行监测
5,ServiceWatcher
监控服务的销毁需要对Service的Stop流程有所了解,Service的Stop有三种情况:Activity中stopService(),Service中stopSelf()和unBindService()。这三种情况通过不同方式进入AMS,但最终都会通过Binder方式调用ApplicationThread的scheduleStopService()
方法,ApplicationThread会通过handler发送消息给ActivityThread;ActivityThread中执行Service的Stop相关逻辑,最后ActivityThread会通知AMS做最后收尾工作。大体流程如下:
要监听Service的Stop有两个点:
-
在ActivityThread收到ApplicationThread消息时也就是上图中的Hook点1,通过给ActivityThread中的Handler对象添加callback来实现,此时Service并没有开始执行stop相关操作,因此可以获取其实例。
-
在ActivityThread中执行完Service的Stop后会通过binder调用通知AMS完成最后的工作,可以通过Hook AMS来监听到。此时服务已经执行了
onDestory()
,可能无法在获取到其实例了,因此需要在hook点1处保存service实例,然后在此处获取实例。
LeakCanary在Hook点1处获取即将stop的Service的实例,并通过弱引用保存,然后在Hook点2处获取实例的弱引用,进而对其监听。
代码使用了高阶函数,大体如下:
class ServiceWatcher
private val servicesToBeDestroyed = WeakHashMap<IBinder, WeakReference<Service>>()
private val activityThreadClass by lazy Class.forName("android.app.ActivityThread")
// 反射获取ActivityThread实例
private val activityThreadInstance by lazy
activityThreadClass.getDeclaredMethod("currentActivityThread").invoke(null)!!
// 获取ActivityThread中的mService对象
private val activityThreadServices by lazy
val mServicesField =
activityThreadClass.getDeclaredField("mServices").apply isAccessible = true
mServicesField.get(activityThreadInstance) as Map<IBinder, Service>
fun install()
try
swapActivityThreadHandlerCallback mCallback ->
// 创建一个Handler的Callback对象
Handler.Callback msg ->
if (msg.what == STOP_SERVICE)
val key = msg.obj as IBinder
// 根据key(token)从mService中获取Service对象
activityThreadServices[key]?.let
onServicePreDestroy(key, it)
mCallback?.handleMessage(msg) ?: false
swapActivityManager activityManagerInterface, activityManagerInstance ->
// 动态代理
Proxy.newProxyInstance(
activityManagerInterface.classLoader, arrayOf(activityManagerInterface)
) _, method, args ->
if (METHOD_SERVICE_DONE_EXECUTING == method.name)
val token = args!![0] as IBinder
if (servicesToBeDestroyed.containsKey(token))
// 当调用AMS的serviceDoneExecuting方法时执行onServiceDestroyed()
onServiceDestroyed(token)
try
if (args == null)
method.invoke(activityManagerInstance)
else
method.invoke(activityManagerInstance, *args)
catch (invocationException: InvocationTargetException)
throw invocationException.targetException
catch (ignored: Throwable)
LogUtil.e("Could not watch destroyed services")
private fun onServicePreDestroy(
token: IBinder,
service: Service
)
LogUtil.e("onServicePreDestroy")
servicesToBeDestroyed[token] = WeakReference(service)
private fun onServiceDestroyed(token: IBinder)
servicesToBeDestroyed.remove(token)?.also serviceWeakReference ->
serviceWeakReference.get()?.let service ->
LogUtil.e("$service::class.java.name received Service#onDestroy() callback")
/**
* hook ActivityThread的handler
*/
private fun swapActivityThreadHandlerCallback(swap: (Handler.Callback?) -> Handler.Callback?)
val mHField =
activityThreadClass.getDeclaredField("mH").apply isAccessible = true
val mH = mHField[activityThreadInstance] as Handler
val mCallbackField =
Handler::class.java.getDeclaredField("mCallback").apply isAccessible = true
// 获取mH的mCallback
val mCallback = mCallbackField[mH] as Handler.Callback?
// 替换mH的callback对象
mCallbackField[mH] = swap(mCallback)
/**
* hook ams binder proxy
*/
private fun swapActivityManager(swap: (Class<*>, Any) -> Any)
val singletonClass = Class.forName("android.util.Singleton")
val mInstanceField =
singletonClass.getDeclaredField("mInstance").apply isAccessible = true
val singletonGetMethod = singletonClass.getDeclaredMethod("get")
val (className, fieldName) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
"android.app.ActivityManager" to "IActivityManagerSingleton"
else
"android.app.ActivityManagerNative" to "gDefault"
val activityManagerClass = Class.以上是关于LeakCanary可能被你忽略的点的主要内容,如果未能解决你的问题,请参考以下文章
如何使用模块化代码片段中的LeakCanary检测内存泄漏?