Use getReactApplicationContextIfActiveOrWarn in modules that access JS or Native modules through ReactApplicationContext

Summary:
In D18032458 we introduce `getReactApplicationContextIfActiveOrWarn`. In this diff, modules that access a JS or Native module through ReactApplicationContext need to check if the CatalystInstance is still alive before continuing.

Changelog: [Internal]

Reviewed By: furdei

Differential Revision: D18032788

fbshipit-source-id: 5152783afd0b93b8ce0970fe4a509ea71396a54a
This commit is contained in:
Joshua Gross
2019-10-21 15:57:31 -07:00
committed by Facebook Github Bot
parent b12a29cfcb
commit 2ea33044bd
14 changed files with 291 additions and 300 deletions
@@ -116,10 +116,14 @@ public class NativeAnimatedModule extends ReactContextBaseJavaModule
@Override
public void initialize() {
ReactApplicationContext reactCtx = getReactApplicationContext();
UIManagerModule uiManager = reactCtx.getNativeModule(UIManagerModule.class);
reactCtx.addLifecycleEventListener(this);
uiManager.addUIManagerListener(this);
ReactApplicationContext reactApplicationContext =
getReactApplicationContextIfActiveOrWarn(NAME, "initialize");
if (reactApplicationContext != null) {
UIManagerModule uiManager = reactApplicationContext.getNativeModule(UIManagerModule.class);
reactApplicationContext.addLifecycleEventListener(this);
uiManager.addUIManagerListener(this);
}
}
@Override
@@ -175,9 +179,13 @@ public class NativeAnimatedModule extends ReactContextBaseJavaModule
private NativeAnimatedNodesManager getNodesManager() {
if (mNodesManager == null) {
UIManagerModule uiManager =
getReactApplicationContext().getNativeModule(UIManagerModule.class);
mNodesManager = new NativeAnimatedNodesManager(uiManager);
ReactApplicationContext reactApplicationContext =
getReactApplicationContextIfActiveOrWarn(NAME, "getNodesManager");
if (reactApplicationContext != null) {
UIManagerModule uiManager = reactApplicationContext.getNativeModule(UIManagerModule.class);
mNodesManager = new NativeAnimatedNodesManager(uiManager);
}
}
return mNodesManager;
@@ -219,9 +227,15 @@ public class NativeAnimatedModule extends ReactContextBaseJavaModule
WritableMap onAnimatedValueData = Arguments.createMap();
onAnimatedValueData.putInt("tag", tag);
onAnimatedValueData.putDouble("value", value);
getReactApplicationContext()
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("onAnimatedValueUpdate", onAnimatedValueData);
ReactApplicationContext reactApplicationContext =
getReactApplicationContextIfActiveOrWarn(
NAME, "startListeningToAnimatedNodeValue.onValueUpdate");
if (reactApplicationContext != null) {
reactApplicationContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("onAnimatedValueUpdate", onAnimatedValueData);
}
}
};
@@ -19,6 +19,8 @@ import java.io.File;
// requires it to already be initialized, thus we eagerly initialize this module
@ReactModule(name = "JSCHeapCapture", needsEagerInit = true)
public class JSCHeapCapture extends ReactContextBaseJavaModule {
public static final String TAG = JSCHeapCapture.class.getSimpleName();
public interface HeapCapture extends JavaScriptModule {
void captureHeap(String path);
}
@@ -54,13 +56,18 @@ public class JSCHeapCapture extends ReactContextBaseJavaModule {
File f = new File(path + "/capture.json");
f.delete();
HeapCapture heapCapture = getReactApplicationContext().getJSModule(HeapCapture.class);
if (heapCapture == null) {
callback.onFailure(new CaptureException("Heap capture js module not registered."));
return;
ReactApplicationContext reactApplicationContext =
getReactApplicationContextIfActiveOrWarn(TAG, "captureHeap");
if (reactApplicationContext != null) {
HeapCapture heapCapture = reactApplicationContext.getJSModule(HeapCapture.class);
if (heapCapture == null) {
callback.onFailure(new CaptureException("Heap capture js module not registered."));
return;
}
mCaptureInProgress = callback;
heapCapture.captureHeap(f.getPath());
}
mCaptureInProgress = callback;
heapCapture.captureHeap(f.getPath());
}
@ReactMethod
@@ -20,7 +20,6 @@ import java.util.Map;
@ReactModule(name = JSDevSupport.MODULE_NAME)
public class JSDevSupport extends ReactContextBaseJavaModule {
public static final String MODULE_NAME = "JSDevSupport";
public static final int ERROR_CODE_EXCEPTION = 0;
@@ -55,8 +54,14 @@ public class JSDevSupport extends ReactContextBaseJavaModule {
}
public synchronized void getJSHierarchy(int reactTag, DevSupportCallback callback) {
JSDevSupportModule jsDevSupportModule =
getReactApplicationContext().getJSModule(JSDevSupportModule.class);
ReactApplicationContext reactApplicationContext =
getReactApplicationContextIfActiveOrWarn(MODULE_NAME, "getJSHierarchy");
JSDevSupportModule jsDevSupportModule = null;
if (reactApplicationContext != null) {
jsDevSupportModule = reactApplicationContext.getJSModule(JSDevSupportModule.class);
}
if (jsDevSupportModule == null) {
callback.onFailure(
ERROR_CODE_EXCEPTION,
@@ -116,18 +116,29 @@ public class AccessibilityInfoModule extends ReactContextBaseJavaModule
if (mReduceMotionEnabled != isReduceMotionEnabled) {
mReduceMotionEnabled = isReduceMotionEnabled;
getReactApplicationContext()
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(REDUCE_MOTION_EVENT_NAME, mReduceMotionEnabled);
ReactApplicationContext reactApplicationContext =
getReactApplicationContextIfActiveOrWarn(NAME, "updateAndSendReduceMotionChangeEvent");
if (reactApplicationContext != null) {
reactApplicationContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(REDUCE_MOTION_EVENT_NAME, mReduceMotionEnabled);
}
}
}
private void updateAndSendTouchExplorationChangeEvent(boolean enabled) {
if (mTouchExplorationEnabled != enabled) {
mTouchExplorationEnabled = enabled;
getReactApplicationContext()
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(TOUCH_EXPLORATION_EVENT_NAME, mTouchExplorationEnabled);
ReactApplicationContext reactApplicationContext =
getReactApplicationContextIfActiveOrWarn(
NAME, "updateAndSendTouchExplorationChangeEvent");
if (reactApplicationContext != null) {
getReactApplicationContext()
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(TOUCH_EXPLORATION_EVENT_NAME, mTouchExplorationEnabled);
}
}
}
@@ -21,6 +21,7 @@ import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEm
/** Module that exposes the user's preferred color scheme. For API >= 29. */
@ReactModule(name = AppearanceModule.NAME)
public class AppearanceModule extends ReactContextBaseJavaModule {
public static final String NAME = "Appearance";
private static final String APPEARANCE_CHANGED_EVENT_NAME = "appearanceChanged";
@@ -85,8 +86,13 @@ public class AppearanceModule extends ReactContextBaseJavaModule {
WritableMap appearancePreferences = Arguments.createMap();
appearancePreferences.putString("colorScheme", colorScheme);
getReactApplicationContext()
.getJSModule(RCTDeviceEventEmitter.class)
.emit(APPEARANCE_CHANGED_EVENT_NAME, appearancePreferences);
ReactApplicationContext reactApplicationContext =
getReactApplicationContextIfActiveOrWarn(NAME, "emitAppearanceChanged");
if (reactApplicationContext != null) {
reactApplicationContext
.getJSModule(RCTDeviceEventEmitter.class)
.emit(APPEARANCE_CHANGED_EVENT_NAME, appearancePreferences);
}
}
}
@@ -286,37 +286,61 @@ public class BlobModule extends ReactContextBaseJavaModule {
return type;
}
private WebSocketModule getWebSocketModule() {
return getReactApplicationContext().getNativeModule(WebSocketModule.class);
private WebSocketModule getWebSocketModule(String reason) {
ReactApplicationContext reactApplicationContext =
getReactApplicationContextIfActiveOrWarn(NAME, reason);
if (reactApplicationContext != null) {
return reactApplicationContext.getNativeModule(WebSocketModule.class);
}
return null;
}
@ReactMethod
public void addNetworkingHandler() {
NetworkingModule networkingModule =
getReactApplicationContext().getNativeModule(NetworkingModule.class);
networkingModule.addUriHandler(mNetworkingUriHandler);
networkingModule.addRequestBodyHandler(mNetworkingRequestBodyHandler);
networkingModule.addResponseHandler(mNetworkingResponseHandler);
ReactApplicationContext reactApplicationContext =
getReactApplicationContextIfActiveOrWarn(NAME, "addNetworkingHandler");
if (reactApplicationContext != null) {
NetworkingModule networkingModule =
reactApplicationContext.getNativeModule(NetworkingModule.class);
networkingModule.addUriHandler(mNetworkingUriHandler);
networkingModule.addRequestBodyHandler(mNetworkingRequestBodyHandler);
networkingModule.addResponseHandler(mNetworkingResponseHandler);
}
}
@ReactMethod
public void addWebSocketHandler(final int id) {
getWebSocketModule().setContentHandler(id, mWebSocketContentHandler);
WebSocketModule webSocketModule = getWebSocketModule("addWebSocketHandler");
if (webSocketModule != null) {
webSocketModule.setContentHandler(id, mWebSocketContentHandler);
}
}
@ReactMethod
public void removeWebSocketHandler(final int id) {
getWebSocketModule().setContentHandler(id, null);
WebSocketModule webSocketModule = getWebSocketModule("removeWebSocketHandler");
if (webSocketModule != null) {
webSocketModule.setContentHandler(id, null);
}
}
@ReactMethod
public void sendOverSocket(ReadableMap blob, int id) {
byte[] data = resolve(blob.getString("blobId"), blob.getInt("offset"), blob.getInt("size"));
WebSocketModule webSocketModule = getWebSocketModule("sendOverSocket");
if (data != null) {
getWebSocketModule().sendBinary(ByteString.of(data), id);
} else {
getWebSocketModule().sendBinary((ByteString) null, id);
if (webSocketModule != null) {
byte[] data = resolve(blob.getString("blobId"), blob.getInt("offset"), blob.getInt("size"));
if (data != null) {
webSocketModule.sendBinary(ByteString.of(data), id);
} else {
webSocketModule.sendBinary((ByteString) null, id);
}
}
}
@@ -30,16 +30,29 @@ public class FileReaderModule extends ReactContextBaseJavaModule {
return NAME;
}
private BlobModule getBlobModule() {
return getReactApplicationContext().getNativeModule(BlobModule.class);
private BlobModule getBlobModule(String reason) {
ReactApplicationContext reactApplicationContext =
getReactApplicationContextIfActiveOrWarn(NAME, reason);
if (reactApplicationContext != null) {
return reactApplicationContext.getNativeModule(BlobModule.class);
}
return null;
}
@ReactMethod
public void readAsText(ReadableMap blob, String encoding, Promise promise) {
BlobModule blobModule = getBlobModule("readAsText");
if (blobModule == null) {
promise.reject(
new IllegalStateException("Could not get BlobModule from ReactApplicationContext"));
return;
}
byte[] bytes =
getBlobModule()
.resolve(blob.getString("blobId"), blob.getInt("offset"), blob.getInt("size"));
blobModule.resolve(blob.getString("blobId"), blob.getInt("offset"), blob.getInt("size"));
if (bytes == null) {
promise.reject(ERROR_INVALID_BLOB, "The specified blob is invalid");
@@ -55,9 +68,16 @@ public class FileReaderModule extends ReactContextBaseJavaModule {
@ReactMethod
public void readAsDataURL(ReadableMap blob, Promise promise) {
BlobModule blobModule = getBlobModule("readAsDataURL");
if (blobModule == null) {
promise.reject(
new IllegalStateException("Could not get BlobModule from ReactApplicationContext"));
return;
}
byte[] bytes =
getBlobModule()
.resolve(blob.getString("blobId"), blob.getInt("offset"), blob.getInt("size"));
blobModule.resolve(blob.getString("blobId"), blob.getInt("offset"), blob.getInt("size"));
if (bytes == null) {
promise.reject(ERROR_INVALID_BLOB, "The specified blob is invalid");
@@ -47,16 +47,26 @@ public class DeviceEventManagerModule extends ReactContextBaseJavaModule {
/** Sends an event to the JS instance that the hardware back has been pressed. */
public void emitHardwareBackPressed() {
getReactApplicationContext()
.getJSModule(RCTDeviceEventEmitter.class)
.emit("hardwareBackPress", null);
ReactApplicationContext reactApplicationContext =
getReactApplicationContextIfActiveOrWarn(NAME, "emitHardwareBackPressed");
if (reactApplicationContext != null) {
reactApplicationContext
.getJSModule(RCTDeviceEventEmitter.class)
.emit("hardwareBackPress", null);
}
}
/** Sends an event to the JS instance that a new intent was received. */
public void emitNewIntentReceived(Uri uri) {
WritableMap map = Arguments.createMap();
map.putString("url", uri.toString());
getReactApplicationContext().getJSModule(RCTDeviceEventEmitter.class).emit("url", map);
ReactApplicationContext reactApplicationContext =
getReactApplicationContextIfActiveOrWarn(NAME, "emitHardwareBackPressed");
if (reactApplicationContext != null) {
WritableMap map = Arguments.createMap();
map.putString("url", uri.toString());
reactApplicationContext.getJSModule(RCTDeviceEventEmitter.class).emit("url", map);
}
}
/**
@@ -65,6 +75,9 @@ public class DeviceEventManagerModule extends ReactContextBaseJavaModule {
*/
@ReactMethod
public void invokeDefaultBackPressHandler() {
// There should be no need to check if the catalyst instance is alive. After initialization
// the thread instances cannot be null, and scheduling on a thread after ReactApplicationContext
// teardown is a noop.
getReactApplicationContext().runOnUiQueueThread(mInvokeDefaultBackPressRunnable);
}
@@ -25,17 +25,32 @@ public final class TimingModule extends ReactContextBaseJavaModule
public class BridgeTimerManager implements JavaScriptTimerManager {
@Override
public void callTimers(WritableArray timerIDs) {
getReactApplicationContext().getJSModule(JSTimers.class).callTimers(timerIDs);
ReactApplicationContext reactApplicationContext =
getReactApplicationContextIfActiveOrWarn(NAME, "callTimers");
if (reactApplicationContext != null) {
reactApplicationContext.getJSModule(JSTimers.class).callTimers(timerIDs);
}
}
@Override
public void callIdleCallbacks(double frameTime) {
getReactApplicationContext().getJSModule(JSTimers.class).callIdleCallbacks(frameTime);
ReactApplicationContext reactApplicationContext =
getReactApplicationContextIfActiveOrWarn(NAME, "callIdleCallbacks");
if (reactApplicationContext != null) {
reactApplicationContext.getJSModule(JSTimers.class).callIdleCallbacks(frameTime);
}
}
@Override
public void emitTimeDriftWarning(String warningMessage) {
getReactApplicationContext().getJSModule(JSTimers.class).emitTimeDriftWarning(warningMessage);
ReactApplicationContext reactApplicationContext =
getReactApplicationContextIfActiveOrWarn(NAME, "emitTimeDriftWarning");
if (reactApplicationContext != null) {
reactApplicationContext.getJSModule(JSTimers.class).emitTimeDriftWarning(warningMessage);
}
}
}
@@ -92,9 +92,15 @@ public class DevSettingsModule extends ReactContextBaseJavaModule {
public void onOptionSelected() {
WritableMap data = Arguments.createMap();
data.putString("title", title);
getReactApplicationContext()
.getJSModule(RCTDeviceEventEmitter.class)
.emit("didPressMenuItem", data);
ReactApplicationContext reactApplicationContext =
getReactApplicationContextIfActiveOrWarn(NAME, "onOptionSelected");
if (reactApplicationContext != null) {
reactApplicationContext
.getJSModule(RCTDeviceEventEmitter.class)
.emit("didPressMenuItem", data);
}
}
});
}
@@ -249,7 +249,10 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
withCredentials);
} catch (Throwable th) {
FLog.e(TAG, "Failed to send url request: " + url, th);
ResponseUtil.onRequestError(getEventEmitter(), requestId, th.getMessage(), th);
final RCTDeviceEventEmitter eventEmitter = getEventEmitter("sendRequest error");
if (eventEmitter != null) {
ResponseUtil.onRequestError(eventEmitter, requestId, th.getMessage(), th);
}
}
}
@@ -264,7 +267,7 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
final boolean useIncrementalUpdates,
int timeout,
boolean withCredentials) {
final RCTDeviceEventEmitter eventEmitter = getEventEmitter();
final RCTDeviceEventEmitter eventEmitter = getEventEmitter("sendRequestInternal");
try {
Uri uri = Uri.parse(url);
@@ -664,7 +667,7 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
private @Nullable MultipartBody.Builder constructMultipartBody(
ReadableArray body, String contentType, int requestId) {
RCTDeviceEventEmitter eventEmitter = getEventEmitter();
RCTDeviceEventEmitter eventEmitter = getEventEmitter("constructMultipartBody");
MultipartBody.Builder multipartBuilder = new MultipartBody.Builder();
multipartBuilder.setType(MediaType.parse(contentType));
@@ -750,7 +753,14 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
return headersBuilder.build();
}
private RCTDeviceEventEmitter getEventEmitter() {
return getReactApplicationContext().getJSModule(RCTDeviceEventEmitter.class);
private RCTDeviceEventEmitter getEventEmitter(String reason) {
ReactApplicationContext reactApplicationContext =
getReactApplicationContextIfActiveOrWarn(TAG, reason);
if (reactApplicationContext != null) {
return getReactApplicationContext().getJSModule(RCTDeviceEventEmitter.class);
}
return null;
}
}
@@ -7,6 +7,7 @@
package com.facebook.react.modules.network;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
@@ -16,55 +17,69 @@ import java.net.SocketTimeoutException;
/** Util methods to send network responses to JS. */
public class ResponseUtil {
public static void onDataSend(
RCTDeviceEventEmitter eventEmitter, int requestId, long progress, long total) {
@Nullable RCTDeviceEventEmitter eventEmitter, int requestId, long progress, long total) {
WritableArray args = Arguments.createArray();
args.pushInt(requestId);
args.pushInt((int) progress);
args.pushInt((int) total);
eventEmitter.emit("didSendNetworkData", args);
if (eventEmitter != null) {
eventEmitter.emit("didSendNetworkData", args);
}
}
public static void onIncrementalDataReceived(
RCTDeviceEventEmitter eventEmitter, int requestId, String data, long progress, long total) {
@Nullable RCTDeviceEventEmitter eventEmitter,
int requestId,
String data,
long progress,
long total) {
WritableArray args = Arguments.createArray();
args.pushInt(requestId);
args.pushString(data);
args.pushInt((int) progress);
args.pushInt((int) total);
eventEmitter.emit("didReceiveNetworkIncrementalData", args);
if (eventEmitter != null) {
eventEmitter.emit("didReceiveNetworkIncrementalData", args);
}
}
public static void onDataReceivedProgress(
RCTDeviceEventEmitter eventEmitter, int requestId, long progress, long total) {
@Nullable RCTDeviceEventEmitter eventEmitter, int requestId, long progress, long total) {
WritableArray args = Arguments.createArray();
args.pushInt(requestId);
args.pushInt((int) progress);
args.pushInt((int) total);
eventEmitter.emit("didReceiveNetworkDataProgress", args);
if (eventEmitter != null) {
eventEmitter.emit("didReceiveNetworkDataProgress", args);
}
}
public static void onDataReceived(
RCTDeviceEventEmitter eventEmitter, int requestId, String data) {
@Nullable RCTDeviceEventEmitter eventEmitter, int requestId, String data) {
WritableArray args = Arguments.createArray();
args.pushInt(requestId);
args.pushString(data);
eventEmitter.emit("didReceiveNetworkData", args);
if (eventEmitter != null) {
eventEmitter.emit("didReceiveNetworkData", args);
}
}
public static void onDataReceived(
RCTDeviceEventEmitter eventEmitter, int requestId, WritableMap data) {
@Nullable RCTDeviceEventEmitter eventEmitter, int requestId, WritableMap data) {
WritableArray args = Arguments.createArray();
args.pushInt(requestId);
args.pushMap(data);
eventEmitter.emit("didReceiveNetworkData", args);
if (eventEmitter != null) {
eventEmitter.emit("didReceiveNetworkData", args);
}
}
public static void onRequestError(
RCTDeviceEventEmitter eventEmitter, int requestId, String error, Throwable e) {
@Nullable RCTDeviceEventEmitter eventEmitter, int requestId, String error, Throwable e) {
WritableArray args = Arguments.createArray();
args.pushInt(requestId);
args.pushString(error);
@@ -73,19 +88,23 @@ public class ResponseUtil {
args.pushBoolean(true); // last argument is a time out boolean
}
eventEmitter.emit("didCompleteNetworkResponse", args);
if (eventEmitter != null) {
eventEmitter.emit("didCompleteNetworkResponse", args);
}
}
public static void onRequestSuccess(RCTDeviceEventEmitter eventEmitter, int requestId) {
public static void onRequestSuccess(@Nullable RCTDeviceEventEmitter eventEmitter, int requestId) {
WritableArray args = Arguments.createArray();
args.pushInt(requestId);
args.pushNull();
eventEmitter.emit("didCompleteNetworkResponse", args);
if (eventEmitter != null) {
eventEmitter.emit("didCompleteNetworkResponse", args);
}
}
public static void onResponseReceived(
RCTDeviceEventEmitter eventEmitter,
@Nullable RCTDeviceEventEmitter eventEmitter,
int requestId,
int statusCode,
WritableMap headers,
@@ -96,6 +115,8 @@ public class ResponseUtil {
args.pushMap(headers);
args.pushString(url);
eventEmitter.emit("didReceiveNetworkResponse", args);
if (eventEmitter != null) {
eventEmitter.emit("didReceiveNetworkResponse", args);
}
}
}