diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContextBaseJavaModule.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContextBaseJavaModule.java index bd796ae26db..33e95765681 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContextBaseJavaModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContextBaseJavaModule.java @@ -7,9 +7,14 @@ package com.facebook.react.bridge; +import static com.facebook.infer.annotation.ThreadConfined.ANY; + import android.app.Activity; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.facebook.common.logging.FLog; +import com.facebook.infer.annotation.ThreadConfined; +import com.facebook.react.common.build.ReactBuildConfig; /** * Base class for Catalyst native modules that require access to the {@link ReactContext} instance. @@ -22,11 +27,41 @@ public abstract class ReactContextBaseJavaModule extends BaseJavaModule { mReactApplicationContext = reactContext; } - /** Subclasses can use this method to access catalyst context passed as a constructor */ + /** Subclasses can use this method to access catalyst context passed as a constructor. */ protected final ReactApplicationContext getReactApplicationContext() { return mReactApplicationContext; } + /** + * Subclasses can use this method to access catalyst context passed as a constructor. Use this + * version to check that the underlying CatalystInstance is active before returning, and + * automatically have SoftExceptions or debug information logged for you. Consider using this + * whenever calling ReactApplicationContext methods that require the Catalyst instance be alive. + * + *

This can return null at any time, but especially during teardown methods it's + * possible/likely. + * + *

Threading implications have not been analyzed fully yet, so assume this method is not + * thread-safe. + */ + @ThreadConfined(ANY) + protected @Nullable final ReactApplicationContext getReactApplicationContextIfActiveOrWarn( + String tag, String reason) { + if (mReactApplicationContext.hasActiveCatalystInstance()) { + return mReactApplicationContext; + } + + // We want to collect data about how often this happens, but SoftExceptions will cause a crash + // in debug mode, which isn't usually desirable. + String msg = "Catalyst Instance has already disappeared: " + reason; + if (ReactBuildConfig.DEBUG) { + FLog.w(tag, msg); + } else { + ReactSoftException.logSoftException(tag, new RuntimeException(msg)); + } + return null; + } + /** * Get the activity to which this context is currently attached, or {@code null} if not attached. * diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/appstate/AppStateModule.java b/ReactAndroid/src/main/java/com/facebook/react/modules/appstate/AppStateModule.java index 9937b2f7291..e4c5f40dd55 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/appstate/AppStateModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/appstate/AppStateModule.java @@ -7,18 +7,15 @@ package com.facebook.react.modules.appstate; -import android.util.Log; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.LifecycleEventListener; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; -import com.facebook.react.bridge.ReactSoftException; import com.facebook.react.bridge.WindowFocusChangeListener; import com.facebook.react.bridge.WritableMap; import com.facebook.react.common.LifecycleState; -import com.facebook.react.common.build.ReactBuildConfig; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter; import java.util.HashMap; @@ -96,21 +93,12 @@ public class AppStateModule extends ReactContextBaseJavaModule } private void sendEvent(String eventName, @Nullable Object data) { - ReactApplicationContext reactApplicationContext = getReactApplicationContext(); + ReactApplicationContext reactApplicationContext = + getReactApplicationContextIfActiveOrWarn( + TAG, "sendAppStateChangeEvent: trying to update app state"); - if (reactApplicationContext.hasActiveCatalystInstance()) { + if (reactApplicationContext != null) { reactApplicationContext.getJSModule(RCTDeviceEventEmitter.class).emit(eventName, data); - } else { - // We want to collect data about how often this happens, but this will cause a crash - // in debug, which isn't desirable. - String msg = - "sendAppStateChangeEvent: trying to update app state when Catalyst Instance has already disappeared: " - + eventName; - if (ReactBuildConfig.DEBUG) { - Log.e(AppStateModule.TAG, msg); - } else { - ReactSoftException.logSoftException(AppStateModule.TAG, new RuntimeException(msg)); - } } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/websocket/WebSocketModule.java b/ReactAndroid/src/main/java/com/facebook/react/modules/websocket/WebSocketModule.java index 6684d9e33b5..d1e3d6a6c76 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/websocket/WebSocketModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/websocket/WebSocketModule.java @@ -7,22 +7,18 @@ package com.facebook.react.modules.websocket; -import android.util.Log; import androidx.annotation.Nullable; import com.facebook.common.logging.FLog; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; -import com.facebook.react.bridge.ReactSoftException; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableMapKeySetIterator; import com.facebook.react.bridge.ReadableType; import com.facebook.react.bridge.WritableMap; import com.facebook.react.common.ReactConstants; -import com.facebook.react.common.build.ReactBuildConfig; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.modules.core.DeviceEventManagerModule; import com.facebook.react.modules.network.ForwardingCookieHandler; @@ -43,6 +39,8 @@ import okio.ByteString; @ReactModule(name = WebSocketModule.NAME, hasConstants = false) public final class WebSocketModule extends ReactContextBaseJavaModule { + public static final String TAG = WebSocketModule.class.getSimpleName(); + public static final String NAME = "WebSocketModule"; public interface ContentHandler { @@ -54,30 +52,22 @@ public final class WebSocketModule extends ReactContextBaseJavaModule { private final Map mWebSocketConnections = new ConcurrentHashMap<>(); private final Map mContentHandlers = new ConcurrentHashMap<>(); - private ReactContext mReactContext; private ForwardingCookieHandler mCookieHandler; public WebSocketModule(ReactApplicationContext context) { super(context); - mReactContext = context; mCookieHandler = new ForwardingCookieHandler(context); } private void sendEvent(String eventName, WritableMap params) { - if (mReactContext.hasActiveCatalystInstance()) { - mReactContext + ReactApplicationContext reactApplicationContext = + getReactApplicationContextIfActiveOrWarn( + TAG, "sendAppStateChangeEvent: trying to update app state"); + + if (reactApplicationContext != null) { + reactApplicationContext .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) .emit(eventName, params); - } else { - // We want to collect data about how often this happens, but raising a SoftException in Debug - // will cause a crash, which isn't desirable. - String msg = - "sendEvent: trying to update app state when Catalyst Instance has already disappeared"; - if (ReactBuildConfig.DEBUG) { - Log.e(NAME, msg); - } else { - ReactSoftException.logSoftException(NAME, new IllegalStateException(msg)); - } } }