【问题标题】:Memory leak in activity enter transition coordinator after shared element transition共享元素转换后活动中的内存泄漏进入转换协调器
【发布时间】:2019-05-22 18:50:47
【问题描述】:

我在使用共享元素转换时遇到了EnterTransitionCoordinator 中的内存泄漏问题。您可以在下面看到应用程序结构:

它有 2 个屏幕,第一个是 Activity,里面有 DrawerLayout 和几个 Fragments。其中一个包含照片列表,单击特定照片会触发共享元素从位于另一个ActivityViewPager 转换到Fragment。我在退出和重新进入这两个Activitys 时使用自定义SharedElementCallback 来映射正确的View 以进行共享元素转换。我的代码基于这篇很棒的博文:https://android.jlelse.eu/dynamic-shared-element-transition-23428f62a2af

问题是,在ViewPager 的项目之间滑动后,Fragments 被销毁,但用于共享元素转换的View 被保留在ActivityActivityTransitionState 中,特别是在EnterTransitionCoordinator。使用DrawerLayout 重新进入Activity 然后打开另一个Fragment 时也是如此。用于共享元素转换的 Views 的引用仍保留为 int Activitys,即使 Fragments 已被破坏,这会导致内存泄漏。

我的问题:有没有避免这种内存泄漏的好方法?

【问题讨论】:

    标签: android kotlin memory-leaks shared-element-transition sharedelementcallback


    【解决方案1】:

    我发现EnterTransitionCoordinator中有一个方法clearState(),应该在Activity.onStop()中调用。但由于Activity 尚未停止,来自Fragments 的Views 正在泄露。作为临时解决方法,我通过反射调用此方法在Fragment.onDestroyView() 上手动清除该状态。下面你可以看到代码:

    /**
     * Works only for API < 28
     * https://developer.android.com/about/versions/pie/restrictions-non-sdk-interfaces
     */
    fun Fragment.clearEnterTransitionState() {
        try {
            getActivityTransitionState()
                ?.getEnterTransitionCoordinator()
                ?.invokeClearStateMethod()
        } catch (e: Exception) {
            // no-op
        }
    }
    
    private fun Fragment.getActivityTransitionState() =
        Activity::class.java.getField("mActivityTransitionState", requireActivity())
    
    private fun Any.getEnterTransitionCoordinator() = javaClass.getField("mEnterTransitionCoordinator", this)
    
    private fun Any.invokeClearStateMethod() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            javaClass.superclass?.invokeClearStateMethod(this)
        } else {
            javaClass.invokeClearStateMethod(this)
        }
    }
    
    private fun <T> Class<T>.getField(name: String, target: Any): Any? =
        getDeclaredField(name).run {
            isAccessible = true
            get(target)
        }
    
    private fun <T> Class<T>.invokeClearStateMethod(target: Any) {
        getDeclaredMethod("clearState").apply {
            isAccessible = true
            invoke(target)
        }
    }
    

    【讨论】:

    • 最好不要这样做。如果您实际上内存不足,则框架将破坏先前的活动,这将为您清除。唯一可能出现问题的情况是,如果您有一个非常大的分配,并且需要避免在其上运行 OOM。这也会破坏后退按钮上的任何动画。
    • @GabeSechan 当然,我同意这不是修复它的最佳方法。实际上,它不会破坏后退按钮上的任何动画,因为自定义 SharedElementCallback 设置为映射正确的视图以进行转换。
    猜你喜欢
    • 1970-01-01
    • 2015-06-15
    • 1970-01-01
    • 2014-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多