Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android crashes when resuming from background AND navigation occurs using fragments. #819

Closed
luislukas opened this issue Feb 18, 2021 · 17 comments · Fixed by #1066
Closed
Labels
Platform: Android This issue is specific to Android

Comments

@luislukas
Copy link

We are seeing the same error reported here and here and none of the solutions provided have worked for us.
Our setup is slightly different though:

  • We use navigation components since we have a single activity app.
  • React native is loaded on a fragment using navigation components.

The use case that crashes the app 100% of the time is a deeplink IF we are previously on a react native screen. After some investigation we think the following occurs:

  • The react native screen is loaded and then we background the app to open the email and click the a deeplink to the app.
  • When clicking the link, react native resumes and react-native-screens starts rendering the views, which seem to use Fragments under the hood - creates Fragment transactions for this purpose.
  • At the same time we are trying to navigate to handle the deeplink. Navigation components tries to use another Fragment transaction.
  • The system crashes since there are multiple Fragment transactions not finished.

Libraries:

  • "react-native-screens": "2.10.1"
  • "react-native": "0.63.4"
  • "react-navigation/native": "5.7.2"

We have tried one thing: IF "react-native-screens": "2.10.1" and we don't force that version, ie change other dependencies to use that version, the resulting app has react-native-screens 2.10.1 AND 2.9.0 and the issue seems to be gone. However though this doesn't sound like a very reliable thing to do.

The concerning lines are the following:
at com.swmansion.rnscreens.ScreenContainer.removeMyFragments(ScreenContainer.java:238) at com.swmansion.rnscreens.ScreenContainer.onDetachedFromWindow(ScreenContainer.java:250)

The stack trace we have is the following
java.lang.RuntimeException: Unable to resume activity {com.company.package.flavour.debug/com.company.MainActivity}: java.lang.IllegalStateException: FragmentManager is already executing transactions at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4219) at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4251) at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:52) at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2019) at android.os.Handler.dispatchMessage(Handler.java:107) at android.os.Looper.loop(Looper.java:214) at android.app.ActivityThread.main(ActivityThread.java:7410) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935) Caused by: java.lang.IllegalStateException: FragmentManager is already executing transactions at androidx.fragment.app.FragmentManager.ensureExecReady(FragmentManager.java:1790) at androidx.fragment.app.FragmentManager.execSingleAction(FragmentManager.java:1826) at androidx.fragment.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:303) at com.swmansion.rnscreens.ScreenContainer.removeMyFragments(ScreenContainer.java:238) at com.swmansion.rnscreens.ScreenContainer.onDetachedFromWindow(ScreenContainer.java:250) at android.view.View.dispatchDetachedFromWindow(View.java:19626) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3814) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.removeViewInternal(ViewGroup.java:5431) at android.view.ViewGroup.removeViewInternal(ViewGroup.java:5402) at android.view.ViewGroup.removeView(ViewGroup.java:5333) at androidx.fragment.app.FragmentContainerView.removeView(FragmentContainerView.java:317) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1262) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1368) at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1446) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1516) at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2019) at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1965) at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1861) at androidx.fragment.app.Fragment.performResume(Fragment.java:2745) at androidx.fragment.app.FragmentStateManager.resume(FragmentStateManager.java:373) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1211) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1368) at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1446) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1509) at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2637) at androidx.fragment.app.FragmentManager.dispatchResume(FragmentManager.java:2601) at androidx.fragment.app.FragmentController.dispatchResume(FragmentController.java:269) at androidx.fragment.app.FragmentActivity.onResumeFragments(FragmentActivity.java:478) at androidx.fragment.app.FragmentActivity.onPostResume(FragmentActivity.java:467) at androidx.appcompat.app.AppCompatActivity.onPostResume(AppCompatActivity.java:204) at android.app.Activity.performResume(Activity.java:7964) at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4209)

@WoLewicki
Copy link
Member

WoLewicki commented Feb 22, 2021

Looks like this line is quite crucial: androidx.fragment.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:303) and is triggered here: https://github.com/software-mansion/react-native-screens/blob/master/android/src/main/java/com/swmansion/rnscreens/ScreenContainer.java#L277. Making it synchronous (commitNowAllowingStateLoss instead of commitAllowingStateLoss) makes it reach invalid state where there is another transaction already being executed (see https://android.googlesource.com/platform/frameworks/support/+/84448d71fda0a24ba5d60fe9368ac47b97564c88/fragment/src/main/java/androidx/fragment/app/FragmentManagerImpl.java#1707 triggered by androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1861) for setting the execution of transition and here: https://android.googlesource.com/platform/frameworks/support/+/84448d71fda0a24ba5d60fe9368ac47b97564c88/fragment/src/main/java/androidx/fragment/app/FragmentManagerImpl.java#1643 triggered by androidx.fragment.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:303) for check that throws an exception from the issue). Seems like the only solution to this is to change the commitNowAllowingStateLoss to commitAllowingStateLoss in ScreenContainer, but it also may lead to other problems. Can you check if it resolves the issue and does not introduce any new issues?

