LeakCanary最新2.8.1版本源碼 原理分析 [2022年初kotlin版]

首先從LeakCanary的使用開始講,接著會到底層分析源碼邏輯

kotlin新版本如何使用

dependencies {
  // debugImplementation because LeakCanary should only run in debug builds.
  debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1'
}

只需要這樣一步就搞定了.

默認監(jiān)測哪些泄漏

官方網(wǎng)站的說明,無侵入式依賴,會自動給注入如下幾個模塊的內(nèi)存泄漏監(jiān)聽

LeakCanary automatically detects leaks of the following objects:

destroyed Activity instances
destroyed Fragment instances
destroyed fragment View instances
cleared ViewModel instances

整體工作流程

它會經(jīng)過下面4個步驟來完成所有的工作.

  • Detecting retained objects. 監(jiān)測保留未被回收的對象
  • Dumping the heap. 轉(zhuǎn)儲堆區(qū)
  • Analyzing the heap. 分析堆區(qū)
  • Categorizing leaks. 堆泄漏進行分來

監(jiān)測未被回收的對象

在前臺可見的時候,是監(jiān)聽到有5個未回收的對象就會開始dump

在后臺不可見的時候,是監(jiān)聽到有1個未被回收的對象就會開始dump.

2秒鐘監(jiān)測一次,dump的周期是5秒鐘一次.

轉(zhuǎn)儲堆

當達到上面的閾值情況下,就會觸發(fā)dump,
生成.hprof文件

分析堆區(qū)

現(xiàn)在是通過Shark來分析

泄漏分類

################################

步入正題,死磕源碼.

編譯后的文件里會有自動注入一些provider和activity.

如圖所示


1.png
2.png
3.png

1: ProcessLifecycleOwnerInitializer

androidx.lifecycle.ProcessLifecycleOwnerInitializer

這是安卓系統(tǒng)自帶的一個ContentProvider

在onCreate方法里主要做了2個操作

LifecycleDispatcher.init(getContext());
ProcessLifecycleOwner.init(getContext());

1.1: LifecycleDispatcher

//底層會在application中把這個callback納入application的維護范疇內(nèi)
((Application) context.getApplicationContext())
                .registerActivityLifecycleCallbacks(new DispatcherActivityCallback());

在注意看DispatcherActivityCallback中其實就做了一個事情

    @VisibleForTesting
    static class DispatcherActivityCallback extends EmptyActivityLifecycleCallbacks {

        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
           //核心就是這句了.
            ReportFragment.injectIfNeededIn(activity);
        }
..........省略.......................
    }

上面的回調(diào)Callback 是Application中的一個接口<ActivityLifecycleCallbacks>,同時Application中維護了一個ArrayList<ActivityLifecycleCallbacks>

 @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的生命周期回調(diào)
        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);
                }
            }
          .......省略.......
        });
    }

**總結(jié): 上面的2個生命周期注冊回調(diào) ,最終都是在Application類中處理的.

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

這個類我也沒看懂具體看嘛的, 大概意思就是操作file類時使用到.

3: MainProcessAppWatcherInstaller

leakcanary.internal.MainProcessAppWatcherInstaller

這個類也是集成了ContentProvider, 替代了以前老版本LeackCanary手動install, 在這個類的onCreate方法中會自動執(zhí)行如下操作[神來之筆]

 override fun onCreate(): Boolean {
    val application = context!!.applicationContext as Application
    AppWatcher.manualInstall(application)
    return true
  }

3.1: 核心代碼

 @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: 反射加載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
    }
  }

上面?zhèn)鲄s Application會執(zhí)行到invoke方法

internal object InternalLeakCanary : (Application) -> Unit, OnObjectRetainedListener {
..................省略................

override fun invoke(application: Application) {
    _application = application
 //校驗是否開啟了只在debug模式使用. 設計原理只給debug時候使用
    checkRunningInDebuggableBuild()
    //創(chuàng)建AppWatcher對象 同時設置監(jiān)聽
    AppWatcher.objectWatcher.addOnObjectRetainedListener(this)
    //GC回收工具
    val gcTrigger = GcTrigger.Default

    val configProvider = { LeakCanary.config }
    //創(chuàng)建異步線程
    val handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME)
    handlerThread.start()
    //異步線程用于后臺服務
    val backgroundHandler = Handler(handlerThread.looper)

