Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.shopify.reactnative.checkoutkit;

import android.app.Activity;
import android.util.Log;
import androidx.activity.ComponentActivity;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactMethod;
Expand All @@ -19,6 +20,8 @@

public class ShopifyCheckoutKitModule extends NativeShopifyCheckoutKitSpec {

private static final String TAG = "ShopifyCheckoutKit";

public static Configuration checkoutConfig = new Configuration();

private CheckoutKitDialog checkoutSheet;
Expand Down Expand Up @@ -60,7 +63,7 @@ public void present(String checkoutURL, ReadableArray subscribedMethods) {

Activity currentActivity = getCurrentActivity();
if (currentActivity instanceof ComponentActivity) {
DispatchHandle dispatch = new DispatchHandle(json -> emitOnDispatch(json));
DispatchHandle dispatch = new DispatchHandle(this::emitWhenReactInstanceIsActive);
CustomCheckoutListener listener = new CustomCheckoutListener(dispatch);
checkoutListener = listener;

Expand Down Expand Up @@ -113,6 +116,31 @@ private void releaseCheckoutListener() {
}
}

/**
* Emits a dispatch envelope only while the React instance is still active.
*
* A checkout sheet can outlive the React runtime, so a late lifecycle or protocol event can
* arrive after teardown. The native emitter behind emitOnDispatch is freed at that point, and
* calling it reads freed memory and aborts the process.
*/
private void emitWhenReactInstanceIsActive(String json) {
if (!getReactApplicationContext().hasActiveReactInstance()) {
Log.w(TAG, "Dropping dispatch event, the React instance is no longer active.");
return;
}
emitOnDispatch(json);
}

/**
* Called by React Native when this module is torn down. Releases the listener so an orphaned
* checkout sheet cannot dispatch into the runtime that is going away.
*/
@Override
public void invalidate() {
releaseCheckoutListener();
super.invalidate();
}

@ReactMethod(isBlockingSynchronousMethod = true)
public WritableMap getConfig() {
WritableMap resultConfig = Arguments.createMap();
Expand Down
Loading