flutter-stripe / flutter_stripe

Flutter SDK for Stripe.
https://pub.dev/packages/flutter_stripe
937 stars 518 forks source link

After changing FlutterActivity to FlutterFragmentActivity in PlayStore shows app crash logs #996

Open vishnunew opened 1 year ago

vishnunew commented 1 year ago

Describe the bug As per documentation after changing FlutterActivity to FlutterFragmentActivity app crash in release mode but it is not crashing for all the device and not able to reproduce on my end but it shows in crashlytics and PlayStore console.

Fatal Exception: java.lang.RuntimeException: Unable to start activity ComponentInfo{*******}: androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment yl.o0: could not find Fragment constructor
       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3006)
       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3084)
       at android.app.ActivityThread.-wrap11(ActivityThread.java)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1781)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loop(Looper.java:210)
       at android.app.ActivityThread.main(ActivityThread.java:7080)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:523)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:863)
Caused by androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment yl.o0: could not find Fragment constructor
       at androidx.fragment.app.Fragment.instantiate(Fragment.java:630)
       at androidx.fragment.app.FragmentContainer.instantiate(FragmentContainer.java:57)
       at androidx.fragment.app.FragmentManager$2.instantiate(FragmentManager.java:448)
       at androidx.fragment.app.FragmentState.instantiate(FragmentState.java:81)
       at androidx.fragment.app.FragmentStateManager.<init>(FragmentStateManager.java:85)
       at androidx.fragment.app.FragmentManager.restoreSaveStateInternal(FragmentManager.java:2410)
       at androidx.fragment.app.FragmentManager.attachController(FragmentManager.java:2584)
       at androidx.fragment.app.FragmentController.attachHost(FragmentController.java:116)
       at androidx.fragment.app.FragmentActivity.lambda$init$1(FragmentActivity.java:128)
       at androidx.fragment.app.FragmentActivity.$r8$lambda$QtiQ2ZI3e38UkO1_xuJ8vE_JZj4(FragmentActivity.java)
       at androidx.fragment.app.FragmentActivity$$InternalSyntheticLambda$0$cef12c4fb802c6ea87b1fbddce076644080634cb6e3f7fb823e201a9f4f7f1ec$1.onContextAvailable(FragmentActivity.java:2)
       at androidx.activity.contextaware.ContextAwareHelper.dispatchOnContextAvailable(ContextAwareHelper.java:99)
       at androidx.activity.ComponentActivity.onCreate(ComponentActivity.java:320)
       at androidx.fragment.app.FragmentActivity.onCreate(FragmentActivity.java:249)
       at io.flutter.embedding.android.FlutterFragmentActivity.onCreate(FlutterFragmentActivity.java:279)
       at android.app.Activity.performCreate(Activity.java:7314)
       at android.app.Activity.performCreate(Activity.java:7305)
       at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1215)
       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2959)
       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3084)
       at android.app.ActivityThread.-wrap11(ActivityThread.java)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1781)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loop(Looper.java:210)
       at android.app.ActivityThread.main(ActivityThread.java:7080)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:523)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:863)