@WoLewicki WoLewicki added the Platform: Android This issue is specific to Android label Feb 22, 2021
@luislukas
Copy link
Author

luislukas commented Feb 23, 2021

@WoLewicki - I've tested the change suggested but unfortunately it also ends in the same crash.
For the time being we have disabled this from being used in android, however, the react native engineers would like to enabled it at some point.
I'm happy to try any other suggestion. I did try executePendingTransactions before the code is doing the for loop on mFragmentManager.getFragments() but ends the same.

@WoLewicki
Copy link
Member

Can you post stacktraces of the crashes in the changed flows?

@luislukas
Copy link
Author

luislukas commented Feb 23, 2021

Using commitAllowingStateLoss instead of commitNowAllowingStateLoss

java.lang.RuntimeException: Unable to resume activity {com.package.flavour.debug/com.package.MainActivity}: java.lang.IllegalStateException: FragmentManager is already executing transactions at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4219) at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4251) at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:52) at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2019) at android.os.Handler.dispatchMessage(Handler.java:107) at android.os.Looper.loop(Looper.java:214) at android.app.ActivityThread.main(ActivityThread.java:7410) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935) Caused by: java.lang.IllegalStateException: FragmentManager is already executing transactions at androidx.fragment.app.FragmentManager.ensureExecReady(FragmentManager.java:1790) at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1855) at androidx.fragment.app.FragmentManager.executePendingTransactions(FragmentManager.java:489) at com.swmansion.rnscreens.ScreenContainer.onDetachedFromWindow(ScreenContainer.java:253) at android.view.View.dispatchDetachedFromWindow(View.java:19626) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3814) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.removeViewInternal(ViewGroup.java:5431) at android.view.ViewGroup.removeViewInternal(ViewGroup.java:5402) at android.view.ViewGroup.removeView(ViewGroup.java:5333) at androidx.fragment.app.FragmentContainerView.removeView(FragmentContainerView.java:317) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1262) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1368) at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1446) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1516) at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2019) at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1965) at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1861) at androidx.fragment.app.Fragment.performResume(Fragment.java:2745) at androidx.fragment.app.FragmentStateManager.resume(FragmentStateManager.java:373) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1211) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1368) at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1446) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1509) at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2637) at androidx.fragment.app.FragmentManager.dispatchResume(FragmentManager.java:2601) at androidx.fragment.app.FragmentController.dispatchResume(FragmentController.java:269) at androidx.fragment.app.FragmentActivity.onResumeFragments(FragmentActivity.java:478) at androidx.fragment.app.FragmentActivity.onPostResume(FragmentActivity.java:467) at androidx.appcompat.app.AppCompatActivity.onPostResume(AppCompatActivity.java:204) at android.app.Activity.performResume(Activity.java:7964) at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4209)

For this second stackTrace I've added transaction.commitAllowingStateLoss(); in the for loop:

`private void removeMyFragments() {
FragmentTransaction transaction = mFragmentManager.beginTransaction();
boolean hasFragments = false;

for (Fragment fragment : mFragmentManager.getFragments()) {
  if (fragment instanceof ScreenFragment && ((ScreenFragment) fragment).mScreenView.getContainer() == this) {
    transaction.remove(fragment);
    transaction.commitAllowingStateLoss();
    hasFragments = true;
  }
}

}`

And the crash seems to happen when we do mFragmentManager.executePendingTransactions(); on method onDetachedFromWindow()

