In modules or classes that call ReactApplicationContext.getJSModule, ensure that there's still a CatalystInstance alive

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.

Modules that don't derive from `ReactContextBaseJavaModule` manually check for the catalyst impl and log their own SoftExceptions.

In this diff we also introduce SoftExceptions that by contract never cause crashes, even in debug mode.

Changelog: [Internal]

Reviewed By: mdvacca

Differential Revision: D18112989

fbshipit-source-id: 868f01f388aa2db3518db9f873f2afc2a62eed45
This commit is contained in:
Joshua Gross 2019-10-24 17:25:56 -07:00 коммит произвёл Facebook Github Bot
Родитель 070520beea
Коммит 94cb4bf90c
5 изменённых файлов: 59 добавлений и 13 удалений

Просмотреть файл

@ -0,0 +1,19 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.bridge;
/**
* Extends RuntimeException so that it may be caught by a {@link ReactSoftExceptionListener}. Any
* {@link ReactSoftExceptionListener} that catches a ReactNoCrashSoftException should log it only
* and not crash, no matter what.
*/
public class ReactNoCrashSoftException extends RuntimeException {
public ReactNoCrashSoftException(String detailMessage) {
super(detailMessage);
}
}

Просмотреть файл

@ -12,6 +12,8 @@ import androidx.annotation.Nullable;
import com.facebook.react.bridge.BaseJavaModule;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactNoCrashSoftException;
import com.facebook.react.bridge.ReactSoftException;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
@ -77,9 +79,16 @@ public class DeviceInfoModule extends BaseJavaModule
return;
}
mReactApplicationContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("didUpdateDimensions", DisplayMetricsHolder.getDisplayMetricsNativeMap(mFontScale));
if (mReactApplicationContext.hasActiveCatalystInstance()) {
mReactApplicationContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("didUpdateDimensions", DisplayMetricsHolder.getDisplayMetricsNativeMap(mFontScale));
} else {
ReactSoftException.logSoftException(
NAME,
new ReactNoCrashSoftException(
"No active CatalystInstance, cannot emitUpdateDimensionsEvent"));
}
}
@Override

Просмотреть файл

@ -25,6 +25,8 @@ import com.facebook.react.R;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactNoCrashSoftException;
import com.facebook.react.bridge.ReactSoftException;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
@ -278,9 +280,14 @@ public class ReactAccessibilityDelegate extends AccessibilityDelegateCompat {
final WritableMap event = Arguments.createMap();
event.putString("actionName", mAccessibilityActionsMap.get(action));
ReactContext reactContext = (ReactContext) host.getContext();
reactContext
.getJSModule(RCTEventEmitter.class)
.receiveEvent(host.getId(), "topAccessibilityAction", event);
if (reactContext.hasActiveCatalystInstance()) {
reactContext
.getJSModule(RCTEventEmitter.class)
.receiveEvent(host.getId(), "topAccessibilityAction", event);
} else {
ReactSoftException.logSoftException(
TAG, new ReactNoCrashSoftException("Cannot get RCTEventEmitter, no CatalystInstance"));
}
// In order to make Talkback announce the change of the adjustable's value,
// schedule to send a TYPE_VIEW_SELECTED event after performing the scroll actions.

Просмотреть файл

@ -20,11 +20,14 @@ import android.widget.TextView;
import androidx.annotation.Nullable;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactNoCrashSoftException;
import com.facebook.react.bridge.ReactSoftException;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.uimanager.NativeViewHierarchyOptimizer;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.Spacing;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.UIViewOperationQueue;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.events.RCTEventEmitter;
@ -157,14 +160,21 @@ public class ReactTextShadowNode extends ReactBaseTextShadowNode {
}
if (mShouldNotifyOnTextLayout) {
ThemedReactContext themedReactContext = getThemedContext();
WritableArray lines =
FontMetricsUtil.getFontMetrics(
text, layout, sTextPaintInstance, getThemedContext());
text, layout, sTextPaintInstance, themedReactContext);
WritableMap event = Arguments.createMap();
event.putArray("lines", lines);
getThemedContext()
.getJSModule(RCTEventEmitter.class)
.receiveEvent(getReactTag(), "topTextLayout", event);
if (themedReactContext.hasActiveCatalystInstance()) {
themedReactContext
.getJSModule(RCTEventEmitter.class)
.receiveEvent(getReactTag(), "topTextLayout", event);
} else {
ReactSoftException.logSoftException(
"ReactTextShadowNode",
new ReactNoCrashSoftException("Cannot get RCTEventEmitter, no CatalystInstance"));
}
}
if (mNumberOfLines != UNSET && mNumberOfLines < layout.getLineCount()) {

Просмотреть файл

@ -110,13 +110,14 @@ public class ReactTextView extends AppCompatTextView implements ReactCompoundVie
return;
}
if (!getReactContext().hasCatalystInstance()) {
ReactContext reactContext = getReactContext();
if (!reactContext.hasCatalystInstance()) {
// In bridgeless mode there's no Catalyst instance; in that case, bail.
// TODO (T45503888): Figure out how to support nested views from JS or cpp.
return;
}
UIManagerModule uiManager = getReactContext().getNativeModule(UIManagerModule.class);
UIManagerModule uiManager = reactContext.getNativeModule(UIManagerModule.class);
Spanned text = (Spanned) getText();
Layout layout = getLayout();
@ -253,7 +254,7 @@ public class ReactTextView extends AppCompatTextView implements ReactCompoundVie
WritableMap event = Arguments.createMap();
event.putArray("inlineViews", inlineViewInfoArray2);
getReactContext()
reactContext
.getJSModule(RCTEventEmitter.class)
.receiveEvent(getId(), "topInlineViewLayout", event);
}