Caused by java.lang.NoSuchMethodException: <init> []
       at java.lang.Class.getConstructor0(Class.java:2320)
       at java.lang.Class.getConstructor(Class.java:1725)
       at androidx.fragment.app.Fragment.instantiate(Fragment.java:615)
       at androidx.fragment.app.FragmentContainer.instantiate(FragmentContainer.java:57)
       at androidx.fragment.app.FragmentManager$2.instantiate(FragmentManager.java:448)
       at androidx.fragment.app.FragmentState.instantiate(FragmentState.java:81)
       at androidx.fragment.app.FragmentStateManager.<init>(FragmentStateManager.java:85)
       at androidx.fragment.app.FragmentManager.restoreSaveStateInternal(FragmentManager.java:2410)
       at androidx.fragment.app.FragmentManager.attachController(FragmentManager.java:2584)
       at androidx.fragment.app.FragmentController.attachHost(FragmentController.java:116)
       at androidx.fragment.app.FragmentActivity.lambda$init$1(FragmentActivity.java:128)
       at androidx.fragment.app.FragmentActivity.$r8$lambda$QtiQ2ZI3e38UkO1_xuJ8vE_JZj4(FragmentActivity.java)
       at androidx.fragment.app.FragmentActivity$$InternalSyntheticLambda$0$cef12c4fb802c6ea87b1fbddce076644080634cb6e3f7fb823e201a9f4f7f1ec$1.onContextAvailable(FragmentActivity.java:2)
       at androidx.activity.contextaware.ContextAwareHelper.dispatchOnContextAvailable(ContextAwareHelper.java:99)
       at androidx.activity.ComponentActivity.onCreate(ComponentActivity.java:320)
       at androidx.fragment.app.FragmentActivity.onCreate(FragmentActivity.java:249)
       at io.flutter.embedding.android.FlutterFragmentActivity.onCreate(FlutterFragmentActivity.java:279)
       at android.app.Activity.performCreate(Activity.java:7314)
       at android.app.Activity.performCreate(Activity.java:7305)
       at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1215)
       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2959)
       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3084)
       at android.app.ActivityThread.-wrap11(ActivityThread.java)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1781)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loop(Looper.java:210)
       at android.app.ActivityThread.main(ActivityThread.java:7080)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:523)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:863)

Smartphone / tablet

Flutter 3.0.5 • channel stable • https://github.com/flutter/flutter.git
Framework • revision f1875d570e (4 months ago) • 2022-07-13 11:24:16 -0700
Engine • revision e85ea0e79c
Tools • Dart 2.17.6 • DevTools 2.12.2

Additional context

This was not causing any issue when it was FlutterActivity but after changed to FlutterFragmentActivity it starts crash.

remonh87 commented 1 year ago

Can you try to upgrade to the latest version 6.0.0 and try if it happens there? I do not see any code from our repo crashing in the logs.

joknjokn commented 1 year ago

We have the same issue, with version 6.0.0.

Anyone knows the cause of this or how to fix?

vishnunew commented 1 year ago

@remonh87 I have upgraded to 6.0.0 and still showing crash in Crashlytics and PlayStore.

vishnunew commented 1 year ago

I think this is related to this https://github.com/flutter/flutter/issues/114971. This package is also using the FlutterFragmentActivity and found crash.

thni-monta commented 1 year ago

On version 7.0.0 here and still happening. Both in PaymentSheetFragment and GooglePayFragment.

It looks like these fragments don't have a no-arg constructor, which causes the crash when the app is resumed and fragments re-created.

Steps to reproduce (for us at least):

  1. Enable "Don't keep activities" in Developer Options.
  2. After calling Stripe.instance.initPaymentSheet(...) somewhere, put the app in background.
  3. Re-enter the app. Result: The app crashes after splash with the following stacktrace:
