2021-02-03Leakcanary 源码流程<二>(Fragment、ViewModel、RootView、Service 泄露监测)

接上一篇,上一篇分析了Activity的检测,继续来看剩下的Fragment、ViewModel 、RootView、Service的检测

Fragment、ViewModel 泄漏检测

根据上一篇的内容,可以知道Fragment的泄漏检测是在FragmentAndViewModelWatcher中的install方法中注册

  override fun install() {
    //也是调用Application注册监听所有Activity中实现
    application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
  }

继续看lifecycleCallbacks中的处理

  private val lifecycleCallbacks =
    object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
      //监听所有Activity的onCreate方法
      override fun onActivityCreated(
        activity: Activity,
        savedInstanceState: Bundle?
      ) {
        //调用添加的watcher方法
        for (watcher in fragmentDestroyWatchers) {
          watcher(activity)
        }
      }
    }

继续看fragmentDestroyWatchers中怎么添加数据的

  private val fragmentDestroyWatchers: List<(Activity) -> Unit> = run {
    //最后返回的就是这个list,所以看list中添加什么样的数据
    val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()
    //如果sdk版本大于O,也就是26,这里先默认大于,走这个if
    if (SDK_INT >= O) {
      //往list中添加AndroidOFragmentDestroyWatcher对象
      fragmentDestroyWatchers.add(
        AndroidOFragmentDestroyWatcher(reachabilityWatcher)
      )
    }
    //判断是否为androidx 包下面的fragment,
    //如果是就通过反射创建一个AndroidXFragmentDestroyWatcher对象,并添加到list中
    getWatcherIfAvailable(
      ANDROIDX_FRAGMENT_CLASS_NAME,
      ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
      reachabilityWatcher
    )?.let {
      fragmentDestroyWatchers.add(it)
    }

    //判断是否为v4 包下面的fragment,
    //如果是就通过反射创建一个AndroidSupportFragmentDestroyWatcher对象,并添加到list中
    getWatcherIfAvailable(
      ANDROID_SUPPORT_FRAGMENT_CLASS_NAME,
      ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
      reachabilityWatcher
    )?.let {
      fragmentDestroyWatchers.add(it)
    }
    fragmentDestroyWatchers
  }

先来看如果sdk大于26的情况下,AndroidOFragmentDestroyWatcher的处理。再添加到list中,在循环中调用watcher(activity)方法时,会走到对应的list中数据的invoke方法中,看AndroidOFragmentDestroyWatcher.invoke()方法

  override fun invoke(activity: Activity) {
    //获取fragmentManager
    val fragmentManager = activity.fragmentManager
    //通过fragmentManager注册fragment生命周期监听
    fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
  }

通过fragmentManager注册fragment生命周期监听,看对应监听方法

  private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {
    //监听fragment的ViewDestroyed方法,就是fragment中View销毁时回调
    override fun onFragmentViewDestroyed(
      fm: FragmentManager,
      fragment: Fragment
    ) {
      //获取view
      val view = fragment.view
      if (view != null) {
        //如果view不为null,进入观察方法中
        reachabilityWatcher.expectWeaklyReachable(
          view, "${fragment::class.java.name} received Fragment#onDestroyView() callback " +
          "(references to its views should be cleared to prevent leaks)"
        )
      }
    }
    //监听fragment的Destroyed方法
    override fun onFragmentDestroyed(
      fm: FragmentManager,
      fragment: Fragment
    ) {
      //观察这个fragment是否回收
      reachabilityWatcher.expectWeaklyReachable(
        fragment, "${fragment::class.java.name} received Fragment#onDestroy() callback"
      )
    }
  }

通过onFragmentViewDestroyed和onFragmentDestroyed方法分别监听fragment中view和自身的回收情况来判断是否出现泄漏,其中expectWeaklyReachable方法在上一篇已经介绍。

