我知道这个问题似乎被问过和回答过,但事实并非如此。因为我还没有遇到解决方案。
我有很多片段的活动。并且我存储所有转换历史记录(可以通过按后按 LIFO 顺序返回每个片段 - 因为每个片段都添加到后退堆栈)
我想达到下一个功能: 当我按下后退时 - 底部片段的视图不得重新创建。
我做下一个
1)使用安卓导航组件和过渡,如
fun navigate(@IdRes resId: Int, bundle: Bundle? = null, navOptions: NavOptions? = null, sharedElements: List<View>? = null) {
navController.navigate(resId, bundle, navOptions, sharedElements?.let {
if (it.isEmpty()) null else
FragmentNavigatorExtras(
*it.map { view -> Pair(view, view.transitionNameCompat.safe) }
.filter { pair -> pair.second.isNotEmpty() }
.toTypedArray()
)
})
}
其中 idRes 是目标 ID(不是转换 ID)
2)将内容视图保存在片段中,并在onCreateView视图方法中将其与父视图分离。因为getView()
返回 null 甚至片段也从后堆栈出现。
private var contentView: View? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
prepareView(contentView) ?: createView(inflater, container)
private fun prepareView(view: View?): View? {
val parent = view?.parent
val viewGroup = parent as? ViewGroup
viewGroup?.removeView(view)
return view
}
protected open fun createView(inflater: LayoutInflater, container: ViewGroup?): View? {
val layout = layout()
if (layout == -1)
throw IllegalArgumentException("You need to override "layout()" fun or override "createView" fun")
return inflater.inflate(layout(), container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (contentView == null) {
onFirstInitialization(view, savedInstanceState)
} else {
onNextInitialization(view, savedInstanceState)
}
onEachInitialization(view, savedInstanceState)
contentView = view
}
只有当我转移到下一个片段并快速按"返回"时,我才有例外!在正常模式下一切都很好和正确。
例外:
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
at android.view.ViewGroup.addViewInner(ViewGroup.java:5050)
at android.view.ViewGroup.addView(ViewGroup.java:4881)
at android.view.ViewGroup.addView(ViewGroup.java:4821)
at android.view.ViewGroup.addView(ViewGroup.java:4794)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:890)
at androidx.fragment.app.FragmentManagerImpl.addAddedFragments(FragmentManagerImpl.java:2092)
at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1866)
at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1822)
at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManagerImpl.java:1723)
at androidx.fragment.app.FragmentManagerImpl.dispatchStateChange(FragmentManagerImpl.java:2624)
at androidx.fragment.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManagerImpl.java:2580)
at androidx.fragment.app.Fragment.performActivityCreated(Fragment.java:2571)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:907)
at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1235)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1301)
at androidx.fragment.app.FragmentManagerImpl.dispatchStateChange(FragmentManagerImpl.java:2620)
at androidx.fragment.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManagerImpl.java:2580)
at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:246)
at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:541)
at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:201)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1470)
at android.app.Activity.performStart(Activity.java:7176)
at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3086)
at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:180)
at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:165)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:142)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1926)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6923)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
我可以通过prepareView
onDestroyView
生命周期方法来解决这个问题,并且一切正常,但它会产生内存泄漏:我发现它 - 当视图在onDestroyView
之后没有父级时,onDestroy
从未调用过,即使在后按后,片段也会保留在内存中。
你可以说"嘿,伙计,不要着急,慢慢使用你的应用程序,正如你所说,异常永远不会被抛出。但是应用程序在生产中,我遇到了此错误的崩溃":(我该如何处理此案?
更新:我研究了一下,发现了一些东西。仅当视图未与窗口分离时引发异常。并且仅在新片段打开后 300-400 毫秒后从窗口分离
查看是的!是真的。您无法再次创建其对象,因为它具有父对象。您可能没有注意到的主要问题是,每当您再次调用片段时,都会调用onCreateView()
。
因此,在排序中,我建议您将代码从片段的onCreateView()
移动到onCreate()
方法。
我也为这类问题而苦苦挣扎。因此,onCreate()
方法中膨胀视图,并在onCreateView()
方法中返回主视图。