E/AndroidRuntime( 1575): java.lang.RuntimeException: Unable to start activity ...
androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment com.reactnativestripesdk.PaymentSheetFragment: could not find Fragment constructor
E/AndroidRuntime( 1575):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:4166)
E/AndroidRuntime( 1575):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4312)
E/AndroidRuntime( 1575):    at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:101)
E/AndroidRuntime( 1575):    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
E/AndroidRuntime( 1575):    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
E/AndroidRuntime( 1575):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2571)
E/AndroidRuntime( 1575):    at android.os.Handler.dispatchMessage(Handler.java:106)
E/AndroidRuntime( 1575):    at android.os.Looper.loopOnce(Looper.java:226)
E/AndroidRuntime( 1575):    at android.os.Looper.loop(Looper.java:313)
E/AndroidRuntime( 1575):    at android.app.ActivityThread.main(ActivityThread.java:8741)
E/AndroidRuntime( 1575):    at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime( 1575):    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
E/AndroidRuntime( 1575):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)
E/AndroidRuntime( 1575): Caused by: androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment com.reactnativestripesdk.PaymentSheetFragment: could not find Fragment constructor
E/AndroidRuntime( 1575):    at androidx.fragment.app.Fragment.instantiate(Fragment.java:678)
E/AndroidRuntime( 1575):    at androidx.fragment.app.FragmentContainer.instantiate(FragmentContainer.java:57)
E/AndroidRuntime( 1575):    at androidx.fragment.app.FragmentManager$3.instantiate(FragmentManager.java:507)
E/AndroidRuntime( 1575):    at androidx.fragment.app.FragmentState.instantiate(FragmentState.java:81)
E/AndroidRuntime( 1575):    at androidx.fragment.app.FragmentStateManager.<init>(FragmentStateManager.java:85)
E/AndroidRuntime( 1575):    at androidx.fragment.app.FragmentManager.restoreSaveStateInternal(FragmentManager.java:2505)
E/AndroidRuntime( 1575):    at androidx.fragment.app.FragmentManager.attachController(FragmentManager.java:2665)
E/AndroidRuntime( 1575):    at androidx.fragment.app.FragmentController.attachHost(FragmentController.java:117)
E/AndroidRuntime( 1575):    at androidx.fragment.app.FragmentActivity.lambda$init$3$androidx-fragment-app-FragmentActivity(FragmentActivity.java:140)
E/AndroidRuntime( 1575):    at androidx.fragment.app.FragmentActivity$$ExternalSyntheticLambda0.onContextAvailable(Unknown Source:2)
E/AndroidRuntime( 1575):    at androidx.activity.contextaware.ContextAwareHelper.dispatchOnContextAvailable(ContextAwareHelper.java:99)
E/AndroidRuntime( 1575):    at androidx.activity.ComponentActivity.onCreate(ComponentActivity.java:362)
E/AndroidRuntime( 1575):    at androidx.fragment.app.FragmentActivity.onCreate(FragmentActivity.java:217)
E/AndroidRuntime( 1575):    at io.flutter.embedding.android.FlutterFragmentActivity.onCreate(FlutterFragmentActivity.java:279)
E/AndroidRuntime( 1575):    ... 15 more
E/AndroidRuntime( 1575): Caused by: java.lang.NoSuchMethodException: com.reactnativestripesdk.PaymentSheetFragment.<init> []
E/AndroidRuntime( 1575):    at java.lang.Class.getConstructor0(Class.java:2363)
E/AndroidRuntime( 1575):    at java.lang.Class.getConstructor(Class.java:1759)
E/AndroidRuntime( 1575):    at androidx.fragment.app.Fragment.instantiate(Fragment.java:663)
E/AndroidRuntime( 1575):    ... 30 more

The GooglePayFragment crash is almost identical:

  1. Enable "Don't keep activities" in Developer Options.
  2. After calling showing the Google Pay sheet somewhere, exit it and put the app in background.
  3. Re-enter the app.
