自动安装
- 直接添加debug依赖
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.0'
- 自动安装的原理
ContentProvider
/**
* Content providers are loaded before the application class is created. [AppWatcherInstaller] is
* used to install [leakcanary.AppWatcher] on application start.
*/
internal sealed class AppWatcherInstaller : ContentProvider() {
override fun onCreate(): Boolean {
val application = context!!.applicationContext as Application
InternalAppWatcher.install(application)
return true
}
}
AMS的启动流程 :Application->attachBaseContext =====>ContentProvider->onCreate =====>Application->onCreate ContentProvider
-
安装的优化
默认是在APP的主进程的主线程安装,虽然只在Debug版本,仍然可能比较卡,2种方法可以优化,注意,不论怎样,都必须是在主线程初始化checkMainThread()
. 去掉自动安装,在另外一个进程初始化安装
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="leak_canary_watcher_auto_install">false</bool>
</resources>
/**
* The entry point API for using [ObjectWatcher] in an Android app. [AppWatcher.objectWatcher] is
* in charge of detecting retained objects, and [AppWatcher] is auto configured on app start to
* pass it activity and fragment instances. Call [ObjectWatcher.watch] on [objectWatcher] to
* watch any other object that you expect to be unreachable.
*/
object AppWatcher {
/**
* [AppWatcher] is automatically installed on main process start by
* [leakcanary.internal.AppWatcherInstaller] which is registered in the AndroidManifest.xml of
* your app. If you disabled [leakcanary.internal.AppWatcherInstaller] or you need AppWatcher
* or LeakCanary to run outside of the main process then you can call this method to install
* [AppWatcher].
*/
fun manualInstall(application: Application) = InternalAppWatcher.install(application)
}
google的StartUp框架
安装过程
核心逻辑是注册生命回调application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks)
,逻辑封装在ObjectWatcher
internal object InternalAppWatcher {
fun install(application: Application) {
SharkLog.logger = DefaultCanaryLog()
SharkLog.d { "Installing AppWatcher" }
checkMainThread()
if (this::application.isInitialized) {
return
}
InternalAppWatcher.application = application
val configProvider = { AppWatcher.config }
ActivityDestroyWatcher.install(application, objectWatcher, configProvider)
FragmentDestroyWatcher.install(application, objectWatcher, configProvider)
onAppWatcherInstalled(application)
}
以Activity为例
internal class ActivityDestroyWatcher private constructor(
private val objectWatcher: ObjectWatcher,
private val configProvider: () -> Config
) {
private val lifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityDestroyed(activity: Activity) {
if (configProvider().watchActivities) {
objectWatcher.watch(activity)
}
}
}
companion object {
fun install(
application: Application,
objectWatcher: ObjectWatcher,
configProvider: () -> Config
) {
val activityDestroyWatcher =
ActivityDestroyWatcher(objectWatcher, configProvider)
application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks)
}
}
}
监测逻辑
Activity的onDestroy()--->ObjectWatcher.watch()
/**
* [ObjectWatcher] can be passed objects to [watch]. It will create [KeyedWeakReference] instances
* that reference watches objects, and check if those references have been cleared as expected on
* the [checkRetainedExecutor] executor. If not, these objects are considered retained and
* [ObjectWatcher] will then notify the [onObjectRetainedListener] on that executor thread.
*
* [checkRetainedExecutor] is expected to run its tasks on a background thread, with a significant
* to give the GC the opportunity to identify weakly reachable objects.
*
* [ObjectWatcher] is thread safe.
*/
class ObjectWatcher constructor(
@Synchronized fun watch(
watchedObject: Any,
name: String
) {
//1、检查清除,利用引用队列的逻辑
removeWeaklyReachableObjects()
val key = UUID.randomUUID().toString()
val watchUptimeMillis = clock.uptimeMillis()
//2、弱引用,注册引用队列queue
val reference =
KeyedWeakReference(watchedObject, key, name, watchUptimeMillis, queue)
//3、mutableMapOf<String, KeyedWeakReference>()临时引用保存在map
watchedObjects[key] = reference
//4、延迟检测默认5秒之后再检查是否weakly reachable
checkRetainedExecutor.execute {
moveToRetained(key)
}
}
//会反复调用检测
private fun removeWeaklyReachableObjects() {
// WeakReferences are enqueued as soon as the object to which they point to becomes weakly reachable. This is before finalization or garbage collection has actually happened.
var ref: KeyedWeakReference?
do {
ref = queue.poll() as KeyedWeakReference?
if (ref != null) {
watchedObjects.remove(ref.key)
}
} while (ref != null)
}
//线程池,Default delay to 5 seconds
private val checkRetainedExecutor = Executor {
mainHandler.postDelayed(it, AppWatcher.config.watchDurationMillis)
}
// How long to wait before reporting a watched object as retained.
val watchDurationMillis: Long = TimeUnit.SECONDS.toMillis(5)
- 后台线程处理
moveToRetained
@Synchronized private fun moveToRetained(key: String) {
//反复检查
removeWeaklyReachableObjects()
val retainedRef = watchedObjects[key]
//5.仍然没有weakly reachable,交给子线程处理
if (retainedRef != null) {
retainedRef.retainedUptimeMillis = clock.uptimeMillis()
onObjectRetainedListeners.forEach { it.onObjectRetained() }
}
}
//最终的回调逻辑,封装在了HeapDumpTrigger类
internal class HeapDumpTrigger{
fun onObjectRetained() {
scheduleRetainedObjectCheck("found new object retained")
}
private fun scheduleRetainedObjectCheck(reason: String) {
if (checkScheduled) {
SharkLog.d { "Already scheduled retained check, ignoring ($reason)" }
return
}
checkScheduled = true
backgroundHandler.post {
checkScheduled = false
//5、后台检测线程开始工作
checkRetainedObjects(reason)
}
}
//伪代码,保留了核心逻辑
private fun checkRetainedObjects(reason: String) {
SharkLog.d { "Checking retained object because $reason" }
//获取retainedObjectCount的时候,会再调用removeWeaklyReachableObjects
var retainedReferenceCount = objectWatcher.retainedObjectCount
//1.手动gc一次
if (retainedReferenceCount > 0) {
gcTrigger.runGc()
//再调用removeWeaklyReachableObjects,更新数量
retainedReferenceCount = objectWatcher.retainedObjectCount
}
//2.如果数量小于5,而且APP可见,只显示APP通知,然后delay2秒,继续轮训检测
if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return
SharkLog.d { "Found $retainedReferenceCount retained references, dumping the heap" }
//3.Dumping the heap freezes the UI ,不能轻易的dump
//如果数量大于5或者APP不可见,开始Dumps the heap into a file
val heapDumpFile = heapDumper.dumpHeap()
//4开启单独的服务,解析hprof文件
HeapAnalyzerService.runAnalysis(application, heapDumpFile)
}
private fun checkRetainedCount(
retainedKeysCount: Int,
retainedVisibleThreshold: Int
): Boolean {
if (retainedKeysCount < retainedVisibleThreshold) {
if (applicationVisible || applicationInvisibleLessThanWatchPeriod) {
//通知栏显示有泄露的信息
showRetainedCountBelowThresholdNotification(retainedKeysCount, retainedVisibleThreshold)
//delay2秒继续轮训检测
scheduleRetainedObjectCheck(
"Showing retained objects notification", WAIT_FOR_OBJECT_THRESHOLD_MILLIS
)
return true
}
}
return false
}
}
/**
* When the app is visible, LeakCanary will wait for at least
* [retainedVisibleThreshold] retained instances before dumping the heap. Dumping the heap
* freezes the UI and can be frustrating for developers who are trying to work. This is
* especially frustrating as the Android Framework has a number of leaks that cannot easily
* be fixed.
*
* When the app becomes invisible, LeakCanary dumps the heap after
* [AppWatcher.Config.watchDurationMillis] ms.
*
* The app is considered visible if it has at least one activity in started state.
*
* A higher threshold means LeakCanary will dump the heap less often, therefore it won't be
* bothering developers as much but it could miss some leaks.
*
* Defaults to 5.
*/
val retainedVisibleThreshold: Int = 5
- 解析hprof文件