Утечка памяти в действии вводит координатор перехода после перехода совместно используемого элемента - PullRequest
0 голосов
/ 23 декабря 2018

У меня проблема с утечкой памяти в EnterTransitionCoordinator при использовании общих переходов элементов.Ниже вы можете увидеть структуру приложения:

App structure

У него 2 экрана, первый - Activity с DrawerLayout и несколько Fragment с.внутри.Одна из них содержит список фотографий и щелчок по определенной фотографии запускает переход общего элемента в Fragment из ViewPager, расположенного в другом Activity.Я использую пользовательские SharedElementCallback при выходе и повторном вводе этих двух Activity для сопоставления правильного View для перехода с общим элементом.Я основал свой код на этом замечательном посте в блоге: https://android.jlelse.eu/dynamic-shared-element-transition-23428f62a2af

Проблема в том, что после пролистывания элементов ViewPager, Fragment уничтожаются, а View используется для общего доступапереход элемента сохраняется в Activity ActivityTransitionState, в частности в EnterTransitionCoordinator.То же самое при повторном входе в Activity с DrawerLayout и последующем открытии другого Fragment.Ссылки на View s, используемые для переходов совместно используемых элементов, по-прежнему хранятся в Activity s, даже если Fragment s были уничтожены, что приводит к утечке памяти.

Мой вопрос: есть ли хороший способ избежать этой утечки памяти?

1 Ответ

0 голосов
/ 23 декабря 2018

Я обнаружил, что есть метод clearState() в EnterTransitionCoordinator, который должен вызываться в Activity.onStop().Но поскольку Activity еще не остановлено, View s из Fragment s просочились.В качестве временного решения я очищаю это состояние вручную на 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)
    }
}
...