E/AndroidRuntime( 5656): java.lang.RuntimeException: Unable to start activity ...
androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment com.reactnativestripesdk.GooglePayFragment: could not find Fragment constructor
E/AndroidRuntime( 5656):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:4166)
E/AndroidRuntime( 5656):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4312)
E/AndroidRuntime( 5656):    at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:101)
E/AndroidRuntime( 5656):    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
E/AndroidRuntime( 5656):    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
E/AndroidRuntime( 5656):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2571)
E/AndroidRuntime( 5656):    at android.os.Handler.dispatchMessage(Handler.java:106)
E/AndroidRuntime( 5656):    at android.os.Looper.loopOnce(Looper.java:226)
E/AndroidRuntime( 5656):    at android.os.Looper.loop(Looper.java:313)
E/AndroidRuntime( 5656):    at android.app.ActivityThread.main(ActivityThread.java:8741)
E/AndroidRuntime( 5656):    at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime( 5656):    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
E/AndroidRuntime( 5656):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)
E/AndroidRuntime( 5656): Caused by: androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment com.reactnativestripesdk.GooglePayFragment: could not find Fragment constructor
E/AndroidRuntime( 5656):    at androidx.fragment.app.Fragment.instantiate(Fragment.java:678)
E/AndroidRuntime( 5656):    at androidx.fragment.app.FragmentContainer.instantiate(FragmentContainer.java:57)
E/AndroidRuntime( 5656):    at androidx.fragment.app.FragmentManager$3.instantiate(FragmentManager.java:507)
E/AndroidRuntime( 5656):    at androidx.fragment.app.FragmentState.instantiate(FragmentState.java:81)
E/AndroidRuntime( 5656):    at androidx.fragment.app.FragmentStateManager.<init>(FragmentStateManager.java:85)
E/AndroidRuntime( 5656):    at androidx.fragment.app.FragmentManager.restoreSaveStateInternal(FragmentManager.java:2505)
E/AndroidRuntime( 5656):    at androidx.fragment.app.FragmentManager.attachController(FragmentManager.java:2665)
E/AndroidRuntime( 5656):    at androidx.fragment.app.FragmentController.attachHost(FragmentController.java:117)
E/AndroidRuntime( 5656):    at androidx.fragment.app.FragmentActivity.lambda$init$3$androidx-fragment-app-FragmentActivity(FragmentActivity.java:140)
E/AndroidRuntime( 5656):    at androidx.fragment.app.FragmentActivity$$ExternalSyntheticLambda0.onContextAvailable(Unknown Source:2)
E/AndroidRuntime( 5656):    at androidx.activity.contextaware.ContextAwareHelper.dispatchOnContextAvailable(ContextAwareHelper.java:99)
E/AndroidRuntime( 5656):    at androidx.activity.ComponentActivity.onCreate(ComponentActivity.java:362)
E/AndroidRuntime( 5656):    at androidx.fragment.app.FragmentActivity.onCreate(FragmentActivity.java:217)
E/AndroidRuntime( 5656):    at io.flutter.embedding.android.FlutterFragmentActivity.onCreate(FlutterFragmentActivity.java:279)
E/AndroidRuntime( 5656):    ... 17 more
E/AndroidRuntime( 5656): Caused by: java.lang.NoSuchMethodException: com.reactnativestripesdk.GooglePayFragment.<init> []
E/AndroidRuntime( 5656):    at java.lang.Class.getConstructor0(Class.java:2363)
E/AndroidRuntime( 5656):    at java.lang.Class.getConstructor(Class.java:1759)
E/AndroidRuntime( 5656):    at androidx.fragment.app.Fragment.instantiate(Fragment.java:663)
E/AndroidRuntime( 5656):    ... 30 more

A current workaround for us is to remove the two fragments manually: We remove PaymensSheetFragment immediately after calling Stripe.instance.initPaymentSheet(...) via a MethodChannel which calls this removePaymentSheetFragment(activity):

    fun removePaymentSheetFragment(activity: FlutterFragmentActivity) = removeFragmentWithTag(activity, com.reactnativestripesdk.PaymentSheetFragment.TAG)

    fun removeFragmentWithTag(activity: FlutterFragmentActivity, fragmentTag: String): Boolean{
        return activity.supportFragmentManager.findFragmentByTag(fragmentTag)?.let{ possibleStripeFragment ->
            activity.supportFragmentManager.beginTransaction()
                .remove(possibleStripeFragment)
                .commitAllowingStateLoss()
            Log.d(TAG, "successfully removed ${possibleStripeFragment::class.simpleName}")
            true
        } ?: run {
            Log.d(TAG, "didn't find a fragment to remove: $fragmentTag")
            false
        }
    }