    heapDumpTrigger = HeapDumpTrigger(
      application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger,
      configProvider
    )
    //監(jiān)聽應用是否可見的狀態(tài) 可見和不可見 retained的閾值不一樣  5 ---1
    application.registerVisibilityListener { applicationVisible ->
      this.applicationVisible = applicationVisible
      //通知更新 如果可能話這里會觸發(fā)轉(zhuǎn)儲堆
      heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)
    }
    //監(jiān)聽onResume onPause
    registerResumedActivityListener(application)
    //創(chuàng)建桌面快捷圖標 點擊直接進入LeakActivity
    addDynamicShortcut(application)
    
 
    mainHandler.post {
     backgroundHandler.post {
        SharkLog.d {
          //校驗是否可以dump  如果可以dump的話 則發(fā)送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()
            )
          }
        }
      }
    }

綜上所述:通過Android里ContentProvider的特有機制,自動觸發(fā)install操作,在install操作中再通過類的反射去invoke執(zhí)行.

  • 校驗是否只開啟debug模式使用
  • 創(chuàng)建Watcher對象并監(jiān)聽
  • 創(chuàng)建GC回收器
  • 創(chuàng)建后臺異步線程
  • 監(jiān)聽應用可見與否,并調(diào)用不同的閾值策略進行dump heap
  • 創(chuàng)建桌面快捷圖標

3.3: 不同的監(jiān)聽器自動注入

在AppWatcher的注冊里面最后3行 有很關鍵的動作,如下

watchersToInstall.forEach {
      it.install()
    }
watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)

四大金剛正式登場

  fun appDefaultWatchers(
    application: Application,
    reachabilityWatcher: ReachabilityWatcher = objectWatcher
  ): List<InstallableWatcher> {
    return listOf(
      ActivityWatcher(application, reachabilityWatcher),
      FragmentAndViewModelWatcher(application, reachabilityWatcher),
      RootViewWatcher(reachabilityWatcher),
      ServiceWatcher(reachabilityWatcher)
    )
  }

很多朋友反饋LeakCanary老版本功能有限,只監(jiān)聽Activity和Fragment, 不能監(jiān)聽Service, 這次給安排上了.

3.3.1: ActivityWatcher

繼承InstallableWatcher接口 只有install和unInstall2個方法, 通過聲明一個全局變量來做下面的操作

 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

這里面底層其實還是依賴于Activity, 同時還細分兼容為如下的
Android8.0及以上的

Android x系列的Fragment

Android support系列的fragment

//定義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
  }

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"
      )
    }
  }

//底層執(zhí)行 通過寄存的Activity獲取到對應的FragmentManager 設置生命周期回調(diào)
  override fun invoke(activity: Activity) {
    val fragmentManager = activity.fragmentManager
    fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
  }
3.3.2.2: AndroidXFragmentDestroyWatcher 和上面的有區(qū)別

和AndroidOFragmentDestroyWatcher寫法一樣,唯一就是導入的fragment包不一樣 ,以及多了2個重寫的方法處理

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)
    }
  }

通過Androidx 里的ViewModel

  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

和AndroidOFragmentDestroyWatcher一樣,唯一就是引用的Fragment包不一樣

import android.support.v4.app.Fragment
import android.support.v4.app.FragmentActivity
import android.support.v4.app.FragmentManager

綜上所述,在fragmentWatcher處理的時候由區(qū)分兼容處理,最終通過Fragment依賴的Activity中fragmentManager進行生命周期管理.

3.3.3: RootViewWatcher

主要處理View相關的,前提是View不依附于Activity/popWindow, 以及項目中配置的是否支持彈窗

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
          //如果是彈窗里的 則根據(jù)配置文件來覺得
          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
    }
    //可溯源追蹤的就執(zhí)行如下
    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

弱引用關聯(lián)

private val servicesToBeDestroyed = WeakHashMap<IBinder, WeakReference<Service>>()

反射創(chuàng)建

 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>
  }

service這里面的核心源碼暫時沒看懂, 大概能知道是就是有反射調(diào)用IActivityManager

install方法里的核心源碼貼一下

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" }
    }

綜上,我們知道 為什么新版本leakCanary只要依賴就行. 因為上面都是自動給處理了的.

4: 接著分析轉(zhuǎn)儲堆區(qū)

我們在AppWatcher類里面維護著一個ObjectWatcher類

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: 在 AppWatcher 類里面如下

  @Volatile
  var retainedDelayMillis = RETAINED_DELAY_NOT_SET
  
  
 val objectWatcher = ObjectWatcher(
    clock = { SystemClock.uptimeMillis() },
    checkRetainedExecutor = {
      check(isInstalled) {
        "AppWatcher not installed"
      }
      //重要操作 發(fā)送延遲操作的任務  
      mainHandler.postDelayed(it, retainedDelayMillis)
    },//傳
    isEnabled = { true }
  ),
    isEnabled = { true }

上面所提到的handler就是主線程的handler

