BottomNavigator icon indicating copy to clipboard operation
BottomNavigator copied to clipboard

Can the BottomNavigator operate when placed in a Fragment?

Open io7m opened this issue 5 years ago • 3 comments

Hello!

Our application is a single activity, and so each screen is a fragment. We currently have a splash screen fragment S which, when completed, tells the hosting activity to replace it with a fragment T that contains a BottomNavigationView. In the onStart method of T, I initialize a BottomNavigator. This results in the following exception:

    Process: org.nypl.simplified.vanilla, PID: 11993
    io.reactivex.exceptions.OnErrorNotImplementedException: The exception was not handled due to missing onError handler in the subscribe() method call. Further reading: https://github.com/ReactiveX/RxJava/wiki/Error-Handling | java.lang.IllegalStateException: FragmentManager is already executing transactions
        at io.reactivex.internal.functions.Functions$OnErrorMissingConsumer.accept(Functions.java:704)
        at io.reactivex.internal.functions.Functions$OnErrorMissingConsumer.accept(Functions.java:701)
        at io.reactivex.internal.observers.LambdaObserver.onError(LambdaObserver.java:77)
        at io.reactivex.internal.observers.LambdaObserver.onNext(LambdaObserver.java:67)
        at hu.akarnokd.rxjava2.subjects.UnicastWorkSubject.drain(UnicastWorkSubject.java:258)
        at hu.akarnokd.rxjava2.subjects.UnicastWorkSubject.subscribeActual(UnicastWorkSubject.java:159)
        at io.reactivex.Observable.subscribe(Observable.java:12267)
        at io.reactivex.Observable.subscribe(Observable.java:12253)
        at io.reactivex.Observable.subscribe(Observable.java:12155)
        at com.pandora.bottomnavigator.ActivityDelegate.onActivityStart(ActivityDelegate.kt:53)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at androidx.lifecycle.ClassesInfoCache$MethodReference.invokeCallback(ClassesInfoCache.java:216)
        at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeMethodsForEvent(ClassesInfoCache.java:194)
        at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeCallbacks(ClassesInfoCache.java:185)
        at androidx.lifecycle.ReflectiveGenericLifecycleObserver.onStateChanged(ReflectiveGenericLifecycleObserver.java:36)
        at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:361)
        at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.java:188)
        at com.pandora.bottomnavigator.ActivityDelegate.<init>(ActivityDelegate.kt:44)
        at com.pandora.bottomnavigator.BottomNavigator.onCreate(BottomNavigator.kt:399)
        at com.pandora.bottomnavigator.BottomNavigator.access$onCreate(BottomNavigator.kt:36)
        at com.pandora.bottomnavigator.BottomNavigator$Companion.onCreate(BottomNavigator.kt:323)
        at org.nypl.simplified.ui.navigation.tabs.TabbedNavigationController$Companion.create(TabbedNavigationController.kt:70)
        at org.nypl.simplified.main.MainFragment.onStart(MainFragment.kt:54)
        at androidx.fragment.app.Fragment.performStart(Fragment.java:2632)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:915)
        at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1238)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1303)
        at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:439)
        at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManagerImpl.java:2079)
        at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1869)
        at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1824)
        at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManagerImpl.java:1727)
        at androidx.fragment.app.FragmentManagerImpl$2.run(FragmentManagerImpl.java:150)
        at android.os.Handler.handleCallback(Handler.java:739)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:145)
        at android.app.ActivityThread.main(ActivityThread.java:6939)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)
     Caused by: java.lang.IllegalStateException: FragmentManager is already executing transactions
        at androidx.fragment.app.FragmentManagerImpl.ensureExecReady(FragmentManagerImpl.java:1660)

Is there something special I need to do to run a bottom navigator inside a fragment? I suspect that this might be getting into the territory of "nested fragments", although I'm not sure...

io7m avatar Nov 30 '19 11:11 io7m

A workaround here is to delay the creation of the navigator in the onStart method by scheduling the creation to run on the UI thread at a slightly later date. It's not great, but it's all I've been able to find.

io7m avatar Nov 30 '19 12:11 io7m

To be clear: This seems to be caused by the BottomNavigator eagerly instantiating and adding Fragments when the Fragment containing it is in the middle of being added. Delaying it allows the instantiation to happen after the Fragment transaction has completed.

io7m avatar Dec 01 '19 16:12 io7m

Any chance you could create a minimal project that demonstrates the bug?

guelo avatar Dec 03 '19 23:12 guelo