And we do the same with GooglePayFragment, except we remove than in our MainActivity.onResume().

This doesn't feel safe however, as we don't know if there are any side-effects to this, but it seems to fix the crashes without affecting our own payment flow in particular.

Also, I don't know if there are other fragments besides these two in the library, that also don't have a no-arg constructor which could cause crashes too.

I imagine that following the answer here could fix this crash in the library, by creating a newInstance(...) function instead of using args in the Fragment-constructor.

vishnunew commented 1 year ago

Hello @remonh87, Any update on this ?

remonh87 commented 1 year ago

@thni-monta thank you for the great explanation. I did some studying and having a non 0 arg constructor looks indeed wrong. I have addressed the issue with Stripe since they should fix it in their sdk first, however at first hand it doesn't look easy to fix.

niklaesAtMonta commented 1 year ago

Any news on this @remonh87

incloudss commented 1 year ago

I have the same problem. Occurs on android 10+, on some of the devices. Not on all of them.

Caused by androidx.fragment.app.Fragment$k: Unable to instantiate fragment g.n.t0: could not find Fragment constructor
       at androidx.fragment.app.Fragment.instantiate(:94)
       at androidx.fragment.app.FragmentContainer.instantiate(FragmentContainer.java)
       at androidx.fragment.app.FragmentManager$2.instantiate(FragmentManager.java:17)
       at androidx.fragment.app.FragmentState.instantiate(FragmentState.java:2)
       at androidx.fragment.app.FragmentStateManager.<init>(FragmentStateManager.java:13)
       at androidx.fragment.app.FragmentManager.restoreSaveStateInternal(FragmentManager.java:119)
       at androidx.fragment.app.FragmentManager.attachController(FragmentManager.java:140)
       at androidx.fragment.app.FragmentController.attachHost(FragmentController.java:4)
       at androidx.fragment.app.FragmentActivity.lambda$init$1(FragmentActivity.java:3)
       at androidx.fragment.app.FragmentActivity.lambda$init$1$FragmentActivity(FragmentActivity.java)
       at androidx.fragment.app.-$$Lambda$FragmentActivity$QtiQ2ZI3e38UkO1_xuJ8vE_JZj4.onContextAvailable(lambda:2)
       at androidx.activity.contextaware.ContextAwareHelper.dispatchOnContextAvailable(ContextAwareHelper.java:20)
       at androidx.activity.ComponentActivity.onCreate(ComponentActivity.java:7)
       at androidx.fragment.app.FragmentActivity.onCreate(FragmentActivity.java)
       at io.flutter.embedding.android.FlutterFragmentActivity.onCreate(FlutterFragmentActivity.java:9)
       at android.app.Activity.performCreate(Activity.java:8238)
       at android.app.Activity.performCreate(Activity.java:8206)
       at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1329)
       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3710)
       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3904)
       at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)
       at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
       at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2259)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loopOnce(Looper.java:210)
       at android.os.Looper.loop(Looper.java:299)
       at android.app.ActivityThread.main(ActivityThread.java:8103)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:556)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1045)

I am not an android export, but couldn't the fragment be named better than g.n.t0? Or Is it some random name? How can i guess which fragment causes this crash and find the culprit plugin without the meanigful name? I use also in_app_purchases (do they use fragments?), but if i would have to guess, i would say that flutter_stripe is the culript. Do you have any news on that?

EDIT. Confirmed in debug mode that this is actually caused by flutter_stripe plugin. It crashes even though i never opened payment sheet at the first place.

androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment com.reactnativestripesdk.PaymentLauncherFragment

Honestly, for me this plugin is not production ready with such a huge bug.

remonh87 commented 11 months ago

@incloudss we are dependent on the Stripe team to fix this issue (see https://github.com/stripe/stripe-react-native/issues/1262 ) . As long as this is open there is nothing we can do.