继续往下看,如果使用的androidx包下的framgent,进入AndroidXFragmentDestroyWatcher中invoke方法

  override fun invoke(activity: Activity) {
    //判断当前activity是否为FragmentActivity
    if (activity is FragmentActivity) {
      //获取supportFragmentManager
      val supportFragmentManager = activity.supportFragmentManager
      //同样通过supportFragmentManager注册framgent监听
      supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
      //ViewModel监听
      ViewModelClearedWatcher.install(activity, reachabilityWatcher)
    }
  }

先来看framgent的监听,也是走到对应的监听方法中

  private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {

    override fun onFragmentCreated(
      fm: FragmentManager,
      fragment: Fragment,
      savedInstanceState: Bundle?
    ) {
      //注册framgent中的ViewModel肩痛
      ViewModelClearedWatcher.install(fragment, reachabilityWatcher)
    }

    //同上,监听fragment的view
    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)"
        )
      }
    }
    //监听framgent的回收
    override fun onFragmentDestroyed(
      fm: FragmentManager,
      fragment: Fragment
    ) {
      reachabilityWatcher.expectWeaklyReachable(
        fragment, "${fragment::class.java.name} received Fragment#onDestroy() callback"
      )
    }
  }

framgnet的监听和前面O的监听一样,主要就是增加了一个ViewModel的监听,接下来看ViewModel的监听,走到ViewModelClearedWatcher.install方法中

    fun install(
      storeOwner: ViewModelStoreOwner,
      reachabilityWatcher: ReachabilityWatcher
    ) {
      //通过provider创建一个ViewModelClearedWatcher的ViewModel对象
      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)
    }
  }

上面代码就是创建一个ViewModelClearedWatcher对象,并将它和activity或fragment关联起来,当ViewMode回收时,会调用onCleared方法,所以走到ViewModelClearedWatcher的onCleared方法中

  private val viewModelMap: Map<String, ViewModel>?

  init {
     //init 方法在初始化的时候就会调用,在这里获取缓存ViewModel的mMap对象
    // We could call ViewModelStore#keys with a package spy in androidx.lifecycle instead,
    // however that was added in 2.1.0 and we support AndroidX first stable release. viewmodel-2.0.0
    // does not have ViewModelStore#keys. All versions currently have the mMap field.
    viewModelMap = try {
      val mMapField = ViewModelStore::class.java.getDeclaredField("mMap")
      mMapField.isAccessible = true
      @Suppress("UNCHECKED_CAST")
      mMapField[storeOwner.viewModelStore] as Map<String, ViewModel>
    } catch (ignored: Exception) {
      null
    }
  }

  override fun onCleared() {
    //循环mMap中的数据,判断ViewModel是否出现泄漏
    viewModelMap?.values?.forEach { viewModel ->
      reachabilityWatcher.expectWeaklyReachable(
        viewModel, "${viewModel::class.java.name} received ViewModel#onCleared() callback"
      )
    }
  }

RootView泄漏检测

走到RootView泄漏检测的类中,也就是RootViewWatcher的install方法

  override fun install() {
    //如果sdk版本小雨19,直接返回,不检测
    if (Build.VERSION.SDK_INT < 19) {
      return
    }
    //这一段代码是,通过反射获取mView,拦截mView.add()方法,每当调用add方法是,就会走自身的onRootViewAdded方法,并将这个view传过去
    swapViewManagerGlobalMViews { mViews ->
      object : ArrayList<View>(mViews) {
        override fun add(element: View): Boolean {
          onRootViewAdded(element)
          return super.add(element)
        }
      }
    }
  }

  //反射获取WindowManagerGlobal中的mView
  @SuppressLint("PrivateApi")
  @Suppress("FunctionName")
  private fun swapViewManagerGlobalMViews(swap: (ArrayList<View>) -> ArrayList<View>) {
    try {
      val windowManagerGlobalClass = Class.forName("android.view.WindowManagerGlobal")
      val windowManagerGlobalInstance =
        windowManagerGlobalClass.getDeclaredMethod("getInstance").invoke(null)

      val mViewsField =
        windowManagerGlobalClass.getDeclaredField("mViews").apply { isAccessible = true }

      @Suppress("UNCHECKED_CAST")
      val mViews = mViewsField[windowManagerGlobalInstance] as ArrayList<View>

      mViewsField[windowManagerGlobalInstance] = swap(mViews)
    } catch (ignored: Throwable) {
      SharkLog.d(ignored) { "Could not watch detached root views" }
    }
  }

