braintree / braintree_android

Braintree SDK for Android
https://developer.paypal.com/braintree/docs/start/hello-client/android/v4
MIT License
403 stars 232 forks source link

URGENT!! java.lang.IllegalStateException: Method addObserver must be called on the main thread #997

Closed thanh-fluz closed 3 months ago

thanh-fluz commented 4 months ago

Braintree SDK Version

4.45

Environment

Both

Android Version & Device

No response

Braintree dependencies

implementation 'com.braintreepayments.api:google-pay:4.45.0'
implementation 'com.braintreepayments.api:data-collector:4.45.0'
implementation 'com.braintreepayments.api:paypal-data-collector:4.45.0'
implementation 'com.braintreepayments.api:venmo:4.45.0'
implementation 'com.braintreepayments.api:paypal:4.45.0'
implementation 'com.braintreepayments.api:three-d-secure:4.45.0'

Describe the bug

I am getting an addObserver must be called on the main thread error after upgrading to braintree android v4.45 on android. I saw that the venmo Client class has a addObserver method. How come the googlePay Client doesnt?

https://github.com/thanh-fluz/react-native-braintree-no-ui/blob/master/android/src/main/java/com/pw/droplet/braintree/Braintree.java

To reproduce

Upgrade braintree to 4.45

Expected behavior

Running the without the "addObserver must be called on the main thread" error

Screenshots

image

sarahkoop commented 3 months ago

Can you share a snipped of your integration code? Constructing the feature clients in the onCreate method of your fragment or activity should resolve this error.

thanh-fluz commented 3 months ago

I saw that the venmo Client class has a addObserver method. How come the googlePay Client doesnt?

thanh-fluz commented 3 months ago

@sarahkoop Here is our integration. We followed the migration guide to v4+ the best we can but there are current items we are not 100% on as I am finding out now during testing.

https://github.com/thanh-fluz/react-native-braintree-no-ui/blob/master/android/src/main/java/com/pw/droplet/braintree/Braintree.java

sarahkoop commented 3 months ago

Both VenmoClient and GooglePayClient add observers in the same way. Unfortunately, we do not offer official support for react native, if there are react native specific integration issues.

thanh-fluz commented 3 months ago

@sarahkoop understood. But Why does google pay doesnt have a addObserver method? or if the GooglePayLifecycleObserver class was public, i could implement it like the snippet below in the GooglePayClient

    @VisibleForTesting
    GooglePayClient(FragmentActivity activity, Lifecycle lifecycle, BraintreeClient braintreeClient, GooglePayInternalClient internalGooglePayClient) {
        this.braintreeClient = braintreeClient;
        this.internalGooglePayClient = internalGooglePayClient;
        if (activity != null && lifecycle != null) {
            this.observer = new GooglePayLifecycleObserver(activity.getActivityResultRegistry(), this);
            lifecycle.addObserver(this.observer);
        }
    }
sarahkoop commented 3 months ago

I'm not sure I understand the use case here - the addObserver method is private in the VenmoClient and internal to the SDK, it is not meant to be integrated with directly.

thanh-fluz commented 3 months ago

@sarahkoop what i am trying to understand is if we need to implement our own lifecycle.addObserver(this.observer) and create our own observer?

sarahkoop commented 3 months ago

I recommend checking out our developer docs - they contain the necessary steps to integrate with the SDK. Implementing a custom observer is not necessary for integrating with the SDK.

thanh-fluz commented 3 months ago

I have the activity like this but I am still getting a "Method addObserver must be called on the main thread" error


public class MyActivity extends AppCompatActivity implements GooglePayListener {

  private BraintreeClient braintreeClient;
  private GooglePayClient googlePayClient;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    braintreeClient = new BraintreeClient(this, new ExampleClientTokenProvider());
    googlePayClient = new GooglePayClient(this, braintreeClient);
    googlePayClient.setListener(this);
  }

  public void onGooglePayButtonClick(View view) {
    GooglePayRequest googlePayRequest = new GooglePayRequest();
    googlePayRequest.setTransactionInfo(TransactionInfo.newBuilder()
        .setTotalPrice("1.00")
        .setTotalPriceStatus(WalletConstants.TOTAL_PRICE_STATUS_FINAL)
        .setCurrencyCode("USD")
        .build());
    googlePayRequest.setBillingAddressRequired(true);

    googlePayClient.requestPayment(this, googlePayRequest);
  }

  @Override
  public void onGooglePaySuccess(@NonNull PaymentMethodNonce paymentMethodNonce) {
      // send paymentMethodNonce.getString() to server
  }

  @Override
  public void onGooglePayFailure(@NonNull Exception error) {
    if (error instanceof UserCanceledException) {
      // user canceled
    } else {
      // handle error
    }
  }
}
sshropshire commented 3 months ago

Hey @thanh-fluz can you place the following in your onCreate() method to see if it evaluates to true?

boolean isMainThread = Looper.myLooper() == Looper.getMainLooper()

Source: Stackoverflow.com

Also, is this a pure Android activity i.e. without react native? I'm not as familiar with the React Native threading model, but at the moment we do have an implicit requirement that some of our feature clients must be instantiated and have their listeners added on the main thread for payment methods that perform either a browser switch or an app switch.

It's a limitation that we're working to improve in our next major version.