Primero, hablemos sobre el uso de LeakCanary y luego vayamos a la capa inferior para analizar la lógica del código fuente.
Cómo usar la nueva versión de kotlin
dependencies {
// debugImplementation because LeakCanary should only run in debug builds.
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1'
}
Solo un paso y listo.
Qué fugas se monitorean por defecto
Según el sitio web oficial, no hay una dependencia intrusiva y el monitoreo de pérdida de memoria de los siguientes módulos se inyectará automáticamente
LeakCanary detecta automáticamente fugas de los siguientes objetos:
destruido Instancias de actividad
destruidas Fragmento instancias
destruido fragmento Ver instancias
borradas ViewModel instancias
flujo de trabajo general
Pasará por los siguientes 4 pasos para hacer todo el trabajo.
- Detección de objetos retenidos Seguimiento de objetos retenidos que no han sido reciclados
- Volcar el montón.
- Analizando el montón Analizando el montón
- Categorización de fugas. Las fugas de montón se dividen en
Supervisar objetos no recogidos
Cuando esté visible en primer plano, comenzará a volcar cuando detecte que hay 5 objetos sin reciclar
Cuando no está visible en segundo plano, comenzará a volcar cuando detecte que hay algún objeto que no ha sido reciclado.
Se monitorea cada 2 segundos y el ciclo de volcado es cada 5 segundos.
montón de volcado
Cuando se alcance el umbral anterior, se activará el volcado y
se generará el archivo .hprof
Analizar montón
Ahora lo analiza Shark
Clasificación de fugas
##############################
Ingrese al tema y rompa el código fuente.
Algunos proveedores y actividades se inyectarán automáticamente en el archivo compilado.
1: ProcessLifecycleOwnerInitializer
androidx.lifecycle.ProcessLifecycleOwnerInitializer
Este es un ContentProvider que viene con Android
En el método onCreate, se realizan principalmente dos operaciones
LifecycleDispatcher.init(getContext());
ProcessLifecycleOwner.init(getContext());
1.1: LifecycleDispatcher
//底层会在application中把这个callback纳入application的维护范畴内
((Application) context.getApplicationContext())
.registerActivityLifecycleCallbacks(new DispatcherActivityCallback());
Prestar atención a DispatcherActivityCallback en realidad hizo una cosa
@VisibleForTesting
static class DispatcherActivityCallback extends EmptyActivityLifecycleCallbacks {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
//核心就是这句了.
ReportFragment.injectIfNeededIn(activity);
}
..........省略.......................
}
La devolución de llamada anterior es una interfaz en la aplicación, y se mantiene una ArrayList en la aplicación
@UnsupportedAppUsage
private ArrayList<ActivityLifecycleCallbacks> mActivityLifecycleCallbacks =
new ArrayList<ActivityLifecycleCallbacks>();
1.2: propietario del ciclo de vida del proceso
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);
}
}
.......省略.......
});
}
**Resumen: Las dos devoluciones de llamada de registro del ciclo de vida anteriores se procesan en última instancia en la clase Aplicación.
public void registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) {
synchronized (mActivityLifecycleCallbacks) {
mActivityLifecycleCallbacks.add(callback);
}
}
public void unregisterActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) {
synchronized (mActivityLifecycleCallbacks) {
mActivityLifecycleCallbacks.remove(callback);
}
}
2: FugaCanaryFileProvider
leakcanary.internal.LeakCanaryFileProvider
No entendí esta clase, pero probablemente significa que se usa cuando se opera la clase de archivo.
3: MainProcessAppWatcherInstaller
leakcanary.internal.MainProcessAppWatcherInstaller
Esta clase también está integrada con ContentProvider, que reemplaza la instalación manual de la versión anterior de LeackCanary.En el método onCreate de esta clase, se realizarán automáticamente las siguientes operaciones [Miraculous Pen]
override fun onCreate(): Boolean {
val application = context!!.applicationContext as Application
AppWatcher.manualInstall(application)
return true
}
3.1: Código central
@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: Carga reflexiva de 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
}
}
El parámetro pasado anteriormente como aplicación se ejecutará en el método de invocación
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()
)
}
}
}
}
En resumen: a través del mecanismo único de ContentProvider en Android, la operación de instalación se activa automáticamente y, en la operación de instalación, se ejecuta a través del reflejo de la clase.
- Compruebe si solo está habilitado el modo de depuración
- Crea un objeto Watcher y escucha
- Crear un colector de GC
- Crear un subproceso asíncrono de fondo
- Supervise si la aplicación es visible o no, y llame a diferentes estrategias de umbral para volcar el montón
- Crear un icono de acceso directo en el escritorio
3.3: Inyección automática de diferentes oyentes
En el registro de AppWatcher, las últimas 3 líneas tienen acciones muy críticas, como sigue
watchersToInstall.forEach {
it.install()
}
watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)
Los Cuatro King Kong debutan oficialmente
fun appDefaultWatchers(
application: Application,
reachabilityWatcher: ReachabilityWatcher = objectWatcher
): List<InstallableWatcher> {
return listOf(
ActivityWatcher(application, reachabilityWatcher),
FragmentAndViewModelWatcher(application, reachabilityWatcher),
RootViewWatcher(reachabilityWatcher),
ServiceWatcher(reachabilityWatcher)
)
}
Muchos amigos informaron que la versión anterior de LeakCanary tiene funciones limitadas, solo monitorea Actividad y Fragmento, y no puede monitorear Servicio. Esta vez está arreglado.
3.3.1: Monitor de actividad
Heredar la interfaz InstallableWatcher tiene solo dos métodos, instalar y desinstalar, declarando una variable global para realizar las siguientes operaciones
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
La capa inferior aquí en realidad depende de la Actividad, y también está subdividida y es compatible con el siguiente
Android8.0 y superior
Fragmento de la serie Android x
Serie de soporte de Android de fragmentos
//定义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
}
Uso de 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 es diferente al anterior
Está escrito de la misma manera que AndroidOFragmentDestroyWatcher, lo único es que el paquete de fragmentos importado es diferente y hay dos métodos más reescritos para el procesamiento.
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)
}
}
A través de ViewModel en 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
Igual que AndroidOFragmentDestroyWatcher, lo único es que el paquete Fragment al que se hace referencia es diferente
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentActivity
import android.support.v4.app.FragmentManager
En resumen, cuando se procesa fragmentWatcher, se maneja distinguiendo la compatibilidad y, finalmente, a través de fragmentManager en la actividad de la que depende el fragmento para la gestión del ciclo de vida.
3.3.3: RootViewWatcher
Principalmente relacionado con la Vista, la premisa es que la Vista no está adjunta a la Actividad/ventana emergente, y si la configuración en el proyecto admite ventanas emergentes
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: Observador de servicios
asociación de referencia débil
private val servicesToBeDestroyed = WeakHashMap<IBinder, WeakReference<Service>>()
creación de reflexión
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>
}
El código fuente central del servicio no se ha entendido por el momento, y probablemente se sepa que hay una reflexión llamando a IActivityManager
Pegue el código fuente central en el método de instalación
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" }
}
En resumen, sabemos por qué la nueva versión de leakCanary solo necesita depender de él, porque lo anterior se procesa automáticamente.
4: luego analice el área del montón de volcado
Mantenemos una clase ObjectWatcher en la clase AppWatcher
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: En la clase AppWatcher de la siguiente manera
@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 }
El controlador mencionado anteriormente es el controlador del hilo principal
internal val mainHandler by lazy { Handler(Looper.getMainLooper()) }
En el método de ejecución de invocación de InternalLeakCanary, hay una operación para enviar una notificación al monitorear
5: Receptor de notificaciones
Esta clase es principalmente responsable de la operación de recibir el evento de transmisión DUMP_HEAP,
5.1: En el paso 3.3 anterior, puede ver el procesamiento de envío de transmisión
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 En la clase HenapDumpControl
fun iCanHasHeap(): ICanHazHeap {
........省略代码.........
synchronized(this) {
if (::latest.isInitialized && dumpHeap is Yup && latest is Nope) {
//dump的调度处理
InternalLeakCanary.scheduleRetainedObjectCheck()
}
latest = dumpHeap
}
return dumpHeap
}
5.3: En INternalLeakCanary
fun scheduleRetainedObjectCheck() {
if (this::heapDumpTrigger.isInitialized) {
heapDumpTrigger.scheduleRetainedObjectCheck()
}
}
5.4 En 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 Procesamiento de recepción en transmisión
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: Procesamiento de recepción de volcados en la clase InternalLeakCanary
fun onDumpHeapReceived(forceDump: Boolean) {
if (this::heapDumpTrigger.isInitialized) {
heapDumpTrigger.onDumpHeapReceived(forceDump)
}
}
##5.8 Procesamiento en 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 Procesamiento dumpHeap
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 Recopilación de eventos en la clase LeakCanary
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)
}
Finalmente, se llaman varias clases de datos HeapAnalysisFailure HeapAnalysisSuccess en HeapAnalysis, y el método toString() dentro es el registro de error empalmado que vimos en LeakCanary.
Código de la sección de resumen
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 a 5.10.1 con menos pasos
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)
}
}
}
Arriba: el volcado y el análisis de monitoreo de dependencia de LeakCanary se aclaran básicamente.
6 FontaneroInstalador
leakcanary.internal.PlomberInstaller
Además, agregue alimentos para complementar esto. La función de esta clase es principalmente reflejar si las diferentes versiones y los diferentes fabricantes de teléfonos móviles admiten algunas API.
Principalmente para diferentes modelos y diferentes versiones, algunos escenarios especiales causarán fugas, que se establecerán manualmente en nulo cuando la actividad se destruya para facilitar el reciclaje.
AndroidLeakFixes.applyFixes(application)
Este análisis se realiza en segundo plano por un solo grupo de subprocesos
Executors.newSingleThreadScheduledExecutor
Por ejemplo, por ejemplo, hay una versión para dispositivos Samsung que no está entre 19 y 21
Reflejando el campo mLastHoveredView en 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" }
}
}
}
}
},
Actividad de fuga
actividad.interna.de.leakcanary.LeakActivity
Abrimos el icono canario para mostrar esta Actividad.
Importar el archivo .hprof
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
Se utiliza principalmente para solicitar un permiso.
- Desde la perspectiva del modo de diseño, se utiliza el modo de fábrica (Wacher y analizador)
- Utilice inteligentemente la función ContentProvider para lograr un acceso de código de una línea no intrusivo
- En comparación con la versión anterior, se ha agregado el monitoreo de RootView y Service
Nota: Este anuncio oficial solo se puede realizar en modo de depuración.
Lo anterior es el análisis general.