internal val mainHandler by lazy { Handler(Looper.getMainLooper()) }

在InternalLeakCanary的invoke執(zhí)行方法里面,有監(jiān)測的時候發(fā)送Notification的操作

5: NotificationReceiver

這個類主要負責接收廣播事件 DUMP_HEAP的操作,

5.1: 在上面的3.3步驟中是可以看到發(fā)送廣播的處理

backgroundHandler.post {
        SharkLog.d {
          //校驗是否可以dump  如果可以dump的話 則發(fā)送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 在HenapDumpControl類中

fun iCanHasHeap(): ICanHazHeap {
    ........省略代碼.........

    synchronized(this) {
      if (::latest.isInitialized && dumpHeap is Yup && latest is Nope) {
        //dump的調(diào)度處理
        InternalLeakCanary.scheduleRetainedObjectCheck()
      }
      latest = dumpHeap
    }

    return dumpHeap
  }

5.3: 在INternalLeakCanary中

  fun scheduleRetainedObjectCheck() {
    if (this::heapDumpTrigger.isInitialized) {
      heapDumpTrigger.scheduleRetainedObjectCheck()
    }
  }

5.4 在HeapDumpTrigger中

fun scheduleRetainedObjectCheck(
    delayMillis: Long = 0L
  ) {
    val checkCurrentlyScheduledAt = checkScheduledAt
    if (checkCurrentlyScheduledAt > 0) {
      return
    }
    checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
    backgroundHandler.postDelayed({
      checkScheduledAt = 0
      //檢查是否需要dump的地方 根據(jù)需要的時候就發(fā)送廣播出去
      checkRetainedObjects()
    }, delayMillis)
  }

5.6 在廣播中接收處理

 override fun onReceive(
    context: Context,
    intent: Intent
  ) {
    when (intent.action) {
      DUMP_HEAP.name -> {
      //具體的執(zhí)行看下面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: InternalLeakCanary類中的Dump接收處理

 fun onDumpHeapReceived(forceDump: Boolean) {
    if (this::heapDumpTrigger.isInitialized) {
      heapDumpTrigger.onDumpHeapReceived(forceDump)
    }
  }

5.8 在HeapDumpTrigger中的處理

fun onDumpHeapReceived(forceDump: Boolean) {
    backgroundHandler.post {
      //取消notify提示
      dismissNoRetainedOnTapNotification()
      //手動執(zhí)行GC 底層調(diào)用 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的處理

 private fun dumpHeap(
    retainedReferenceCount: Int,
    retry: Boolean,
    reason: String
  ) {
    //創(chuàng)建存儲dump 文件的 目錄
    val directoryProvider =
      InternalLeakCanary.createLeakDirectoryProvider(InternalLeakCanary.application)
    //在dump文件夾創(chuàng)建新的dump文件    目錄context.cacheDir
    val heapDumpFile = directoryProvider.newHeapDumpFile()

    val durationMillis: Long
    try {
      //發(fā)送事件  當前的事件唯一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 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)
      //入棧 執(zhí)行workQueue
      workManager.enqueue(heapAnalysisRequest)
    }

最終調(diào)用HeapAnalysis中的幾個數(shù)據(jù)類 HeapAnalysisFailure HeapAnalysisSuccess,里面的toString()方法就是拼接的 我們在LeakCanary中看到的錯誤記錄.

摘要部分代碼

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

和5.10.1有些類似 步驟更少

  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)
      }
    }
  }

以上: LeakCanary的依賴 監(jiān)聽 dump 及分析都基本上理清了.

6 PlumberInstaller

leakcanary.internal.PlumberInstaller

另外加餐補充這個, 這個類的作用主要是反射調(diào)用不同版本不同手機廠商 對有些api是否支持到.

主要針對不同機型 不同版本,有些特殊的場景會導致Leak的時候,在Activity銷毀的時候手動置為null方便回收.

AndroidLeakFixes.applyFixes(application)

這個分析是在后臺通過單一線程池執(zhí)行的

 Executors.newSingleThreadScheduledExecutor

舉個例子,比如 這里面就有針對三星設備 且不是19到21之間的版本
反射TextView中的mLastHoveredView字段

  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

我們打開金絲雀圖標展示的就是這個Activity.

導入.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

主要就是申請權限使用的.

以上就是整體的分析過程.

  • 從設計模式來看,使用到了工廠模式(Wacher和分析器)
  • 巧妙運用ContentProvider特性達到無侵入式 一行代碼接入
  • 相比老版本,新增了RootView及Service的監(jiān)測

注意事項: 這個官方聲明只能在debug模式.

才疏學淺,歡迎探討.

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容