java.lang.RuntimeException: Unable to resume activity {com.package.flavour.debug/com.package.MainActivity}: java.lang.IllegalStateException: FragmentManager is already executing transactions at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4219) at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4251) at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:52) at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2019) at android.os.Handler.dispatchMessage(Handler.java:107) at android.os.Looper.loop(Looper.java:214) at android.app.ActivityThread.main(ActivityThread.java:7410) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935) Caused by: java.lang.IllegalStateException: FragmentManager is already executing transactions at androidx.fragment.app.FragmentManager.ensureExecReady(FragmentManager.java:1790) at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1855) at androidx.fragment.app.FragmentManager.executePendingTransactions(FragmentManager.java:489) at com.swmansion.rnscreens.ScreenContainer.onDetachedFromWindow(ScreenContainer.java:257) at android.view.View.dispatchDetachedFromWindow(View.java:19626) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3814) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:3806) at android.view.ViewGroup.removeViewInternal(ViewGroup.java:5431) at android.view.ViewGroup.removeViewInternal(ViewGroup.java:5402) at android.view.ViewGroup.removeView(ViewGroup.java:5333) at androidx.fragment.app.FragmentContainerView.removeView(FragmentContainerView.java:317) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1262) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1368) at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1446) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1516) at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2019) at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1965) at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1861) at androidx.fragment.app.Fragment.performResume(Fragment.java:2745) at androidx.fragment.app.FragmentStateManager.resume(FragmentStateManager.java:373) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1211) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1368) at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1446) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1509) at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2637) at androidx.fragment.app.FragmentManager.dispatchResume(FragmentManager.java:2601) at androidx.fragment.app.FragmentController.dispatchResume(FragmentController.java:269) at androidx.fragment.app.FragmentActivity.onResumeFragments(FragmentActivity.java:478) at androidx.fragment.app.FragmentActivity.onPostResume(FragmentActivity.java:467) at androidx.appcompat.app.AppCompatActivity.onPostResume(AppCompatActivity.java:204) at android.app.Activity.performResume(Activity.java:7964) at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4209)

Now the interesting thing is - if I do the change suggested:

if (hasFragments) { transaction.commitAllowingStateLoss(); }

And I remove mFragmentManager.executePendingTransactions(); from method onDetachedFromWindow() all seems to work fine. My guess would be that the navigation components take care of the transactions enqueued in ScreenContainer.

Adding back commitNowAllowingStateLoss on the if above and not having executePendingTransactions() in onDetachedFromWindow() ends up in the same crash as well.

@WoLewicki
Copy link
Member

Hmm you can try and keep the configuration that causes no crashes, and see if the new issues arise from this. This implementation was for sure like this for a reason so deleting mFragmentManager.executePendingTransactions(); may produce new problems, but it would be good to check it for sure. Thank you for your feedback and please comment here the progress!

@luislukas
Copy link
Author

Cool - I'll get in touch with the team and see if we can try this and give the app a good shake.

@amutsch
Copy link

amutsch commented Mar 5, 2021

I have a closed source code base I can reproduce this with consistently. The latest has not fixed it, Let me see if I can troubleshoot the issue against my code base and PR. As a work around one can not use the enableScreens on the RN side and this does not occur. We are trying to avoid that

@WoLewicki
Copy link
Member

Not using enableScreens is not a solution since it just removes all of the integration with the native navigation. There is still no reproduction passed here so there is not much we can do with it. @amutsch could you pass a simple example where this can be seen? You probably don't need anything specific to your application, rather some steps that are crucial to see this.

@amutsch
Copy link

amutsch commented Mar 15, 2021

@WoLewicki Unfortunately the app is so large we do not have a good single source to boil it down to. I did make a minor change to ScreenContainer similar to what was outlined above. We have this in testing with our QA team and if everything continues to look good I will get a PR up.

@WoLewicki
Copy link
Member

Thank you! Please comment here also with the results.

@amutsch
Copy link

amutsch commented Mar 15, 2021

The Application is being hit hard by the release testing teams this week and the automation teams this week. We have closed out most of the crashes related to this bug. I will update and PR later this week.

@amutschBBY
Copy link

@WoLewicki The testing is continuing to go good on this PR, I'll be getting that PR setup this week.

@rodperottoni
Copy link

I was able to consistently get this to happen by
-> Opening an app that invokes enableScreens()
-> Background the app
-> Open the app's settings and revoke a permission (e.g. location)
-> Try to restore the app from the app drawer.

The crash happens every single time.

@amutschBBY
Copy link

I haven't forgotten about this issue, I'm monitoring our production crashes as we roll out to make sure my changes have this resolved.

@LcTwisk
Copy link

LcTwisk commented Apr 8, 2021

@amutschBBY any first insights already? 🙏

@amutschBBY
Copy link

@LcTwisk We are investigating some additional similar issues in onUpdate, onPause calls from native. I will start getting a fork prepped to PR back but likely won't create the PR until we work through some additional fragment manager issues. So far the onResume issues seem to be working well.

amutschBBY added a commit to bby-stdc/react-native-screens that referenced this issue Apr 13, 2021
This update fixes an issue where android is crashing on resume from background when using using react native module inside a native page.

resolves: software-mansion#819
@WoLewicki
Copy link
Member

Hopefully this issue should be solved by #1066. Could you check if it does not introduce any new problems? It certainly needs much testing and we would like to add it to the next release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Platform: Android This issue is specific to Android
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants