Expo and React Native - Context must be application context to avoid memory leak

547 Views Asked by At

I'm trying to implement a Visa SDK through React Native's Native Modules. I'm currently using Expo 46. So far, the application is registered correctly, and I can view the 3 methods that are exposed within the app.

Unfortunately, whenever I call the init() method in my app, the application crashes, and this message appears.

Context must be application context to avoid memory leak

Using adb logcat to further inspect the issue returns:

Method addObserver must be called on the main thread

Leading me to the following line:

ProcessLifecycleOwner.get().getLifecycle().addObserver(new VisaInAppCoreApplicationObserver());

After some research, I found: https://stackoverflow.com/a/70591942/1057052

Therefore, I tried running it like this:

 getCurrentActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    ProcessLifecycleOwner.get().getLifecycle().addObserver(new VisaInAppCoreApplicationObserver());
                }
            });

But no luck.

Here's my code so far.

// PushProvisioningModule.java
// adb logcat DEBUG-VISA:D *:S
package app.pana; // replace com.your-app-name with your app’s name

import android.util.Log;

import androidx.lifecycle.ProcessLifecycleOwner;

import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.google.gson.Gson;
import com.visa.mobileEnablement.inAppCore.VisaInAppConfig;
import com.visa.mobileEnablement.inAppCore.VisaInAppCore;
import com.visa.mobileEnablement.inAppCore.VisaInAppCoreApplicationObserver;
import com.visa.mobileEnablement.inAppCore.VisaInAppEnvironment;
import com.visa.mobileEnablement.pushProvisioning.VPCardProvisioningRequest;
import com.visa.mobileEnablement.pushProvisioning.VPCardProvisioningResponse;
import com.visa.mobileEnablement.pushProvisioning.VPError;
import com.visa.mobileEnablement.pushProvisioning.VPInitResponse;
import com.visa.mobileEnablement.pushProvisioning.VPSupportedWallet;
import com.visa.mobileEnablement.pushProvisioning.VPSupportedWalletCode;
import com.visa.mobileEnablement.pushProvisioning.VPSupportedWalletRequest;
import com.visa.mobileEnablement.pushProvisioning.VPSupportedWalletResponse;
import com.visa.mobileEnablement.pushProvisioning.VisaPushProvisioningInterface;
import com.visa.mobileEnablement.pushProvisioning.VisaPushProvisioningInterfaceFactory;
import com.visa.mobileEnablement.pushProvisioning.VisaPushProvisioningListener;



import java.util.List;

import org.jetbrains.annotations.NotNull;

// adb logcat DEBUG-VISA:D *:S

/**
 * Page 32 Visa In-App Provisioning SDK Guide .pdf
 */
public class PushProvisioningModule extends ReactContextBaseJavaModule {

    PushProvisioningModule(ReactApplicationContext context) {
        super(context);
    }

    private VisaPushProvisioningInterface visaPushProvisioningInterface;
    private Callback successCallback;
    private Callback errorCallback;
    private final Gson gson = new Gson();

    @NotNull
    @Override
    public String getName() {
        return "PushProvisioningModule";
    }

    // Initialize
    @ReactMethod
    public void init(
            String appId,
            String environment,
            Callback errorCallback,
            Callback successCallback
    ) {
        Log.d("DEBUG-VISA", "Got here here");
        this.errorCallback = errorCallback;
        this.successCallback = successCallback;
        initializeSdk(appId, environment);
    }

    private void initializeSdk(String appId, String environment) {
        // VPInitRequest vpInitRequest = new VPInitRequest(environment, appId, null);
        runConfigure(environment, appId);
        VisaPushProvisioningListener listener = new VisaPushProvisioningListener() {
            // Initialization success result:
            @Override
            public void initializationSuccess(
                    VisaPushProvisioningInterface visaPushProvisioningInterface,
                    VPInitResponse vpInitResponse
            ) {
                String nonce = vpInitResponse.getSignedNonce();
                successCallback.invoke(gson.toJson(vpInitResponse));
            }

            // Initialization failure result:
            @Override
            public void initializationFailure(
                    @NotNull VisaPushProvisioningInterface visaPushProvisioningInterface,
                    @NotNull VPError vpError
            ) {
                errorCallback.invoke(gson.toJson(vpError));
            }

            // startCardProvisioning success result:
            @Override
            public void cardProvisioningSuccess(
                    @NotNull VisaPushProvisioningInterface visaPushProvisioningInterface,
                    @NotNull VPCardProvisioningResponse vpCardProvisioningResponse
            ) {
                Log.i(
                        "CARD PROVISIONING",
                        "Card Provisioning Success : " + vpCardProvisioningResponse
                );
                successCallback.invoke(gson.toJson(vpCardProvisioningResponse));
            }

            // startCardProvisioning failure result:
            @Override
            public void cardProvisioningFailure(
                    @NotNull VisaPushProvisioningInterface visaPushProvisioningInterface,
                    @NotNull VPError vpError
            ) {
                errorCallback.invoke(gson.toJson(vpError));
            }

            // getSupportedWallets success result:
            @Override
            public void supportedWalletSuccess(
                    @NotNull VisaPushProvisioningInterface visaPushProvisioningInterface,
                    @NotNull VPSupportedWalletResponse vpSupportedWalletResponse
            ) {
                List<VPSupportedWallet> vpSupportedWallets = vpSupportedWalletResponse.getWallets();
                successCallback.invoke(gson.toJson(vpSupportedWallets));
            }

            // getSupportedWallets failure result:
            @Override
            public void supportedWalletFailure(
                    @NotNull VisaPushProvisioningInterface visaPushProvisioningInterface,
                    @NotNull VPError vpError
            ) {
                errorCallback.invoke(gson.toJson(vpError));
            }
        };
        //VisaPushProvisioningInterfaceFactory.INSTANCE.createPushProvisioningInterface(getReactApplicationContext().getApplicationContext(), vpInitRequest,
        visaPushProvisioningInterface =
                VisaPushProvisioningInterfaceFactory.INSTANCE.createPushProvisioningInterface(
                        listener,
                        null
                );
        visaPushProvisioningInterface.initialize();

        Log.d("DEBUG-VISA", "Initialization Successful");
    }

    private void runConfigure(String environment, String appId) {
        try {

            VisaInAppEnvironment configEnv = VisaInAppEnvironment.Production;
            //Log.d("DEBUG-VISA", "Configuration Environment", configEnv);
            VisaInAppConfig config = new VisaInAppConfig(configEnv, appId, null);
            ReactApplicationContext context = this.getReactApplicationContext();

            VisaInAppCore.Companion.configure(context, config);

            getCurrentActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    ProcessLifecycleOwner.get().getLifecycle().addObserver(new VisaInAppCoreApplicationObserver());
                }
            });


        } catch (Exception ex) {
            Log.d("DEBUG-VISA", "Exception thrown");
            Log.e("DEBUG-VISA", ex.getMessage());
        }
    }

    // to declare delegate methods and initialize (described below)  }
    // Get eligible wallets
    @ReactMethod
    public void launchGetWallets(
            String encPayload,
            Callback errorCallback,
            Callback successCallback
    ) {
        Log.i("DEBUG-VISA", "Launching GET WALLETS");
        this.errorCallback = errorCallback;
        this.successCallback = successCallback;
        Log.i("DEBUG-VISA", "Launching GET WALLETS after callbacks");

        try {
            VPSupportedWalletRequest request = new VPSupportedWalletRequest(encPayload, null);
            visaPushProvisioningInterface.getSupportedWallets(
                    request
            );


        } catch (Exception e) {
            Log.e("DEBUG-VISA", e.getMessage());
        }
    }

    // Start card provisioning
    @ReactMethod
    public void launchStartCardProvisioning(
            String walletDescription,
            String walletCode,
            Callback errorCallback,
            Callback successCallback
    ) {
        this.errorCallback = errorCallback;
        this.successCallback = successCallback;
        visaPushProvisioningInterface.startCardProvisioning(
                getReactApplicationContext().getApplicationContext(),
                new VPCardProvisioningRequest(
                        VPSupportedWalletCode.valueOf(walletCode),
                        "PROVISIONING_REQUEST",
                        null
                )
        );
    }
}

Here are the dependencies:

dependencies { 
     /**
    * Visa SDK - START
    */
    //In-App Provisioning SDK
    implementation files('libs/VisaPushProvisioning-3.3.0.aar')
    implementation files('libs/VisaInAppCore-3.3.0.aar')
    implementation files('libs/TMXProfiling-6.1-67.aar')
    implementation files('libs/TMXProfilingConnections-6.1-67.aar')
    implementation files('libs/dexguard-runtime-9.2.12.aar')
    //Samsung Pay
    implementation files('libs/samsungpaysdk-2.6.00.jar')
    //Google Pay
    implementation files('libs/play-services-tapandpay-18.1.0.aar')
    implementation "com.google.android.gms:play-services-base:18.0.0"
    //okhttp covers for okttp, okio dependency
    implementation "com.squareup.okhttp3:okhttp:3.14.9"
    implementation "com.squareup.okhttp3:logging-interceptor:3.14.9"
    implementation "net.minidev:json-smart:2.3"
    //For TrustDefender
    implementation "commons-io:commons-io:2.6"
    implementation "commons-codec:commons-codec:1.13"
    //Nimbus
    implementation "com.nimbusds:nimbus-jose-jwt:8.2.1"
    implementation "com.google.code.gson:gson:2.8.5"
    implementation "androidx.lifecycle:lifecycle-process:2.5.1"
    // If kotlin plugins are not added then below dependencies require to add to kotlin coroutines
    // used by SDK.
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
    //Analytics Database
    implementation "androidx.room:room-runtime:2.4.3"
    implementation "androidx.room:room-ktx:2.4.3"
    kapt "androidx.room:room-compiler:2.4.3"

    //Instant apps
    implementation "com.google.android.gms:play-services-instantapps:18.0.0"

    /**
    * Visa SDK - END
    */
}

Note, that I was successfully able to use the code above inside a non-expo react-native application.

0

There are 0 best solutions below