I am trying to fix a bug where my app crashes if a user performs an orientation change during the onboarding process (I used a 3 fragment viewpager2 layout within JetPack NavHost). I tried adding my viewmodel to it so that it would be lifecycle aware during onboarding, but I received this identical error message both before and after adding the viewModel:
java.lang.IllegalStateException: FragmentManager is already executing transactions
So I think the FragmentManager object within my ViewPager2 is causing a crash. The OnBoarding works fine if I don't switch my orientation, and then works as intended post OnBoarding.
Is it best practice to lock the Onboarding to a vertical layout? I did some research via Play apps and found a lot of them went about OnBoarding in this way.
Code Reference for my First Screen of OnBoarding:
class FirstScreen : Fragment() {
private var _binding: FragmentFirstScreenBinding? = null
private val binding get() = _binding!!
private val sharedViewModel: PrimaryViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.apply {
viewModel = sharedViewModel
firstScreen = this@FirstScreen
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
_binding = FragmentFirstScreenBinding.inflate(inflater, container, false)
val viewPager = activity?.findViewById<ViewPager2>(R.id.onboardingViewPager)
binding.firstScreenNext.setOnClickListener {
viewPager?.currentItem = 1
}
return binding.root
}}
Adapter construction from ViewPageFragment:
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
_binding = FragmentViewPagerBinding.inflate(inflater, container, false)
val fragmentList = arrayListOf<Fragment>(
FirstScreen(),
SecondScreen(),
ThirdScreen()
)
val adapter = ViewPagerAdapter(
fragmentList,
requireActivity().supportFragmentManager,
lifecycle
)
binding.onboardingViewPager.adapter = adapter
return binding.root
ViewPagerAdapter:
class ViewPagerAdapter(
list: ArrayList<Fragment>,
fm: FragmentManager,
lifeCycle: Lifecycle) : FragmentStateAdapter(fm, lifeCycle) {
private val fragmentList = list
override fun getItemCount(): Int {
return fragmentList.size
}
override fun createFragment(position: Int): Fragment {
return fragmentList[position]
}}
Logcat stack trace:
2022-07-11 19:40:27.179 21638-21638/com.campfire.geostereo E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.campfire.geostereo, PID: 21638
java.lang.IllegalStateException: FragmentManager is already executing transactions
at androidx.fragment.app.FragmentManager.ensureExecReady(FragmentManager.java:1686)
at androidx.fragment.app.FragmentManager.execSingleAction(FragmentManager.java:1716)
at androidx.fragment.app.BackStackRecord.commitNow(BackStackRecord.java:317)
at androidx.viewpager2.adapter.FragmentStateAdapter$FragmentMaxLifecycleEnforcer.updateFragmentMaxLifecycle(FragmentStateAdapter.java:726)
at androidx.viewpager2.adapter.FragmentStateAdapter$FragmentMaxLifecycleEnforcer$3.onStateChanged(FragmentStateAdapter.java:657)
at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:360)
at androidx.lifecycle.LifecycleRegistry.forwardPass(LifecycleRegistry.java:271)
at androidx.lifecycle.LifecycleRegistry.sync(LifecycleRegistry.java:313)
at androidx.lifecycle.LifecycleRegistry.moveToState(LifecycleRegistry.java:151)
at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent(LifecycleRegistry.java:134)
at androidx.fragment.app.Fragment.performStart(Fragment.java:3167)
at androidx.fragment.app.FragmentStateManager.start(FragmentStateManager.java:588)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:279)
at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:113)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1424)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2968)
at androidx.fragment.app.FragmentManager.dispatchStart(FragmentManager.java:2893)
at androidx.fragment.app.Fragment.performStart(Fragment.java:3171)
at androidx.fragment.app.FragmentStateManager.start(FragmentStateManager.java:588)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:279)
at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:113)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1424)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2968)
at androidx.fragment.app.FragmentManager.dispatchStart(FragmentManager.java:2893)
at androidx.fragment.app.FragmentController.dispatchStart(FragmentController.java:274)
at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:359)
at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:246)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1467)
at android.app.Activity.performStart(Activity.java:8082)
at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3732)
at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221)
at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2253)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7870)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)