First, let’s talk about the use of LeakCanary, and then go to the bottom layer to analyze the source code logic
How to use the new version of kotlin
dependencies {
// debugImplementation because LeakCanary should only run in debug builds.
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1'
}
Just one step and you're done.
Which leaks are monitored by default
According to the official website, there is no intrusive dependency, and the memory leak monitoring of the following modules will be automatically injected
LeakCanary automatically detects leaks of the following objects:
destroyed Activity instances
destroyed Fragment instances
destroyed fragment View instances
cleared ViewModel instances
overall workflow
It will go through the following 4 steps to do all the work.
- Detecting retained objects. Monitoring retained objects that have not been recycled
- Dumping the heap.
- Analyzing the heap. Analyzing the heap
- Categorizing leaks. Heap leaks are divided into
Monitor uncollected objects
When it is visible in the foreground, it will start dumping when it detects that there are 5 unrecycled objects
When it is not visible in the background, it will start dumping when it detects that there is an object that has not been recycled.
It is monitored every 2 seconds, and the dump cycle is every 5 seconds.
dump heap
When the above threshold is reached, the dump will be triggered and
the .hprof file will be generated
Analyze Heap
Now it is analyzed by Shark
Leak classification
################################
Step into the topic and smash the source code.
Some providers and activities will be automatically injected into the compiled file.
1: ProcessLifecycleOwnerInitializer
androidx.lifecycle.ProcessLifecycleOwnerInitializer
This is a ContentProvider that comes with Android
In the onCreate method, two operations are mainly done
LifecycleDispatcher.init(getContext());
ProcessLifecycleOwner.init(getContext());
1.1: LifecycleDispatcher
//底层会在application中把这个callback纳入application的维护范畴内
((Application) context.getApplicationContext())
.registerActivityLifecycleCallbacks(new DispatcherActivityCallback());
Paying attention to DispatcherActivityCallback actually did one thing
@VisibleForTesting
static class DispatcherActivityCallback extends EmptyActivityLifecycleCallbacks {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
//核心就是这句了.
ReportFragment.injectIfNeededIn(activity);
}
..........省略.......................
}
The above callback Callback is an interface in Application, and an ArrayList is maintained in Application
@UnsupportedAppUsage
private ArrayList<ActivityLifecycleCallbacks> mActivityLifecycleCallbacks =
new ArrayList<ActivityLifecycleCallbacks>();
1.2: ProcessLifecycleOwner
static void init(Context context) {
sInstance.attach(context);
}
void attach(Context context) {
mHandler = new Handler();
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
Application app = (Application) context.getApplicationContext();
//核心还是下面这行代码了 注册activity的生命周期回调
app.registerActivityLifecycleCallbacks(new EmptyActivityLifecycleCallbacks() {
@Override
public void onActivityPreCreated(@NonNull Activity activity,
@Nullable Bundle savedInstanceState) {
activity.registerActivityLifecycleCallbacks(new EmptyActivityLifecycleCallbacks() {
.......省略.......
onActivityPostStarted
.......省略.......
onActivityPostResumed
.......省略.......
});
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
if (Build.VERSION.SDK_INT < 29) {
ReportFragment.get(activity).setProcessListener(mInitializationListener);
}
}
.......省略.......
});
}
**Summary: The above two life cycle registration callbacks are ultimately processed in the Application class.
public void registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) {
synchronized (mActivityLifecycleCallbacks) {
mActivityLifecycleCallbacks.add(callback);
}
}
public void unregisterActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) {
synchronized (mActivityLifecycleCallbacks) {
mActivityLifecycleCallbacks.remove(callback);
}
}
2: LeakCanaryFileProvider
leakcanary.internal.LeakCanaryFileProvider
I didn’t understand this class, but it probably means that it is used when operating the file class.
3: MainProcessAppWatcherInstaller
leakcanary.internal.MainProcessAppWatcherInstaller
This class is also integrated with ContentProvider, which replaces the manual install of the old version of LeackCanary. In the onCreate method of this class, the following operations will be automatically performed [Miraculous Pen]
override fun onCreate(): Boolean {
val application = context!!.applicationContext as Application
AppWatcher.manualInstall(application)
return true
}
3.1: Core code
@JvmOverloads
fun manualInstall(
application: Application,
retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5),
watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)
) {
//校验当前是否在主线程 Looper.getMainLooper().thread === Thread.currentThread()
checkMainThread()
if (isInstalled) {
throw IllegalStateException(
"AppWatcher already installed, see exception cause for prior install call", installCause
)
}
check(retainedDelayMillis >= 0) {
"retainedDelayMillis $retainedDelayMillis must be at least 0 ms"
}
installCause = RuntimeException("manualInstall() first called here")
this.retainedDelayMillis = retainedDelayMillis
if (application.isDebuggableBuild) {
//debug模式 打开日志开关
LogcatSharkLog.install()
}
// Requires AppWatcher.objectWatcher to be set
LeakCanaryDelegate.loadLeakCanary(application)
watchersToInstall.forEach {
it.install()
}
}
3.2: Reflective loading of InternalLeakCanary
@Suppress("UNCHECKED_CAST")
val loadLeakCanary by lazy {
try {
val leakCanaryListener = Class.forName("leakcanary.internal.InternalLeakCanary")
leakCanaryListener.getDeclaredField("INSTANCE")
.get(null) as (Application) -> Unit
} catch (ignored: Throwable) {
NoLeakCanary
}
}
The parameter passed above As Application will be executed to the invoke method
internal object InternalLeakCanary : (Application) -> Unit, OnObjectRetainedListener {
..................省略................
override fun invoke(application: Application) {
_application = application
//校验是否开启了只在debug模式使用. 设计原理只给debug时候使用
checkRunningInDebuggableBuild()
//创建AppWatcher对象 同时设置监听
AppWatcher.objectWatcher.addOnObjectRetainedListener(this)
//GC回收工具
val gcTrigger = GcTrigger.Default
val configProvider = { LeakCanary.config }
//创建异步线程
val handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME)
handlerThread.start()
//异步线程用于后台服务
val backgroundHandler = Handler(handlerThread.looper)
heapDumpTrigger = HeapDumpTrigger(
application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger,
configProvider
)
//监听应用是否可见的状态 可见和不可见 retained的阈值不一样 5 ---1
application.registerVisibilityListener { applicationVisible ->
this.applicationVisible = applicationVisible
//通知更新 如果可能话这里会触发转储堆
heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)
}
//监听onResume onPause
registerResumedActivityListener(application)
//创建桌面快捷图标 点击直接进入LeakActivity
addDynamicShortcut(application)
mainHandler.post {
backgroundHandler.post {
SharkLog.d {
//校验是否可以dump 如果可以dump的话 则发送notification的广播
when (val iCanHasHeap = HeapDumpControl.iCanHasHeap()) {
is Yup -> application.getString(R.string.leak_canary_heap_dump_enabled_text)
is Nope -> application.getString(
R.string.leak_canary_heap_dump_disabled_text, iCanHasHeap.reason()
)
}
}
}
}
To sum up: Through the unique mechanism of ContentProvider in Android, the install operation is automatically triggered, and in the install operation, it is executed through the reflection of the class.
- Check if only debug mode is enabled
- Create a Watcher object and listen
- Create a GC collector
- Create a background asynchronous thread
- Monitor whether the application is visible or not, and call different threshold strategies to dump heap
- Create a desktop shortcut icon
3.3: Automatic injection of different listeners
In the registration of AppWatcher, the last 3 lines have very critical actions, as follows
watchersToInstall.forEach {
it.install()
}
watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)
The Four King Kong officially debut
fun appDefaultWatchers(
application: Application,
reachabilityWatcher: ReachabilityWatcher = objectWatcher
): List<InstallableWatcher> {
return listOf(
ActivityWatcher(application, reachabilityWatcher),
FragmentAndViewModelWatcher(application, reachabilityWatcher),
RootViewWatcher(reachabilityWatcher),
ServiceWatcher(reachabilityWatcher)
)
}
Many friends reported that the old version of LeakCanary has limited functions, only monitors Activity and Fragment, and cannot monitor Service. This time it is arranged.
3.3.1: ActivityWatcher
Inheriting the InstallableWatcher interface has only two methods, install and unInstall, by declaring a global variable to do the following operations
private val lifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityDestroyed(activity: Activity) {
//watchObject 和 description 这个描述会Log日志
reachabilityWatcher.expectWeaklyReachable(
activity, "${activity::class.java.name} received Activity#onDestroy() callback"
)
}
}
override fun install() {
application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
}
override fun uninstall() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
}
3.3.2: FragmentAndViewModelWatcher
The bottom layer here actually depends on the Activity, and it is also subdivided and compatible with the following
Android8.0 and above
Fragment of Android x series
Android support series of fragments
//定义List<Activity>的集合
private val fragmentDestroyWatchers: List<(Activity) -> Unit> = run {
val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()
//大于等于8.0版本的AndroidOFragmentDestroyWatcher
if (SDK_INT >= O) {
fragmentDestroyWatchers.add(
AndroidOFragmentDestroyWatcher(reachabilityWatcher)
)
}
//AndroidX 里的Fragment
getWatcherIfAvailable(
ANDROIDX_FRAGMENT_CLASS_NAME,
ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
reachabilityWatcher
)?.let {
fragmentDestroyWatchers.add(it)
}
//support系列里的Fragment
getWatcherIfAvailable(
ANDROID_SUPPORT_FRAGMENT_CLASS_NAME,
ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
reachabilityWatcher
)?.let {
fragmentDestroyWatchers.add(it)
}
fragmentDestroyWatchers
}
Use of fragmentDestroyWatchers
private val lifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityCreated(
activity: Activity,
savedInstanceState: Bundle?
) {
for (watcher in fragmentDestroyWatchers) {
watcher(activity)
}
}
}
3.3.2.1: AndroidOFragmentDestroyWatcher
import android.app.Fragment
import android.app.FragmentManager
private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {
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"
)
}
}
//底层执行 通过寄存的Activity获取到对应的FragmentManager 设置生命周期回调
override fun invoke(activity: Activity) {
val fragmentManager = activity.fragmentManager
fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
}
3.3.2.2: AndroidXFragmentDestroyWatcher is different from the above
It is written in the same way as AndroidOFragmentDestroyWatcher, the only thing is that the imported fragment package is different, and there are two more rewritten methods for processing
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {
//比androidOFragmentDestroyWatcher多了下面这些处理
override fun onFragmentCreated(
fm: FragmentManager,
fragment: Fragment,
savedInstanceState: Bundle?
) {
ViewModelClearedWatcher.install(fragment, reachabilityWatcher)
}
override fun onFragmentViewDestroyed......省略同AndroidOFragmentDestroyWatcher里..... }
override fun onFragmentDestroyed......省略同AndroidOFragmentDestroyWatcher里..... }
override fun invoke(activity: Activity) {
if (activity is FragmentActivity) {
val supportFragmentManager = activity.supportFragmentManager
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
//比androidOFragmentDestroyWatcher多了下面这一行
ViewModelClearedWatcher.install(activity, reachabilityWatcher)
}
}
Through the ViewModel in Androidx
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)
}
}
3.3.2.3: AndroidSupportFragmentDestroyWatcher
Same as AndroidOFragmentDestroyWatcher, the only thing is that the referenced Fragment package is different
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentActivity
import android.support.v4.app.FragmentManager
To sum up, when fragmentWatcher is processed, it is handled by distinguishing compatibility, and finally through the fragmentManager in the Activity that the Fragment depends on for life cycle management.
3.3.3: RootViewWatcher
Mainly deal with View related, the premise is that View is not attached to Activity/popWindow, and whether the configuration in the project supports pop-up windows
private val listener = OnRootViewAddedListener { rootView ->
val trackDetached = when(rootView.windowType) {
PHONE_WINDOW -> {
when (rootView.phoneWindow?.callback?.wrappedCallback) {
// Activities are already tracked by ActivityWatcher
//如果是依附于activity的就不处理
is Activity -> false
//如果是弹窗里的 则根据配置文件来觉得
is Dialog -> {
// Use app context resources to avoid NotFoundException
// https://github.com/square/leakcanary/issues/2137
val resources = rootView.context.applicationContext.resources
resources.getBoolean(R.bool.leak_canary_watcher_watch_dismissed_dialogs)
}
// Probably a DreamService
else -> true
}
}
// Android widgets keep detached popup window instances around.
//依赖于pop window的也不处理
POPUP_WINDOW -> false
TOOLTIP, TOAST, UNKNOWN -> true
}
//可溯源追踪的就执行如下
if (trackDetached) {
rootView.addOnAttachStateChangeListener(object : OnAttachStateChangeListener {
val watchDetachedView = Runnable {
reachabilityWatcher.expectWeaklyReachable(
rootView, "${rootView::class.java.name} received View#onDetachedFromWindow() callback"
)
}
override fun onViewAttachedToWindow(v: View) {
mainHandler.removeCallbacks(watchDetachedView)
}
override fun onViewDetachedFromWindow(v: View) {
mainHandler.post(watchDetachedView)
}
})
}
}
3.3.4: ServiceWatcher
weak reference association
private val servicesToBeDestroyed = WeakHashMap<IBinder, WeakReference<Service>>()
reflection creation
private val activityThreadClass by lazy { Class.forName("android.app.ActivityThread") }
private val activityThreadInstance by lazy {
activityThreadClass.getDeclaredMethod("currentActivityThread").invoke(null)!!
}
private val activityThreadServices by lazy {
val mServicesField =
activityThreadClass.getDeclaredField("mServices").apply { isAccessible = true }
@Suppress("UNCHECKED_CAST")
mServicesField[activityThreadInstance] as Map<IBinder, Service>
}
The core source code of the service has not been understood for the time being, and it is probably known that there is reflection calling IActivityManager
Paste the core source code in the install method
try {
swapActivityThreadHandlerCallback { mCallback ->
uninstallActivityThreadHandlerCallback = {
swapActivityThreadHandlerCallback {
mCallback
}
}
Handler.Callback { msg ->
// https://github.com/square/leakcanary/issues/2114
// On some Motorola devices (Moto E5 and G6), the msg.obj returns an ActivityClientRecord
// instead of an IBinder. This crashes on a ClassCastException. Adding a type check
// here to prevent the crash.
if (msg.obj !is IBinder) {
return@Callback false
}
if (msg.what == STOP_SERVICE) {
val key = msg.obj as IBinder
activityThreadServices[key]?.let {
onServicePreDestroy(key, it)
}
}
mCallback?.handleMessage(msg) ?: false
}
}
swapActivityManager { activityManagerInterface, activityManagerInstance ->
uninstallActivityManager = {
swapActivityManager { _, _ ->
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)) {
onServiceDestroyed(token)
}
}
try {
if (args == null) {
method.invoke(activityManagerInstance)
} else {
method.invoke(activityManagerInstance, *args)
}
} catch (invocationException: InvocationTargetException) {
throw invocationException.targetException
}
}
}
} catch (ignored: Throwable) {
SharkLog.d(ignored) { "Could not watch destroyed services" }
}
In summary, we know why the new version of leakCanary only needs to depend on it. Because the above is automatically processed.
4: Then analyze the dump heap area
We maintain an ObjectWatcher class in the AppWatcher class
class ObjectWatcher constructor(
private val clock: Clock,
private val checkRetainedExecutor: Executor,
/**
* Calls to [watch] will be ignored when [isEnabled] returns false
*/
private val isEnabled: () -> Boolean = { true }
) : ReachabilityWatcher {
4.1: In the AppWatcher class as follows
@Volatile
var retainedDelayMillis = RETAINED_DELAY_NOT_SET
val objectWatcher = ObjectWatcher(
clock = { SystemClock.uptimeMillis() },
checkRetainedExecutor = {
check(isInstalled) {
"AppWatcher not installed"
}
//重要操作 发送延迟操作的任务
mainHandler.postDelayed(it, retainedDelayMillis)
},//传
isEnabled = { true }
),
isEnabled = { true }
The handler mentioned above is the handler of the main thread
internal val mainHandler by lazy { Handler(Looper.getMainLooper()) }
In the invoke execution method of InternalLeakCanary, there is an operation to send Notification when monitoring
5: NotificationReceiver
This class is mainly responsible for the operation of receiving the broadcast event DUMP_HEAP,
5.1: In the above step 3.3, you can see the processing of sending broadcast
backgroundHandler.post {
SharkLog.d {
//校验是否可以dump 如果可以dump的话 则发送notification的广播
when (val iCanHasHeap = HeapDumpControl.iCanHasHeap()) {
is Yup -> application.getString(R.string.leak_canary_heap_dump_enabled_text)
is Nope -> application.getString(
R.string.leak_canary_heap_dump_disabled_text, iCanHasHeap.reason()
)
}
}
}
5.2 In the HenapDumpControl class
fun iCanHasHeap(): ICanHazHeap {
........省略代码.........
synchronized(this) {
if (::latest.isInitialized && dumpHeap is Yup && latest is Nope) {
//dump的调度处理
InternalLeakCanary.scheduleRetainedObjectCheck()
}
latest = dumpHeap
}
return dumpHeap
}
5.3: In INternalLeakCanary
fun scheduleRetainedObjectCheck() {
if (this::heapDumpTrigger.isInitialized) {
heapDumpTrigger.scheduleRetainedObjectCheck()
}
}
5.4 In HeapDumpTrigger
fun scheduleRetainedObjectCheck(
delayMillis: Long = 0L
) {
val checkCurrentlyScheduledAt = checkScheduledAt
if (checkCurrentlyScheduledAt > 0) {
return
}
checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
backgroundHandler.postDelayed({
checkScheduledAt = 0
//检查是否需要dump的地方 根据需要的时候就发送广播出去
checkRetainedObjects()
}, delayMillis)
}
5.6 Receive processing in broadcast
override fun onReceive(
context: Context,
intent: Intent
) {
when (intent.action) {
DUMP_HEAP.name -> {
//具体的执行看下面5.7
InternalLeakCanary.onDumpHeapReceived(forceDump = false)
}
CANCEL_NOTIFICATION.name -> {
// Do nothing, the notification has auto cancel true.
}
else -> {
SharkLog.d { "NotificationReceiver received unknown intent action for $intent" }
}
}
}
5.7: Dump reception processing in the InternalLeakCanary class
fun onDumpHeapReceived(forceDump: Boolean) {
if (this::heapDumpTrigger.isInitialized) {
heapDumpTrigger.onDumpHeapReceived(forceDump)
}
}
##5.8 Processing in HeapDumpTrigger
fun onDumpHeapReceived(forceDump: Boolean) {
backgroundHandler.post {
//取消notify提示
dismissNoRetainedOnTapNotification()
//手动执行GC 底层调用 Runtime.getRuntime().gc()
gcTrigger.runGc()
val retainedReferenceCount = objectWatcher.retainedObjectCount
if (!forceDump && retainedReferenceCount == 0) {
....省略代码.......
return@post
}
SharkLog.d { "Dumping the heap because user requested it" }
//重要操作
dumpHeap(retainedReferenceCount, retry = false, "user request")
}
}
5.9 dumpHeap processing
private fun dumpHeap(
retainedReferenceCount: Int,
retry: Boolean,
reason: String
) {
//创建存储dump 文件的 目录
val directoryProvider =
InternalLeakCanary.createLeakDirectoryProvider(InternalLeakCanary.application)
//在dump文件夹创建新的dump文件 目录context.cacheDir
val heapDumpFile = directoryProvider.newHeapDumpFile()
val durationMillis: Long
try {
//发送事件 当前的事件唯一id
InternalLeakCanary.sendEvent(DumpingHeap(currentEventUniqueId))
if (heapDumpFile == null) {
throw RuntimeException("Could not create heap dump file")
}
saveResourceIdNamesToMemory()
val heapDumpUptimeMillis = SystemClock.uptimeMillis()
//UUID为key 的一个弱引用因
KeyedWeakReference.heapDumpUptimeMillis = heapDumpUptimeMillis
durationMillis = measureDurationMillis {
configProvider().heapDumper.dumpHeap(heapDumpFile)
}
if (heapDumpFile.length() == 0L) {
throw RuntimeException("Dumped heap file is 0 byte length")
}
lastDisplayedRetainedObjectCount = 0
lastHeapDumpUptimeMillis = SystemClock.uptimeMillis()
objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)
currentEventUniqueId = UUID.randomUUID().toString()
InternalLeakCanary.sendEvent(HeapDump(currentEventUniqueId, heapDumpFile, durationMillis, reason))
5.10 Event collection in LeakCanary class
val eventListeners: List<EventListener> = listOf(
LogcatEventListener,
ToastEventListener,
LazyForwardingEventListener {
if (InternalLeakCanary.formFactor == TV) TvEventListener else NotificationEventListener
},
when {
RemoteWorkManagerHeapAnalyzer.remoteLeakCanaryServiceInClasspath ->
RemoteWorkManagerHeapAnalyzer
WorkManagerHeapAnalyzer.workManagerInClasspath -> WorkManagerHeapAnalyzer
else -> BackgroundThreadHeapAnalyzer
}
),
5.10.1: RemoteWorkManagerHeapAnalyzer
override fun onEvent(event: Event) {
if (event is HeapDump) {
val application = InternalLeakCanary.application
val heapAnalysisRequest =
OneTimeWorkRequest.Builder(RemoteHeapAnalyzerWorker::class.java).apply {
val dataBuilder = Data.Builder()
.putString(ARGUMENT_PACKAGE_NAME, application.packageName)
.putString(ARGUMENT_CLASS_NAME, REMOTE_SERVICE_CLASS_NAME)
setInputData(event.asWorkerInputData(dataBuilder))
with(WorkManagerHeapAnalyzer) {
addExpeditedFlag()
}
}.build()
SharkLog.d { "Enqueuing heap analysis for ${event.file} on WorkManager remote worker" }
val workManager = WorkManager.getInstance(application)
//入栈 执行workQueue
workManager.enqueue(heapAnalysisRequest)
}
Finally, several data classes HeapAnalysisFailure HeapAnalysisSuccess in HeapAnalysis are called, and the toString() method inside is the spliced error record we saw in LeakCanary.
Summary section code
data class HeapAnalysisFailure(
override val heapDumpFile: File,
override val createdAtTimeMillis: Long,
override val dumpDurationMillis: Long = DUMP_DURATION_UNKNOWN,
override val analysisDurationMillis: Long,
/**
* An exception wrapping the actual exception that was thrown.
*/
val exception: HeapAnalysisException
) : HeapAnalysis() {
override fun toString(): String {
return """====================================
HEAP ANALYSIS FAILED
You can report this failure at https://github.com/square/leakcanary/issues
Please provide the stacktrace, metadata and the heap dump file.
====================================
STACKTRACE
$exception====================================
METADATA
Build.VERSION.SDK_INT: ${androidSdkInt()}
Build.MANUFACTURER: ${androidManufacturer()}
LeakCanary version: ${leakCanaryVersion()}
Analysis duration: $analysisDurationMillis ms
Heap dump file path: ${heapDumpFile.absolutePath}
Heap dump timestamp: $createdAtTimeMillis
===================================="""
}
5.10.2: WorkManagerHeapAnalyzer
Similar to 5.10.1 with fewer steps
override fun onEvent(event: Event) {
if (event is HeapDump) {
val heapAnalysisRequest = OneTimeWorkRequest.Builder(HeapAnalyzerWorker::class.java).apply {
setInputData(event.asWorkerInputData())
addExpeditedFlag()
}.build()
SharkLog.d { "Enqueuing heap analysis for ${event.file} on WorkManager remote worker" }
val application = InternalLeakCanary.application
WorkManager.getInstance(application).enqueue(heapAnalysisRequest)
}
}
5.10.3: BackgroundThreadHeapAnalyzer
override fun onEvent(event: Event) {
if (event is HeapDump) {
heapAnalyzerThreadHandler.post {
val doneEvent = AndroidDebugHeapAnalyzer.runAnalysisBlocking(event) { event ->
InternalLeakCanary.sendEvent(event)
}
InternalLeakCanary.sendEvent(doneEvent)
}
}
}
Above: LeakCanary's dependency monitoring dump and analysis are basically clarified.
6 PlumberInstaller
leakcanary.internal.PlumberInstaller
In addition, add food to supplement this. The function of this class is mainly to reflect whether different versions and different mobile phone manufacturers support some APIs.
Mainly for different models and different versions, some special scenarios will cause Leak, manually set to null when the Activity is destroyed to facilitate recycling.
AndroidLeakFixes.applyFixes(application)
This analysis is performed in the background by a single thread pool
Executors.newSingleThreadScheduledExecutor
For example, for example, there is a version for Samsung devices that is not between 19 and 21
Reflecting the mLastHoveredView field in TextView
override fun apply(application: Application) {
if (MANUFACTURER != SAMSUNG || SDK_INT !in 19..21) {
return
}
backgroundExecutor.execute {
val field: Field
try {
field = TextView::class.java.getDeclaredField("mLastHoveredView")
field.isAccessible = true
} catch (ignored: Exception) {
SharkLog.d(ignored) { "Could not fix the $name leak" }
return@execute
}
application.onActivityDestroyed {
try {
field.set(null, null)
} catch (ignored: Exception) {
SharkLog.d(ignored) { "Could not fix the $name leak" }
}
}
}
}
},
LeakActivity
leakcanary.internal.activity.LeakActivity
We open the canary icon to show this Activity.
Import the .hprof file
private fun importHprof(fileUri: Uri) {
try {
contentResolver.openFileDescriptor(fileUri, "r")
?.fileDescriptor?.let { fileDescriptor ->
val inputStream = FileInputStream(fileDescriptor)
InternalLeakCanary.createLeakDirectoryProvider(this)
.newHeapDumpFile()
?.let { target ->
inputStream.use { input ->
target.outputStream()
.use { output ->
input.copyTo(output, DEFAULT_BUFFER_SIZE)
}
}
InternalLeakCanary.sendEvent(
HeapDump(
uniqueId = UUID.randomUUID().toString(),
file = target,
durationMillis = -1,
reason = "Imported by user"
)
)
}
}
} catch (e: IOException) {
SharkLog.d(e) { "Could not imported Hprof file" }
}
}
RequestStoragePermissionActivity
It is mainly used to apply for permission.
- From the perspective of design mode, the factory mode (Wacher and analyzer) is used
- Cleverly use the ContentProvider feature to achieve non-intrusive one-line code access
- Compared with the old version, the monitoring of RootView and Service has been added
Note: This official announcement can only be made in debug mode.
The above is the overall analysis.