根据上面代码,可以知道,每当创建一个view并添加到mView中时,就会调用onRootViewAdded方法

  private fun onRootViewAdded(rootView: View) {
    //注册view的状态监听
    rootView.addOnAttachStateChangeListener(object : OnAttachStateChangeListener {

      val watchDetachedView = Runnable {
        //执行view的泄露检测
        reachabilityWatcher.expectWeaklyReachable(
          rootView, "${rootView::class.java.name} received View#onDetachedFromWindow() callback"
        )
      }

      override fun onViewAttachedToWindow(v: View) {
        //添加到Window时,移除run
        mainHandler.removeCallbacks(watchDetachedView)
      }

      override fun onViewDetachedFromWindow(v: View) {
        //当view在window中销毁时,执行run
        mainHandler.post(watchDetachedView)
      }
    })
  }

总结一下view的泄漏检测,通过反射的方式获取mView,hook add 方法,每次调用add方法时,执行onRootViewAdded方法,通过注册view的状态变化,来观察view的泄漏

Service泄漏检测

走到ServiceWatcher. install方法中

  override fun install() {
    //做一些检查
    checkMainThread()
    check(uninstallActivityThreadHandlerCallback == null) {
      "ServiceWatcher already installed"
    }
    check(uninstallActivityManager == null) {
      "ServiceWatcher already installed"
    }
    try {
      //和前面hook mView差不多,不过是hook mH 的callback
      swapActivityThreadHandlerCallback { mCallback ->
        uninstallActivityThreadHandlerCallback = {
          swapActivityThreadHandlerCallback {
            mCallback
          }
        }
        Handler.Callback { msg ->
          //当调用了service 的onDestroy时
          if (msg.what == STOP_SERVICE) {
            val key = msg.obj as IBinder
            activityThreadServices[key]?.let {
             //将这个service通过弱引用的方式保存到servicesToBeDestroyed中
              onServicePreDestroy(key, it)
            }
          }
          mCallback?.handleMessage(msg) ?: false
        }
      }
      //继续hook,这里hook的是ActivityManagerService
      swapActivityManager { activityManagerInterface, activityManagerInstance ->
        uninstallActivityManager = {
          swapActivityManager { _, _ ->
            activityManagerInstance
          }
        }

        //动态代理的方式
        Proxy.newProxyInstance(
          activityManagerInterface.classLoader, arrayOf(activityManagerInterface)
        ) { _, method, args ->
          //当调用serviceDoneExecuting方法时,观察这个service的泄漏情况
          if (METHOD_SERVICE_DONE_EXECUTING == method.name) {
            //通过token获取service
            val token = args!![0] as IBinder
            if (servicesToBeDestroyed.containsKey(token)) {
              //观察service泄漏情况
              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" }
    }
  }

最后走到onServiceDestroyed方法中

  private fun onServiceDestroyed(token: IBinder) {
    servicesToBeDestroyed.remove(token)?.also { serviceWeakReference ->
      serviceWeakReference.get()?.let { service ->
        //检测service的泄漏情况
        reachabilityWatcher.expectWeaklyReachable(
          service, "${service::class.java.name} received Service#onDestroy() callback"
        )
      }
    }
  }

总结一些service的泄漏检测,通过hook mH回调的service的onDestroy的方法,将该service保存到activityThreadServices。hook AMS,当调用了serviceDoneExecuting方法时,判断service是否出现泄漏。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容