那么AppWatcherInstaller
的onCreate
的方法是在何时调用的?其实就是ContentProvider
是在何时初始化的。它其实是在ActivityThread
的handleBindApplication
的方法中。
private void handleBindApplication(AppBindData data) {
…
// don’t bring up providers in restricted mode; they may depend on the
// app’s custom Application class
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
installContentProviders(app, data.providers);
}
}
// application 初始化
mInstrumentation.callApplicationOnCreate(app);
…
}
通过上述代码可知,ContentProvider
的onCreate
方法要早于Application
的初始化,而LeakCanary
的初始化正是在ContentProvider
初始化时进行的,也就是说它帮助我们自动执行了初始化的过程。
需要注意的是,使用这种方式进行初始化的时候,不可以执行耗时代码,因为ContentProvider
的初始化也是在主线程进行了,否则就会导致app启动速度变慢。
LeakCanary检测原理
- LeakCanary进行内存泄漏检测主要是以下几个步骤:
-
获取可能泄漏的对象
-
生成
.hprof
文件 -
分析
.hprof
文件,并进行提示
- LeakCanary检测的对象
-
Activity
-
Fragment和ViewModel
-
View
-
Service
fun appDefaultWatchers(
application: Application,
reachabilityWatcher: ReachabilityWatcher = objectWatcher
): List {
return listOf(
ActivityWatcher(application, reachabilityWatcher),
FragmentAndViewModelWatcher(application, reachabilityWatcher),
RootViewWatcher(reachabilityWatcher),
ServiceWatcher(reachabilityWatcher)
)
}
首先我们来看一下ObjectWatcher
,它的关键代码如下:
@Synchronized fun watch(
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)
SharkLog.d {
"Watching " +
(if (watchedObject is Class<*>) watchedObject.toString() else “instance of ${watchedObject.javaClass.name}”) +
(if (description.isNotEmpty()) " ($description)" else “”) +
" with key $key"
}
watchedObjects[key] = reference
checkRetainedExecutor.execute {
moveToRetained(key)
}
}
主要是对watchedObject
使用了弱引用,同时注意到里面使用了ReferenceQueue
,这两者结合使用可以实现如果弱引用关联的对象被回收,就会把这个弱引用加入到queue中,以此来判断该对象是否被回收。
LeakCanary
主要的检测对象是以上4种,以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) {
reachabilityWatcher.expectWeaklyReachable(
activity, “${activity::class.java.name} received Activity#onDestroy() callback”
)
}
}
}
在ActivityWatcher
中注册了ActivityLifecycleCallbacks
,同时在onActivityDestroyed
的时候,执行了一些操作,查看源码:
@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)
SharkLog.d {
"Watching " +
(if (watchedObject is Class<*>) watchedObject.toString() else “instance of ${watchedObject.javaClass.name}”) +
(if (description.isNotEmpty()) " ($description)" else “”) +
" with key $key"
}
watchedObjects[key] = reference
checkRetainedExecutor.execute {
moveToRetained(key)
}
}
上述代码的主要逻辑是:
-
移除弱可达的对象
-
将当前的
watchedObject
添加到KeyedWeakReference
当中 -
将
这个weakReference保存到数组中 -
在
checkRetainedExecutor
中执行moveToRetained
方法
根据removeWeaklyReachableObjects
方法中原理,如果这个对象除了由ObjectWatcher
所添加的WeakReference以外,没有其他对象在引用它了,那么这个对象也就可以回收了,watchedObjects
也就可以移除他了。
private fun removeWeaklyReachableObjects() {
var ref: KeyedWeakReference?
do {
ref = queue.poll() as KeyedWeakReference?
if (ref != null) {
watchedObjects.remove(ref.key)
}
} while (ref != null)
}
}
checkRetainedExecutor
其实是个单例对象,里面会通过handler来延迟5s
来执行方法。如果超过5s
则会触发LeakCanary
的泄漏检测机制。5s
只是个经验值应该,因为GC并不是实时发生,因而预留5s
交给GC操作。
触发了LeakCanary的泄漏检测之后,则会执行HeapDumpTrigger
的dumpHeap
方法,在获取到了.hprof
文件之后,调用HeapAnalyzerService.runAnalysis()
给出分析结果。 关于.hprof
文件的分析,不是本文重点,具体可以参考hprof文件协议。其分析基本也就是根据GC Root去寻找泄漏的对象,大体流程图如下。
在Android中常见的内存泄漏
单例
单例所导致的内存泄漏几乎是在android开发中最为常见的内存泄漏问题了。
public class Singleton {
private static Singleton singleton;
private Context context;
private Singleton(Context context) {
this.context = context;
}
public static Singleton getInstance(Context context) {
if (singleton == null) {
singleton = new Singleton(context);
}
return singleton;
}
}
在上面的代码中,如果在执行getInstance
方法的时候,传入的是activity的对象,那么该activity对象就没法被及时回收,导致内存泄漏,可以考虑传入ApplicationContext,或者把context放入到方法变量中。
非静态内部类(包括匿名内部类)
非静态内部类会默认持有外部类的引用,如果它的生命周期长于外部类时,就会导致内存泄漏。 在android开发,这种情况常常见于Handler的使用。
尽可能避免使用静态变量
class MainActivity : AppCompatActivity() {
companion object {
return singleton;
}
}
在上面的代码中,如果在执行getInstance
方法的时候,传入的是activity的对象,那么该activity对象就没法被及时回收,导致内存泄漏,可以考虑传入ApplicationContext,或者把context放入到方法变量中。
非静态内部类(包括匿名内部类)
非静态内部类会默认持有外部类的引用,如果它的生命周期长于外部类时,就会导致内存泄漏。 在android开发,这种情况常常见于Handler的使用。
尽可能避免使用静态变量
class MainActivity : AppCompatActivity() {
companion object {