[英]The specified child already has a parent. Fragment on back press
I know this question seems to be asked and answered, but it is not true. 我知道这个问题似乎已经被提出并回答了,但事实并非如此。 Because I haven't met a solution yet. 因为我还没有找到解决方案。
I have Activity with a lot of fragments. 我有很多碎片的活动。 And I store all transitions history (can return to each fragment in LIFO order with the back press - because each adds to back stack) 并且我存储了所有转换历史记录(可以按后退按LIFO顺序返回每个片段-因为每个片段都添加到了后退堆栈中)
I want to reach the next feature: When I press back - view of bottom fragment must not be re-creating. 我想达到下一个功能:当我按返回时-不能重新创建底部片段的视图。
I do next 我下一步
1) Use android navigation components and transitions like 1)使用android导航组件和过渡
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()
)
})
}
where idRes is destination id (not transition id) 其中idRes是目的地ID(不是过渡ID)
2) hold the content view in fragment and detach it from the parent in onCreateView view method. 2)将内容视图保持在片段中,然后在onCreateView视图方法中将其与父视图分离。 Because getView()
returns null even fragment appear from back stack. 因为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
}
I've got exception ONLY IF I transit to next fragment and press "back" rapidly! 仅当我过渡到下一个片段并快速按“返回”时,我才例外! In normal mode all good and correct. 在正常模式下,一切正常。
Exception: 例外:
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)
I can solve this by place prepareView
to onDestroyView
lifecycle method and everything work correctly, but it produces memory leak: I find it out - when the view has not a parent after onDestroyView
then onDestroy
never called and fragments hold in memory even after the back press. 我可以通过将prepareView
放置到onDestroyView
生命周期方法中来解决此问题,并且一切正常,但是会产生内存泄漏:我发现了-当视图在onDestroyView
之后没有父级时,则永远不会调用onDestroy
并且即使在按回键之后,片段也会保留在内存中。
You can say "Hey dude, don't hurry, use your app slowly and the exception will never be thrown, as you say. But app in production and I met crashes with this bug" :( How I can handle this case? 您可以说:“嘿,伙计,不要着急,慢慢使用您的应用程序,就像您说的那样,永远不会抛出异常。但是在生产中,我遇到了此bug导致崩溃的bug” :(我如何处理这种情况?
UPDATE: I researched a little and found out something. 更新:我研究了一下,发现了一些东西。 Exception thrown only when view not detached from window. 仅当视图未从窗口分离时抛出异常。 And view detach from window only after 300-400 ms after new Fragment opened 并且仅在新片段打开后300-400毫秒后才从窗口中分离视图
Yes! 是! It's true. 这是真的。 You can't create again its object because it has a parent. 您无法再次创建其对象,因为它有一个父对象。 The main problem may you haven't noticed is whenever you called your fragment again then onCreateView()
is called. 您可能没有注意到的主要问题是,每当您再次调用片段时,就会调用onCreateView()
。
So, in-sort I suggest you move your code from onCreateView()
to onCreate()
method of the fragment. 因此,在排序中,我建议您将代码从片段的onCreateView()
移至onCreate()
方法。
I also struggled with this type of problem. 我也为这类问题而苦恼。 So, inflate your view in onCreate()
method and return main view in onCreateView()
method. 因此,膨胀在您看来onCreate()
方法,并返回主视图onCreateView()
方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.