Leak in FragmentContainerView/ViewGroup.mCurrentDragChild using Drag 拖动手势

161 阅读1分钟

@[Toc](Leak in FragmentContainerView/ViewGroup.mCurrentDragChild using Drag 拖动手势)

记录一次我在Fragment中使用startDrag, 造成FragmentContainerView泄漏的解决方案

1.问题

  1. 如图, 一次偶然的机会, 我在使用Jetpack的navigator的时候, 使用拖动startDrag在子Fragment中通过LeakCanary发现了严重的内存泄漏 在这里插入图片描述
  2. 发现此类问题并不多, 只有github的1963汇报了此问题, 并且没有实际的解决方式 在这里插入图片描述
  3. 锅又到google那边了😂,无妨, 点开看看, 留下了到此一游....那么就自己解决吧 在这里插入图片描述

2.解决方案

通过反射强制使其置null, 释放, 如下代码

object UtilKDragAndDrop {
    private const val TAG = "UtilKDragAndDrop>>>>>"

    @JvmStatic
    @Throws(Exception::class)
    fun fixDragAndDropLeak(vararg views: View) {
        var tempView: View
        views.forEach { v ->
            tempView = v
            while (tempView.parent != null && tempView.parent is ViewGroup) {
                tempView = tempView.parent as ViewGroup
                fixDragAndDropLeak(tempView)
            }
        }
    }

    @JvmStatic
    @Throws(Exception::class)
    fun fixDragAndDropLeak(view: View) {
        val field: Field
        val fieldObj: Any?
        try {
            field = UtilKReflect.getField(view, "mCurrentDragChild")
            if (!field.isAccessible) field.isAccessible = true
            fieldObj = field.get(view)
            if (fieldObj != null) {
                field.set(view, null)
                Log.d(TAG, "fixDragAndDropLeak: set viewGroup ${view.javaClass.simpleName} mCurrentDragChild null")
            }
        } catch (e: Exception) {
            e.printStackTrace()
            e.message?.et(TAG)
        }
    }
}
  1. 再加上Lifecycle使用, 使其在onPause之前释放
    override fun onPause(owner: LifecycleOwner) {
        try {
            _viewList.forEach {
                UtilKDragAndDrop.fixDragAndDropLeak(it.first, it.second)
            }
            _viewList.clear()
        } catch (e: Exception) {
            e.printStackTrace()
            e.message?.et(TAG)
        }
        super.onPause(owner)
    }

3.完美解决

在这里插入图片描述