mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Refactor MountingManager into MountingManager + SurfaceMountingManager
Summary: This refactors MountingManager into a minimal API that shims into a more fully-featured SurfaceMountingManager. The SurfaceMountingManager keeps track of surface start/stop, surface ID, and surface Context. This solves a number of issues around (1) race conditions around StopSurface/StartSurface, (2) memory management of Views, (3) Concrete improvements: 1. Simpler to reason about race conditions around StopSurface/StartSurface. 2. 1:1 relationship between SurfaceId and all Views/tags. 3. When surface is stopped, all descendent Views can be GC'd immediately. 4. Fixed separation of concerns and leaky abstractions: surfaceId/rootTag and Surface Context are now stored and manipulated *only* in SurfaceMountingManager. 5. Simpler StopSurface flow: we simply remove references to all Views, and the Fragment (outside of the scope of this code) removes the RootView. This will trigger GC and we do ~0 work. Previously, we ran a REMOVE and DELETE instruction and kept track of each View in a HashMap. Now we can simply delete the map and move on. The caveat: NativeAnimated (or other native modules that go through UIManager). APIs like `updateProps` currently uses only the ReactTag and does not store SurfaceId. This is a good argument for moving away from ReactTag, at least in its current incarnation, but: for now this requires that you do a lookup of a ReactTag across N surfaces (worst-case) to determine which Surface a ReactTag is in. So, to summarize, the "con" of this approach is that now `getSurfaceManagerForViewEnforced` could be slower. It is used in: * NativeAnimatedModule calls `updateProps` through UIManager * FabricEventEmitter calls `receiveEvent` on FabricUIManager directly * On audit, I could find zero native callsites to `sendAccessibilityEvent` through UIManager Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D26000781 fbshipit-source-id: 386ae40c4333f8c584e05818c404868dbee6ce73
This commit is contained in:
committed by
Facebook GitHub Bot
parent
82e9cb1b4e
commit
29eb632f1c
@@ -84,7 +84,7 @@ public interface UIManager extends JSIModule, PerformanceCounter {
|
||||
* layout-related propertied won't be handled properly. Make sure you know what you're doing
|
||||
* before calling this method :)
|
||||
*
|
||||
* @param tag {@link int} that identifies the view that will be updated
|
||||
* @param reactTag {@link int} that identifies the view that will be updated
|
||||
* @param props {@link ReadableMap} props that should be immediately updated in view
|
||||
*/
|
||||
@UiThread
|
||||
|
||||
@@ -58,6 +58,7 @@ import com.facebook.react.fabric.events.EventBeatManager;
|
||||
import com.facebook.react.fabric.events.EventEmitterWrapper;
|
||||
import com.facebook.react.fabric.events.FabricEventEmitter;
|
||||
import com.facebook.react.fabric.mounting.MountingManager;
|
||||
import com.facebook.react.fabric.mounting.SurfaceMountingManager;
|
||||
import com.facebook.react.fabric.mounting.mountitems.DispatchCommandMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.DispatchIntCommandMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.DispatchStringCommandMountItem;
|
||||
@@ -83,7 +84,6 @@ import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
@@ -111,10 +111,6 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
@NonNull private final MountingManager mMountingManager;
|
||||
@NonNull private final EventDispatcher mEventDispatcher;
|
||||
|
||||
@NonNull
|
||||
private final ConcurrentHashMap<Integer, ThemedReactContext> mReactContextForRootTag =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
@NonNull private final EventBeatManager mEventBeatManager;
|
||||
|
||||
private boolean mInDispatch = false;
|
||||
@@ -183,13 +179,11 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
final int rootTag = ReactRootViewTagGenerator.getNextRootViewTag();
|
||||
ReactRoot reactRootView = (ReactRoot) rootView;
|
||||
|
||||
// TODO T31905686: Combine with startSurface below
|
||||
ThemedReactContext reactContext =
|
||||
new ThemedReactContext(
|
||||
mReactApplicationContext, rootView.getContext(), reactRootView.getSurfaceID());
|
||||
mMountingManager.addRootView(rootTag, rootView);
|
||||
mMountingManager.addRootView(rootTag, rootView, reactContext);
|
||||
String moduleName = reactRootView.getJSModuleName();
|
||||
mReactContextForRootTag.put(rootTag, reactContext);
|
||||
if (ENABLE_FABRIC_LOGS) {
|
||||
FLog.d(TAG, "Starting surface for module: %s and reactTag: %d", moduleName, rootTag);
|
||||
}
|
||||
@@ -223,8 +217,7 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
if (ENABLE_FABRIC_LOGS) {
|
||||
FLog.d(TAG, "Starting surface for module: %s and reactTag: %d", moduleName, rootTag);
|
||||
}
|
||||
mMountingManager.addRootView(rootTag, rootView);
|
||||
mReactContextForRootTag.put(rootTag, reactContext);
|
||||
mMountingManager.addRootView(rootTag, rootView, reactContext);
|
||||
|
||||
// If startSurface is executed in the UIThread then, it uses the ViewportOffset from the View,
|
||||
// Otherwise Fabric relies on calling {@link Binding#setConstraints} method to update the
|
||||
@@ -259,15 +252,8 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
@ThreadConfined(ANY)
|
||||
@Override
|
||||
public void stopSurface(final int surfaceID) {
|
||||
mReactContextForRootTag.remove(surfaceID);
|
||||
mBinding.stopSurface(surfaceID);
|
||||
UiThreadUtil.runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mMountingManager.deleteRootView(surfaceID);
|
||||
}
|
||||
});
|
||||
mMountingManager.stopSurface(surfaceID);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -328,14 +314,8 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
@Nullable Object stateWrapper,
|
||||
boolean isLayoutable) {
|
||||
|
||||
// This could be null if teardown/navigation away from a surface on the main thread happens
|
||||
// while a commit is being processed in a different thread. By contract we expect this to be
|
||||
// possible at teardown, but this race should *never* happen at startup.
|
||||
@Nullable ThemedReactContext context = mReactContextForRootTag.get(rootTag);
|
||||
|
||||
addPreAllocateMountItem(
|
||||
new PreAllocateViewMountItem(
|
||||
context,
|
||||
rootTag,
|
||||
reactTag,
|
||||
getFabricComponentName(componentName),
|
||||
@@ -350,12 +330,7 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
@ThreadConfined(ANY)
|
||||
private MountItem createIntBufferBatchMountItem(
|
||||
int rootTag, int[] intBuffer, Object[] objBuffer, int commitNumber) {
|
||||
// This could be null if teardown/navigation away from a surface on the main thread happens
|
||||
// while a commit is being processed in a different thread. By contract we expect this to be
|
||||
// possible at teardown, but this race should *never* happen at startup.
|
||||
@Nullable ThemedReactContext reactContext = mReactContextForRootTag.get(rootTag);
|
||||
|
||||
return new IntBufferBatchMountItem(rootTag, reactContext, intBuffer, objBuffer, commitNumber);
|
||||
return new IntBufferBatchMountItem(rootTag, intBuffer, objBuffer, commitNumber);
|
||||
}
|
||||
|
||||
@DoNotStrip
|
||||
@@ -398,7 +373,7 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
@DoNotStrip
|
||||
@SuppressWarnings("unused")
|
||||
private long measure(
|
||||
int rootTag,
|
||||
int surfaceId,
|
||||
String componentName,
|
||||
ReadableMap localData,
|
||||
ReadableMap props,
|
||||
@@ -409,16 +384,16 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
float maxHeight,
|
||||
@Nullable float[] attachmentsPositions) {
|
||||
|
||||
// This could be null if teardown/navigation away from a surface on the main thread happens
|
||||
// while a commit is being processed in a different thread. By contract we expect this to be
|
||||
// possible at teardown, but this race should *never* happen at startup.
|
||||
@Nullable
|
||||
ReactContext context =
|
||||
rootTag < 0 ? mReactApplicationContext : mReactContextForRootTag.get(rootTag);
|
||||
|
||||
// Don't both measuring if we can't get a context.
|
||||
if (context == null) {
|
||||
return 0;
|
||||
ReactContext context;
|
||||
if (surfaceId > 0) {
|
||||
SurfaceMountingManager surfaceMountingManager =
|
||||
mMountingManager.getSurfaceManagerEnforced(surfaceId, "measure");
|
||||
if (surfaceMountingManager.isStopped()) {
|
||||
return 0;
|
||||
}
|
||||
context = surfaceMountingManager.getContext();
|
||||
} else {
|
||||
context = mReactApplicationContext;
|
||||
}
|
||||
|
||||
return mMountingManager.measure(
|
||||
@@ -435,22 +410,16 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param surfaceID {@link int} surface ID
|
||||
* @param surfaceId {@link int} surface ID
|
||||
* @param defaultTextInputPadding {@link float[]} output parameter will contain the default theme
|
||||
* padding used by RN Android TextInput.
|
||||
* @return if theme data is available in the output parameters.
|
||||
*/
|
||||
@DoNotStrip
|
||||
public boolean getThemeData(int surfaceID, float[] defaultTextInputPadding) {
|
||||
ThemedReactContext themedReactContext = mReactContextForRootTag.get(surfaceID);
|
||||
if (themedReactContext == null) {
|
||||
// TODO T68526882: Review if this should cause a crash instead.
|
||||
ReactSoftException.logSoftException(
|
||||
TAG,
|
||||
new ReactNoCrashSoftException(
|
||||
"Unable to find ThemedReactContext associated to surfaceID: " + surfaceID));
|
||||
return false;
|
||||
}
|
||||
public boolean getThemeData(int surfaceId, float[] defaultTextInputPadding) {
|
||||
SurfaceMountingManager surfaceMountingManager =
|
||||
mMountingManager.getSurfaceManagerEnforced(surfaceId, "getThemeData");
|
||||
ThemedReactContext themedReactContext = surfaceMountingManager.getContext();
|
||||
float[] defaultTextInputPaddingForTheme =
|
||||
UIManagerHelper.getDefaultTextInputPadding(themedReactContext);
|
||||
defaultTextInputPadding[0] = defaultTextInputPaddingForTheme[PADDING_START_INDEX];
|
||||
@@ -615,9 +584,16 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to dispatch MountItems. Returns true if any items were dispatched, false otherwise. A
|
||||
* `false` return value doesn't indicate errors, it may just indicate there was no work to be
|
||||
* done.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@UiThread
|
||||
@ThreadConfined(UI)
|
||||
private void tryDispatchMountItems() {
|
||||
private boolean tryDispatchMountItems() {
|
||||
// If we're already dispatching, don't reenter.
|
||||
// Reentrance can potentially happen a lot on Android in Fabric because
|
||||
// `updateState` from the
|
||||
@@ -627,7 +603,7 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
// This is a pretty blunt tool, but we might not have better options since we really don't want
|
||||
// to execute anything out-of-order.
|
||||
if (mInDispatch) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
final boolean didDispatchItems;
|
||||
@@ -661,6 +637,7 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
tryDispatchMountItems();
|
||||
}
|
||||
mReDispatchCounter = 0;
|
||||
return didDispatchItems;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -694,35 +671,6 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
return drainConcurrentItemQueue(mPreMountItemsConcurrent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a surfaceId is active and ready for MountItems to be executed against it. It is safe
|
||||
* and cheap to call this repeatedly because we expect many operations to be batched with the same
|
||||
* surfaceId in a row and we memoize the parameters and results.
|
||||
*
|
||||
* @param surfaceId
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
@UiThread
|
||||
@ThreadConfined(UI)
|
||||
private boolean isSurfaceActiveForExecution(int surfaceId, String context) {
|
||||
boolean surfaceActive = mReactContextForRootTag.get(surfaceId) != null;
|
||||
|
||||
// If there are many MountItems with the same SurfaceId, we only
|
||||
// log a warning for the first one that is skipped.
|
||||
if (!surfaceActive) {
|
||||
ReactSoftException.logSoftException(
|
||||
TAG,
|
||||
new ReactNoCrashSoftException(
|
||||
"dispatchMountItems: skipping "
|
||||
+ context
|
||||
+ ", because surface not available: "
|
||||
+ surfaceId));
|
||||
}
|
||||
|
||||
return surfaceActive;
|
||||
}
|
||||
|
||||
private static void printMountItem(MountItem mountItem, String prefix) {
|
||||
// If a MountItem description is split across multiple lines, it's because it's a
|
||||
// compound MountItem. Log each line separately.
|
||||
@@ -812,10 +760,7 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
+ preMountItemsToDispatch.size());
|
||||
|
||||
for (PreAllocateViewMountItem preMountItem : preMountItemsToDispatch) {
|
||||
if (isSurfaceActiveForExecution(
|
||||
preMountItem.getRootTag(), "dispatchMountItems PreAllocateViewMountItem")) {
|
||||
preMountItem.execute(mMountingManager);
|
||||
}
|
||||
preMountItem.execute(mMountingManager);
|
||||
}
|
||||
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
@@ -834,16 +779,6 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
}
|
||||
|
||||
try {
|
||||
// Make sure surface associated with this MountItem has been started, and not stopped.
|
||||
// TODO T68118357: clean up this logic and simplify this method overall
|
||||
if (mountItem instanceof IntBufferBatchMountItem) {
|
||||
IntBufferBatchMountItem batchMountItem = (IntBufferBatchMountItem) mountItem;
|
||||
if (!isSurfaceActiveForExecution(
|
||||
batchMountItem.getRootTag(), "dispatchMountItems IntBufferBatchMountItem")) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
mountItem.execute(mMountingManager);
|
||||
} catch (Throwable e) {
|
||||
// If there's an exception, we want to log diagnostics in prod and rethrow.
|
||||
@@ -866,6 +801,17 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect if we still have processing time left in this frame.
|
||||
*
|
||||
* @param frameTimeNanos
|
||||
* @return
|
||||
*/
|
||||
private boolean haveExceededNonBatchedFrameTime(long frameTimeNanos) {
|
||||
long timeLeftInFrame = FRAME_TIME_MS - ((System.nanoTime() - frameTimeNanos) / 1000000);
|
||||
return timeLeftInFrame < MAX_TIME_IN_FRAME_FOR_NON_BATCHED_OPERATIONS_MS;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@ThreadConfined(UI)
|
||||
private void dispatchPreMountItems(long frameTimeNanos) {
|
||||
@@ -877,8 +823,7 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
long timeLeftInFrame = FRAME_TIME_MS - ((System.nanoTime() - frameTimeNanos) / 1000000);
|
||||
if (timeLeftInFrame < MAX_TIME_IN_FRAME_FOR_NON_BATCHED_OPERATIONS_MS) {
|
||||
if (haveExceededNonBatchedFrameTime(frameTimeNanos)) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -889,10 +834,7 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
break;
|
||||
}
|
||||
|
||||
if (isSurfaceActiveForExecution(
|
||||
preMountItemToDispatch.getRootTag(), "dispatchPreMountItems")) {
|
||||
preMountItemToDispatch.execute(mMountingManager);
|
||||
}
|
||||
preMountItemToDispatch.execute(mMountingManager);
|
||||
}
|
||||
} finally {
|
||||
mInDispatch = false;
|
||||
@@ -912,7 +854,7 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
@UiThread
|
||||
@ThreadConfined(UI)
|
||||
public void updateRootLayoutSpecs(
|
||||
final int rootTag,
|
||||
final int surfaceId,
|
||||
final int widthMeasureSpec,
|
||||
final int heightMeasureSpec,
|
||||
final int offsetX,
|
||||
@@ -922,7 +864,9 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
FLog.d(TAG, "Updating Root Layout Specs");
|
||||
}
|
||||
|
||||
ThemedReactContext reactContext = mReactContextForRootTag.get(rootTag);
|
||||
SurfaceMountingManager surfaceMountingManager =
|
||||
mMountingManager.getSurfaceManagerEnforced(surfaceId, "updateRootLayoutSpecs");
|
||||
ThemedReactContext reactContext = surfaceMountingManager.getContext();
|
||||
boolean isRTL = false;
|
||||
boolean doLeftAndRightSwapInRTL = false;
|
||||
if (reactContext != null) {
|
||||
@@ -931,7 +875,7 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
}
|
||||
|
||||
mBinding.setConstraints(
|
||||
rootTag,
|
||||
surfaceId,
|
||||
getMinSize(widthMeasureSpec),
|
||||
getMaxSize(widthMeasureSpec),
|
||||
getMinSize(heightMeasureSpec),
|
||||
@@ -976,21 +920,48 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
@Override
|
||||
public void onHostDestroy() {}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
@Deprecated
|
||||
@AnyThread
|
||||
@ThreadConfined(ANY)
|
||||
public void dispatchCommand(
|
||||
final int reactTag, final int commandId, @Nullable final ReadableArray commandArgs) {
|
||||
dispatchCommandMountItem(new DispatchIntCommandMountItem(reactTag, commandId, commandArgs));
|
||||
throw new UnsupportedOperationException(
|
||||
"dispatchCommand called without surfaceId - Fabric dispatchCommand must be called through Fabric JSI API");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
@AnyThread
|
||||
@ThreadConfined(ANY)
|
||||
public void dispatchCommand(
|
||||
final int reactTag, final String commandId, @Nullable final ReadableArray commandArgs) {
|
||||
dispatchCommandMountItem(new DispatchStringCommandMountItem(reactTag, commandId, commandArgs));
|
||||
throw new UnsupportedOperationException(
|
||||
"dispatchCommand called without surfaceId - Fabric dispatchCommand must be called through Fabric JSI API");
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@AnyThread
|
||||
@ThreadConfined(ANY)
|
||||
public void dispatchCommand(
|
||||
final int surfaceId,
|
||||
final int reactTag,
|
||||
final int commandId,
|
||||
@Nullable final ReadableArray commandArgs) {
|
||||
dispatchCommandMountItem(
|
||||
new DispatchIntCommandMountItem(surfaceId, reactTag, commandId, commandArgs));
|
||||
}
|
||||
|
||||
@DoNotStrip
|
||||
@AnyThread
|
||||
@ThreadConfined(ANY)
|
||||
public void dispatchCommand(
|
||||
final int surfaceId,
|
||||
final int reactTag,
|
||||
final String commandId,
|
||||
@Nullable final ReadableArray commandArgs) {
|
||||
dispatchCommandMountItem(
|
||||
new DispatchStringCommandMountItem(surfaceId, reactTag, commandId, commandArgs));
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@@ -1003,12 +974,15 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
@AnyThread
|
||||
@ThreadConfined(ANY)
|
||||
public void sendAccessibilityEvent(int reactTag, int eventType) {
|
||||
addMountItem(new SendAccessibilityEvent(reactTag, eventType));
|
||||
// Can be called from native, not just JS - we need to migrate the native callsites
|
||||
// before removing this entirely.
|
||||
addMountItem(new SendAccessibilityEvent(-1, reactTag, eventType));
|
||||
}
|
||||
|
||||
@DoNotStrip
|
||||
@AnyThread
|
||||
@ThreadConfined(ANY)
|
||||
public void sendAccessibilityEventFromJS(int reactTag, String eventTypeJS) {
|
||||
public void sendAccessibilityEventFromJS(int surfaceId, int reactTag, String eventTypeJS) {
|
||||
int eventType;
|
||||
if ("focus".equals(eventTypeJS)) {
|
||||
eventType = AccessibilityEvent.TYPE_VIEW_FOCUSED;
|
||||
@@ -1020,7 +994,7 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
throw new IllegalArgumentException(
|
||||
"sendAccessibilityEventFromJS: invalid eventType " + eventTypeJS);
|
||||
}
|
||||
addMountItem(new SendAccessibilityEvent(reactTag, eventType));
|
||||
addMountItem(new SendAccessibilityEvent(surfaceId, reactTag, eventType));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1032,12 +1006,16 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
*/
|
||||
@DoNotStrip
|
||||
public void setJSResponder(
|
||||
final int reactTag, final int initialReactTag, final boolean blockNativeResponder) {
|
||||
final int surfaceId,
|
||||
final int reactTag,
|
||||
final int initialReactTag,
|
||||
final boolean blockNativeResponder) {
|
||||
addMountItem(
|
||||
new MountItem() {
|
||||
@Override
|
||||
public void execute(MountingManager mountingManager) {
|
||||
mountingManager.setJSResponder(reactTag, initialReactTag, blockNativeResponder);
|
||||
mountingManager.setJSResponder(
|
||||
surfaceId, reactTag, initialReactTag, blockNativeResponder);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1161,8 +1139,13 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
|
||||
try {
|
||||
dispatchPreMountItems(frameTimeNanos);
|
||||
boolean dispatchedMountItems = tryDispatchMountItems();
|
||||
|
||||
tryDispatchMountItems();
|
||||
// Only if we did no work (besides preallocation) and have time left, evict stale
|
||||
// SurfaceMountingManagers
|
||||
if (!dispatchedMountItems && !haveExceededNonBatchedFrameTime(frameTimeNanos)) {
|
||||
mMountingManager.evictStaleSurfaces();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
FLog.e(TAG, "Exception thrown when executing UIFrameGuarded", ex);
|
||||
stop();
|
||||
|
||||
@@ -1062,7 +1062,7 @@ void Binding::schedulerDidDispatchCommand(
|
||||
|
||||
static auto dispatchCommand =
|
||||
jni::findClassStatic(Binding::UIManagerJavaDescriptor)
|
||||
->getMethod<void(jint, jstring, ReadableArray::javaobject)>(
|
||||
->getMethod<void(jint, jint, jstring, ReadableArray::javaobject)>(
|
||||
"dispatchCommand");
|
||||
|
||||
local_ref<JString> command = make_jstring(commandName);
|
||||
@@ -1071,7 +1071,11 @@ void Binding::schedulerDidDispatchCommand(
|
||||
castReadableArray(ReadableNativeArray::newObjectCxxArgs(args));
|
||||
|
||||
dispatchCommand(
|
||||
localJavaUIManager, shadowView.tag, command.get(), argsArray.get());
|
||||
localJavaUIManager,
|
||||
shadowView.surfaceId,
|
||||
shadowView.tag,
|
||||
command.get(),
|
||||
argsArray.get());
|
||||
}
|
||||
|
||||
void Binding::schedulerDidSendAccessibilityEvent(
|
||||
@@ -1088,10 +1092,14 @@ void Binding::schedulerDidSendAccessibilityEvent(
|
||||
|
||||
static auto sendAccessibilityEventFromJS =
|
||||
jni::findClassStatic(Binding::UIManagerJavaDescriptor)
|
||||
->getMethod<void(jint, jstring)>("sendAccessibilityEventFromJS");
|
||||
->getMethod<void(jint, jint, jstring)>(
|
||||
"sendAccessibilityEventFromJS");
|
||||
|
||||
sendAccessibilityEventFromJS(
|
||||
localJavaUIManager, shadowView.tag, eventTypeStr.get());
|
||||
localJavaUIManager,
|
||||
shadowView.surfaceId,
|
||||
shadowView.tag,
|
||||
eventTypeStr.get());
|
||||
}
|
||||
|
||||
void Binding::schedulerDidSetJSResponder(
|
||||
@@ -1107,10 +1115,11 @@ void Binding::schedulerDidSetJSResponder(
|
||||
|
||||
static auto setJSResponder =
|
||||
jni::findClassStatic(Binding::UIManagerJavaDescriptor)
|
||||
->getMethod<void(jint, jint, jboolean)>("setJSResponder");
|
||||
->getMethod<void(jint, jint, jint, jboolean)>("setJSResponder");
|
||||
|
||||
setJSResponder(
|
||||
localJavaUIManager,
|
||||
shadowView.surfaceId,
|
||||
shadowView.tag,
|
||||
initialShadowView.tag,
|
||||
(jboolean)blockNativeResponder);
|
||||
|
||||
+212
-705
File diff suppressed because it is too large
Load Diff
+822
@@ -0,0 +1,822 @@
|
||||
/*
|
||||
* 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.fabric.mounting;
|
||||
|
||||
import static com.facebook.infer.annotation.ThreadConfined.ANY;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
import androidx.annotation.AnyThread;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.UiThread;
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
import com.facebook.infer.annotation.ThreadConfined;
|
||||
import com.facebook.react.bridge.ReactSoftException;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.ReadableNativeMap;
|
||||
import com.facebook.react.bridge.RetryableMountingLayerException;
|
||||
import com.facebook.react.bridge.SoftAssertions;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
import com.facebook.react.common.build.ReactBuildConfig;
|
||||
import com.facebook.react.fabric.events.EventEmitterWrapper;
|
||||
import com.facebook.react.touch.JSResponderHandler;
|
||||
import com.facebook.react.uimanager.IllegalViewOperationException;
|
||||
import com.facebook.react.uimanager.ReactStylesDiffMap;
|
||||
import com.facebook.react.uimanager.RootView;
|
||||
import com.facebook.react.uimanager.RootViewManager;
|
||||
import com.facebook.react.uimanager.StateWrapper;
|
||||
import com.facebook.react.uimanager.ThemedReactContext;
|
||||
import com.facebook.react.uimanager.ViewGroupManager;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
import com.facebook.react.uimanager.ViewManagerRegistry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class SurfaceMountingManager {
|
||||
public static final String TAG = SurfaceMountingManager.class.getSimpleName();
|
||||
private static final boolean SHOW_CHANGED_VIEW_HIERARCHIES = ReactBuildConfig.DEBUG && false;
|
||||
private static final long KEEPALIVE_MILLISECONDS = 1000;
|
||||
|
||||
private volatile boolean mIsStopped = false;
|
||||
|
||||
@NonNull private final ThemedReactContext mThemedReactContext;
|
||||
|
||||
// These are all non-null, until StopSurface is called
|
||||
private ConcurrentHashMap<Integer, ViewState> mTagToViewState =
|
||||
new ConcurrentHashMap<>(); // any thread
|
||||
private JSResponderHandler mJSResponderHandler;
|
||||
private ViewManagerRegistry mViewManagerRegistry;
|
||||
private RootViewManager mRootViewManager;
|
||||
|
||||
// This is null *until* StopSurface is called.
|
||||
private Set<Integer> mTagSetForStoppedSurface;
|
||||
private long mLastSuccessfulQueryTime = -1;
|
||||
|
||||
private final int mSurfaceId;
|
||||
|
||||
public SurfaceMountingManager(
|
||||
int surfaceId,
|
||||
@NonNull final View rootView,
|
||||
@NonNull JSResponderHandler jsResponderHandler,
|
||||
@NonNull ViewManagerRegistry viewManagerRegistry,
|
||||
@NonNull RootViewManager rootViewManager,
|
||||
@NonNull ThemedReactContext context) {
|
||||
mSurfaceId = surfaceId;
|
||||
mJSResponderHandler = jsResponderHandler;
|
||||
mViewManagerRegistry = viewManagerRegistry;
|
||||
mRootViewManager = rootViewManager;
|
||||
mThemedReactContext = context;
|
||||
|
||||
addRootView(rootView);
|
||||
}
|
||||
|
||||
public boolean isStopped() {
|
||||
return mIsStopped;
|
||||
}
|
||||
|
||||
public boolean shouldKeepAliveStoppedSurface() {
|
||||
assert mIsStopped;
|
||||
return (System.currentTimeMillis() - mLastSuccessfulQueryTime) < KEEPALIVE_MILLISECONDS;
|
||||
}
|
||||
|
||||
public ThemedReactContext getContext() {
|
||||
return mThemedReactContext;
|
||||
}
|
||||
|
||||
private static void logViewHierarchy(ViewGroup parent, boolean recurse) {
|
||||
int parentTag = parent.getId();
|
||||
FLog.e(TAG, " <ViewGroup tag=" + parentTag + " class=" + parent.getClass().toString() + ">");
|
||||
for (int i = 0; i < parent.getChildCount(); i++) {
|
||||
FLog.e(
|
||||
TAG,
|
||||
" <View idx="
|
||||
+ i
|
||||
+ " tag="
|
||||
+ parent.getChildAt(i).getId()
|
||||
+ " class="
|
||||
+ parent.getChildAt(i).getClass().toString()
|
||||
+ ">");
|
||||
}
|
||||
FLog.e(TAG, " </ViewGroup tag=" + parentTag + ">");
|
||||
|
||||
if (recurse) {
|
||||
FLog.e(TAG, "Displaying Ancestors:");
|
||||
ViewParent ancestor = parent.getParent();
|
||||
while (ancestor != null) {
|
||||
ViewGroup ancestorViewGroup = (ancestor instanceof ViewGroup ? (ViewGroup) ancestor : null);
|
||||
int ancestorId = ancestorViewGroup == null ? View.NO_ID : ancestorViewGroup.getId();
|
||||
FLog.e(
|
||||
TAG,
|
||||
"<ViewParent tag=" + ancestorId + " class=" + ancestor.getClass().toString() + ">");
|
||||
ancestor = ancestor.getParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getViewExists(int tag) {
|
||||
// If Surface stopped, check if tag *was* associated with this Surface, even though it's been
|
||||
// deleted. This helps distinguish between scenarios where an invalid tag is referenced, vs
|
||||
// race conditions where an imperative method is called on a tag during/just after StopSurface.
|
||||
if (mTagSetForStoppedSurface != null && mTagSetForStoppedSurface.contains(tag)) {
|
||||
mLastSuccessfulQueryTime = System.currentTimeMillis();
|
||||
return true;
|
||||
}
|
||||
if (mTagToViewState == null) {
|
||||
return false;
|
||||
}
|
||||
return mTagToViewState.containsKey(tag);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
private void addRootView(@NonNull final View rootView) {
|
||||
// Since this is called from the constructor, we know the surface cannot have stopped yet.
|
||||
mTagToViewState.put(mSurfaceId, new ViewState(mSurfaceId, rootView, mRootViewManager, true));
|
||||
|
||||
UiThreadUtil.runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// The CPU has ticked since `addRootView` was called, so the surface could technically
|
||||
// have already stopped here.
|
||||
if (isStopped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (rootView.getId() != View.NO_ID) {
|
||||
FLog.e(
|
||||
TAG,
|
||||
"Trying to add RootTag to RootView that already has a tag: existing tag: [%d] new tag: [%d]",
|
||||
rootView.getId(),
|
||||
mSurfaceId);
|
||||
throw new IllegalViewOperationException(
|
||||
"Trying to add a root view with an explicit id already set. React Native uses "
|
||||
+ "the id field to track react tags and will overwrite this field. If that is fine, "
|
||||
+ "explicitly overwrite the id field to View.NO_ID before calling addRootView.");
|
||||
}
|
||||
rootView.setId(mSurfaceId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop surface and all operations within it. Garbage-collect Views (caller is responsible for
|
||||
* removing RootView from View layer).
|
||||
*
|
||||
* <p>Delete rootView from cache. Since RN does not control the RootView, in a sense, the fragment
|
||||
* is responsible for actually removing the RootView from the hierarchy / tearing down the
|
||||
* fragment.
|
||||
*
|
||||
* <p>In the original version(s) of this function, we recursively went through all children of the
|
||||
* View and dropped those Views as well; ad infinitum. This was before we had a
|
||||
* SurfaceMountingManager, and all tags were in one global map. Doing this was particularly
|
||||
* important in the case of StopSurface, where race conditions between threads meant you couldn't
|
||||
* rely on DELETE instructions actually deleting all Views in the Surface.
|
||||
*
|
||||
* <p>Now that we have SurfaceMountingManager, we can simply drop our local reference to the View.
|
||||
* Since it will be removed from the View hierarchy entirely (outside of the scope of this class),
|
||||
* garbage collection will take care of destroying it and all descendents.
|
||||
*/
|
||||
@AnyThread
|
||||
public void stopSurface() {
|
||||
if (isStopped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent more views from being created, or the hierarchy from being manipulated at all
|
||||
mIsStopped = true;
|
||||
|
||||
Runnable runnable =
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Evict all views from cache and memory
|
||||
mLastSuccessfulQueryTime = System.currentTimeMillis();
|
||||
mTagSetForStoppedSurface = mTagToViewState.keySet();
|
||||
mTagToViewState = null;
|
||||
mJSResponderHandler = null;
|
||||
mRootViewManager = null;
|
||||
}
|
||||
};
|
||||
|
||||
if (UiThreadUtil.isOnUiThread()) {
|
||||
runnable.run();
|
||||
} else {
|
||||
UiThreadUtil.runOnUiThread(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void addViewAt(final int parentTag, final int tag, final int index) {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
if (isStopped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ViewState parentViewState = getViewState(parentTag);
|
||||
if (!(parentViewState.mView instanceof ViewGroup)) {
|
||||
String message =
|
||||
"Unable to add a view into a view that is not a ViewGroup. ParentTag: "
|
||||
+ parentTag
|
||||
+ " - Tag: "
|
||||
+ tag
|
||||
+ " - Index: "
|
||||
+ index;
|
||||
FLog.e(TAG, message);
|
||||
throw new IllegalStateException(message);
|
||||
}
|
||||
final ViewGroup parentView = (ViewGroup) parentViewState.mView;
|
||||
ViewState viewState = getViewState(tag);
|
||||
final View view = viewState.mView;
|
||||
if (view == null) {
|
||||
throw new IllegalStateException(
|
||||
"Unable to find view for viewState " + viewState + " and tag " + tag);
|
||||
}
|
||||
|
||||
// Display children before inserting
|
||||
if (SHOW_CHANGED_VIEW_HIERARCHIES) {
|
||||
FLog.e(TAG, "addViewAt: [" + tag + "] -> [" + parentTag + "] idx: " + index + " BEFORE");
|
||||
logViewHierarchy(parentView, false);
|
||||
}
|
||||
|
||||
try {
|
||||
getViewGroupManager(parentViewState).addView(parentView, view, index);
|
||||
} catch (IllegalStateException e) {
|
||||
// Wrap error with more context for debugging
|
||||
throw new IllegalStateException(
|
||||
"addViewAt: failed to insert view ["
|
||||
+ tag
|
||||
+ "] into parent ["
|
||||
+ parentTag
|
||||
+ "] at index "
|
||||
+ index,
|
||||
e);
|
||||
}
|
||||
|
||||
// Display children after inserting
|
||||
if (SHOW_CHANGED_VIEW_HIERARCHIES) {
|
||||
// Why are we calling `runOnUiThread`? We're already on the UI thread, right?!
|
||||
// Yes - but if you get the children of the View here and display them, *it might show you
|
||||
// the previous children*. Without getting too much into Android internals, basically if we
|
||||
// wait a tick, everything is what we expect.
|
||||
// tldr is that `parent.children == []; parent.addView(x); parent.children == []`
|
||||
// and you need to wait a tick for `parent.children == [x]`.
|
||||
UiThreadUtil.runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
FLog.e(
|
||||
TAG, "addViewAt: [" + tag + "] -> [" + parentTag + "] idx: " + index + " AFTER");
|
||||
logViewHierarchy(parentView, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void removeViewAt(final int tag, final int parentTag, int index) {
|
||||
if (isStopped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
ViewState viewState = getNullableViewState(parentTag);
|
||||
|
||||
// TODO: throw exception here?
|
||||
if (viewState == null) {
|
||||
ReactSoftException.logSoftException(
|
||||
MountingManager.TAG,
|
||||
new IllegalStateException(
|
||||
"Unable to find viewState for tag: [" + parentTag + "] for removeViewAt"));
|
||||
return;
|
||||
}
|
||||
|
||||
final ViewGroup parentView = (ViewGroup) viewState.mView;
|
||||
|
||||
if (parentView == null) {
|
||||
throw new IllegalStateException("Unable to find view for tag [" + parentTag + "]");
|
||||
}
|
||||
|
||||
if (SHOW_CHANGED_VIEW_HIERARCHIES) {
|
||||
// Display children before deleting any
|
||||
FLog.e(TAG, "removeViewAt: [" + tag + "] -> [" + parentTag + "] idx: " + index + " BEFORE");
|
||||
logViewHierarchy(parentView, false);
|
||||
}
|
||||
|
||||
ViewGroupManager<ViewGroup> viewGroupManager = getViewGroupManager(viewState);
|
||||
|
||||
// Verify that the view we're about to remove has the same tag we expect
|
||||
View view = viewGroupManager.getChildAt(parentView, index);
|
||||
int actualTag = (view != null ? view.getId() : -1);
|
||||
if (actualTag != tag) {
|
||||
int tagActualIndex = -1;
|
||||
int parentChildrenCount = parentView.getChildCount();
|
||||
for (int i = 0; i < parentChildrenCount; i++) {
|
||||
if (parentView.getChildAt(i).getId() == tag) {
|
||||
tagActualIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO T74425739: previously, we did not do this check and `removeViewAt` would be executed
|
||||
// below, sometimes crashing there. *However*, interestingly enough, `removeViewAt` would not
|
||||
// complain if you removed views from an already-empty parent. This seems necessary currently
|
||||
// for certain ViewManagers that remove their own children - like BottomSheet?
|
||||
// This workaround seems not-great, but for now, we just return here for
|
||||
// backwards-compatibility. Essentially, if a view has already been removed from the
|
||||
// hierarchy, we treat it as a noop.
|
||||
if (tagActualIndex == -1) {
|
||||
FLog.e(
|
||||
TAG,
|
||||
"removeViewAt: ["
|
||||
+ tag
|
||||
+ "] -> ["
|
||||
+ parentTag
|
||||
+ "] @"
|
||||
+ index
|
||||
+ ": view already removed from parent! Children in parent: "
|
||||
+ parentChildrenCount);
|
||||
return;
|
||||
}
|
||||
|
||||
// Here we are guaranteed that the view is still in the View hierarchy, just
|
||||
// at a different index. In debug mode we'll crash here; in production, we'll remove
|
||||
// the child from the parent and move on.
|
||||
// This is an issue that is safely recoverable 95% of the time. If this allows corruption
|
||||
// of the view hierarchy and causes bugs or a crash after this point, there will be logs
|
||||
// indicating that this happened.
|
||||
// This is likely *only* necessary because of Fabric's LayoutAnimations implementation.
|
||||
// If we can fix the bug there, or remove the need for LayoutAnimation index adjustment
|
||||
// entirely, we can just throw this exception without regression user experience.
|
||||
logViewHierarchy(parentView, true);
|
||||
ReactSoftException.logSoftException(
|
||||
TAG,
|
||||
new IllegalStateException(
|
||||
"Tried to remove view ["
|
||||
+ tag
|
||||
+ "] of parent ["
|
||||
+ parentTag
|
||||
+ "] at index "
|
||||
+ index
|
||||
+ ", but got view tag "
|
||||
+ actualTag
|
||||
+ " - actual index of view: "
|
||||
+ tagActualIndex));
|
||||
index = tagActualIndex;
|
||||
}
|
||||
|
||||
try {
|
||||
viewGroupManager.removeViewAt(parentView, index);
|
||||
} catch (RuntimeException e) {
|
||||
// Note: `getChildCount` may not always be accurate!
|
||||
// We don't currently have a good explanation other than, in situations where you
|
||||
// would empirically expect to see childCount > 0, the childCount is reported as 0.
|
||||
// This is likely due to a ViewManager overriding getChildCount or some other methods
|
||||
// in a way that is strictly incorrect, but potentially only visible here.
|
||||
// The failure mode is actually that in `removeViewAt`, a NullPointerException is
|
||||
// thrown when we try to perform an operation on a View that doesn't exist, and
|
||||
// is therefore null.
|
||||
// We try to add some extra diagnostics here, but we always try to remove the View
|
||||
// from the hierarchy first because detecting by looking at childCount will not work.
|
||||
//
|
||||
// Note that the lesson here is that `getChildCount` is not /required/ to adhere to
|
||||
// any invariants. If you add 9 children to a parent, the `getChildCount` of the parent
|
||||
// may not be equal to 9. This apparently causes no issues with Android and is common
|
||||
// enough that we shouldn't try to change this invariant, without a lot of thought.
|
||||
int childCount = viewGroupManager.getChildCount(parentView);
|
||||
|
||||
logViewHierarchy(parentView, true);
|
||||
|
||||
throw new IllegalStateException(
|
||||
"Cannot remove child at index "
|
||||
+ index
|
||||
+ " from parent ViewGroup ["
|
||||
+ parentView.getId()
|
||||
+ "], only "
|
||||
+ childCount
|
||||
+ " children in parent. Warning: childCount may be incorrect!",
|
||||
e);
|
||||
}
|
||||
|
||||
// Display children after deleting any
|
||||
if (SHOW_CHANGED_VIEW_HIERARCHIES) {
|
||||
final int finalIndex = index;
|
||||
UiThreadUtil.runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
FLog.e(
|
||||
TAG,
|
||||
"removeViewAt: ["
|
||||
+ tag
|
||||
+ "] -> ["
|
||||
+ parentTag
|
||||
+ "] idx: "
|
||||
+ finalIndex
|
||||
+ " AFTER");
|
||||
logViewHierarchy(parentView, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void createView(
|
||||
@NonNull String componentName,
|
||||
int reactTag,
|
||||
@Nullable ReadableMap props,
|
||||
@Nullable StateWrapper stateWrapper,
|
||||
boolean isLayoutable) {
|
||||
if (isStopped()) {
|
||||
return;
|
||||
}
|
||||
if (getNullableViewState(reactTag) != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
View view = null;
|
||||
ViewManager viewManager = null;
|
||||
|
||||
ReactStylesDiffMap propsDiffMap = null;
|
||||
if (props != null) {
|
||||
propsDiffMap = new ReactStylesDiffMap(props);
|
||||
}
|
||||
|
||||
if (isLayoutable) {
|
||||
viewManager = mViewManagerRegistry.get(componentName);
|
||||
// View Managers are responsible for dealing with initial state and props.
|
||||
view =
|
||||
viewManager.createView(
|
||||
reactTag, mThemedReactContext, propsDiffMap, stateWrapper, mJSResponderHandler);
|
||||
view.setId(reactTag);
|
||||
}
|
||||
|
||||
ViewState viewState = new ViewState(reactTag, view, viewManager);
|
||||
viewState.mCurrentProps = propsDiffMap;
|
||||
viewState.mCurrentState = (stateWrapper != null ? stateWrapper.getState() : null);
|
||||
|
||||
mTagToViewState.put(reactTag, viewState);
|
||||
}
|
||||
|
||||
public void updateProps(int reactTag, ReadableMap props) {
|
||||
if (isStopped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ViewState viewState = getViewState(reactTag);
|
||||
viewState.mCurrentProps = new ReactStylesDiffMap(props);
|
||||
View view = viewState.mView;
|
||||
|
||||
if (view == null) {
|
||||
throw new IllegalStateException("Unable to find view for tag [" + reactTag + "]");
|
||||
}
|
||||
|
||||
Assertions.assertNotNull(viewState.mViewManager)
|
||||
.updateProperties(view, viewState.mCurrentProps);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void receiveCommand(int reactTag, int commandId, @Nullable ReadableArray commandArgs) {
|
||||
if (isStopped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ViewState viewState = getNullableViewState(reactTag);
|
||||
|
||||
// It's not uncommon for JS to send events as/after a component is being removed from the
|
||||
// view hierarchy. For example, TextInput may send a "blur" command in response to the view
|
||||
// disappearing. Throw `ReactNoCrashSoftException` so they're logged but don't crash in dev
|
||||
// for now.
|
||||
if (viewState == null) {
|
||||
throw new RetryableMountingLayerException(
|
||||
"Unable to find viewState for tag: [" + reactTag + "] for commandId: " + commandId);
|
||||
}
|
||||
|
||||
if (viewState.mViewManager == null) {
|
||||
throw new RetryableMountingLayerException("Unable to find viewManager for tag " + reactTag);
|
||||
}
|
||||
|
||||
if (viewState.mView == null) {
|
||||
throw new RetryableMountingLayerException(
|
||||
"Unable to find viewState view for tag " + reactTag);
|
||||
}
|
||||
|
||||
viewState.mViewManager.receiveCommand(viewState.mView, commandId, commandArgs);
|
||||
}
|
||||
|
||||
public void receiveCommand(
|
||||
int reactTag, @NonNull String commandId, @Nullable ReadableArray commandArgs) {
|
||||
if (isStopped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ViewState viewState = getNullableViewState(reactTag);
|
||||
|
||||
// It's not uncommon for JS to send events as/after a component is being removed from the
|
||||
// view hierarchy. For example, TextInput may send a "blur" command in response to the view
|
||||
// disappearing. Throw `ReactNoCrashSoftException` so they're logged but don't crash in dev
|
||||
// for now.
|
||||
if (viewState == null) {
|
||||
throw new RetryableMountingLayerException(
|
||||
"Unable to find viewState for tag: " + reactTag + " for commandId: " + commandId);
|
||||
}
|
||||
|
||||
if (viewState.mViewManager == null) {
|
||||
throw new RetryableMountingLayerException(
|
||||
"Unable to find viewState manager for tag " + reactTag);
|
||||
}
|
||||
|
||||
if (viewState.mView == null) {
|
||||
throw new RetryableMountingLayerException(
|
||||
"Unable to find viewState view for tag " + reactTag);
|
||||
}
|
||||
|
||||
viewState.mViewManager.receiveCommand(viewState.mView, commandId, commandArgs);
|
||||
}
|
||||
|
||||
public void sendAccessibilityEvent(int reactTag, int eventType) {
|
||||
if (isStopped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ViewState viewState = getViewState(reactTag);
|
||||
|
||||
if (viewState.mViewManager == null) {
|
||||
throw new RetryableMountingLayerException(
|
||||
"Unable to find viewState manager for tag " + reactTag);
|
||||
}
|
||||
|
||||
if (viewState.mView == null) {
|
||||
throw new RetryableMountingLayerException(
|
||||
"Unable to find viewState view for tag " + reactTag);
|
||||
}
|
||||
|
||||
viewState.mView.sendAccessibilityEvent(eventType);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void updateLayout(int reactTag, int x, int y, int width, int height, int displayType) {
|
||||
if (isStopped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ViewState viewState = getViewState(reactTag);
|
||||
// Do not layout Root Views
|
||||
if (viewState.mIsRoot) {
|
||||
return;
|
||||
}
|
||||
|
||||
View viewToUpdate = viewState.mView;
|
||||
if (viewToUpdate == null) {
|
||||
throw new IllegalStateException("Unable to find View for tag: " + reactTag);
|
||||
}
|
||||
|
||||
viewToUpdate.measure(
|
||||
View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
|
||||
View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY));
|
||||
|
||||
ViewParent parent = viewToUpdate.getParent();
|
||||
if (parent instanceof RootView) {
|
||||
parent.requestLayout();
|
||||
}
|
||||
|
||||
// TODO: T31905686 Check if the parent of the view has to layout the view, or the child has
|
||||
// to lay itself out. see NativeViewHierarchyManager.updateLayout
|
||||
viewToUpdate.layout(x, y, x + width, y + height);
|
||||
|
||||
// displayType: 0 represents display: 'none'
|
||||
int visibility = displayType == 0 ? View.INVISIBLE : View.VISIBLE;
|
||||
if (viewToUpdate.getVisibility() != visibility) {
|
||||
viewToUpdate.setVisibility(visibility);
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void updatePadding(int reactTag, int left, int top, int right, int bottom) {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
if (isStopped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ViewState viewState = getViewState(reactTag);
|
||||
// Do not layout Root Views
|
||||
if (viewState.mIsRoot) {
|
||||
return;
|
||||
}
|
||||
|
||||
View viewToUpdate = viewState.mView;
|
||||
if (viewToUpdate == null) {
|
||||
throw new IllegalStateException("Unable to find View for tag: " + reactTag);
|
||||
}
|
||||
|
||||
ViewManager viewManager = viewState.mViewManager;
|
||||
if (viewManager == null) {
|
||||
throw new IllegalStateException("Unable to find ViewManager for view: " + viewState);
|
||||
}
|
||||
|
||||
//noinspection unchecked
|
||||
viewManager.setPadding(viewToUpdate, left, top, right, bottom);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void updateState(final int reactTag, @Nullable StateWrapper stateWrapper) {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
if (isStopped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ViewState viewState = getViewState(reactTag);
|
||||
@Nullable ReadableNativeMap newState = stateWrapper == null ? null : stateWrapper.getState();
|
||||
|
||||
viewState.mCurrentState = newState;
|
||||
|
||||
ViewManager viewManager = viewState.mViewManager;
|
||||
|
||||
if (viewManager == null) {
|
||||
throw new IllegalStateException("Unable to find ViewManager for tag: " + reactTag);
|
||||
}
|
||||
Object extraData =
|
||||
viewManager.updateState(viewState.mView, viewState.mCurrentProps, stateWrapper);
|
||||
if (extraData != null) {
|
||||
viewManager.updateExtraData(viewState.mView, extraData);
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void updateEventEmitter(int reactTag, @NonNull EventEmitterWrapper eventEmitter) {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
if (isStopped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ViewState viewState = mTagToViewState.get(reactTag);
|
||||
if (viewState == null) {
|
||||
// TODO T62717437 - Use a flag to determine that these event emitters belong to virtual nodes
|
||||
// only.
|
||||
viewState = new ViewState(reactTag, null, null);
|
||||
mTagToViewState.put(reactTag, viewState);
|
||||
}
|
||||
viewState.mEventEmitter = eventEmitter;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public synchronized void setJSResponder(
|
||||
int reactTag, int initialReactTag, boolean blockNativeResponder) {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
if (isStopped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!blockNativeResponder) {
|
||||
mJSResponderHandler.setJSResponder(initialReactTag, null);
|
||||
return;
|
||||
}
|
||||
|
||||
ViewState viewState = getViewState(reactTag);
|
||||
View view = viewState.mView;
|
||||
if (initialReactTag != reactTag && view instanceof ViewParent) {
|
||||
// In this case, initialReactTag corresponds to a virtual/layout-only View, and we already
|
||||
// have a parent of that View in reactTag, so we can use it.
|
||||
mJSResponderHandler.setJSResponder(initialReactTag, (ViewParent) view);
|
||||
return;
|
||||
} else if (view == null) {
|
||||
SoftAssertions.assertUnreachable("Cannot find view for tag [" + reactTag + "].");
|
||||
return;
|
||||
}
|
||||
|
||||
if (viewState.mIsRoot) {
|
||||
SoftAssertions.assertUnreachable(
|
||||
"Cannot block native responder on [" + reactTag + "] that is a root view");
|
||||
}
|
||||
mJSResponderHandler.setJSResponder(initialReactTag, view.getParent());
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void deleteView(int reactTag) {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
if (isStopped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ViewState viewState = getNullableViewState(reactTag);
|
||||
|
||||
if (viewState == null) {
|
||||
ReactSoftException.logSoftException(
|
||||
MountingManager.TAG,
|
||||
new IllegalStateException(
|
||||
"Unable to find viewState for tag: " + reactTag + " for deleteView"));
|
||||
return;
|
||||
}
|
||||
|
||||
// To delete we simply remove the tag from the registry.
|
||||
// We want to rely on the correct set of MountInstructions being sent to the platform,
|
||||
// or StopSurface being called, so we do not handle deleting descendents of the View.
|
||||
mTagToViewState.remove(reactTag);
|
||||
|
||||
// For non-root views we notify viewmanager with {@link ViewManager#onDropInstance}
|
||||
ViewManager viewManager = viewState.mViewManager;
|
||||
if (!viewState.mIsRoot && viewManager != null) {
|
||||
viewManager.onDropViewInstance(viewState.mView);
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void preallocateView(
|
||||
String componentName,
|
||||
int reactTag,
|
||||
@Nullable ReadableMap props,
|
||||
@Nullable StateWrapper stateWrapper,
|
||||
boolean isLayoutable) {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
if (isStopped()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (getNullableViewState(reactTag) != null) {
|
||||
throw new IllegalStateException(
|
||||
"View for component " + componentName + " with tag " + reactTag + " already exists.");
|
||||
}
|
||||
|
||||
createView(componentName, reactTag, props, stateWrapper, isLayoutable);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
@ThreadConfined(ANY)
|
||||
public @Nullable EventEmitterWrapper getEventEmitter(int reactTag) {
|
||||
ViewState viewState = getNullableViewState(reactTag);
|
||||
return viewState == null ? null : viewState.mEventEmitter;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private ViewState getViewState(int tag) {
|
||||
ViewState viewState = mTagToViewState.get(tag);
|
||||
if (viewState == null) {
|
||||
throw new RetryableMountingLayerException("Unable to find viewState view for tag " + tag);
|
||||
}
|
||||
return viewState;
|
||||
}
|
||||
|
||||
private @Nullable ViewState getNullableViewState(int tag) {
|
||||
return mTagToViewState.get(tag);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked") // prevents unchecked conversion warn of the <ViewGroup> type
|
||||
private static @NonNull ViewGroupManager<ViewGroup> getViewGroupManager(
|
||||
@NonNull ViewState viewState) {
|
||||
if (viewState.mViewManager == null) {
|
||||
throw new IllegalStateException("Unable to find ViewManager for view: " + viewState);
|
||||
}
|
||||
return (ViewGroupManager<ViewGroup>) viewState.mViewManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class holds view state for react tags. Objects of this class are stored into the {@link
|
||||
* #mTagToViewState}, and they should be updated in the same thread.
|
||||
*/
|
||||
private static class ViewState {
|
||||
@Nullable final View mView;
|
||||
final int mReactTag;
|
||||
final boolean mIsRoot;
|
||||
@Nullable final ViewManager mViewManager;
|
||||
@Nullable public ReactStylesDiffMap mCurrentProps = null;
|
||||
@Nullable public ReadableMap mCurrentLocalData = null;
|
||||
@Nullable public ReadableMap mCurrentState = null;
|
||||
@Nullable public EventEmitterWrapper mEventEmitter = null;
|
||||
|
||||
private ViewState(int reactTag, @Nullable View view, @Nullable ViewManager viewManager) {
|
||||
this(reactTag, view, viewManager, false);
|
||||
}
|
||||
|
||||
private ViewState(int reactTag, @Nullable View view, ViewManager viewManager, boolean isRoot) {
|
||||
mReactTag = reactTag;
|
||||
mView = view;
|
||||
mIsRoot = isRoot;
|
||||
mViewManager = viewManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
boolean isLayoutOnly = mViewManager == null;
|
||||
return "ViewState ["
|
||||
+ mReactTag
|
||||
+ "] - isRoot: "
|
||||
+ mIsRoot
|
||||
+ " - props: "
|
||||
+ mCurrentProps
|
||||
+ " - localData: "
|
||||
+ mCurrentLocalData
|
||||
+ " - viewManager: "
|
||||
+ mViewManager
|
||||
+ " - isLayoutOnly: "
|
||||
+ isLayoutOnly;
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -14,12 +14,14 @@ import com.facebook.react.fabric.mounting.MountingManager;
|
||||
|
||||
public class DispatchIntCommandMountItem extends DispatchCommandMountItem {
|
||||
|
||||
private final int mSurfaceId;
|
||||
private final int mReactTag;
|
||||
private final int mCommandId;
|
||||
private final @Nullable ReadableArray mCommandArgs;
|
||||
|
||||
public DispatchIntCommandMountItem(
|
||||
int reactTag, int commandId, @Nullable ReadableArray commandArgs) {
|
||||
int surfaceId, int reactTag, int commandId, @Nullable ReadableArray commandArgs) {
|
||||
mSurfaceId = surfaceId;
|
||||
mReactTag = reactTag;
|
||||
mCommandId = commandId;
|
||||
mCommandArgs = commandArgs;
|
||||
@@ -27,7 +29,7 @@ public class DispatchIntCommandMountItem extends DispatchCommandMountItem {
|
||||
|
||||
@Override
|
||||
public void execute(@NonNull MountingManager mountingManager) {
|
||||
mountingManager.receiveCommand(mReactTag, mCommandId, mCommandArgs);
|
||||
mountingManager.receiveCommand(mSurfaceId, mReactTag, mCommandId, mCommandArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-2
@@ -14,12 +14,14 @@ import com.facebook.react.fabric.mounting.MountingManager;
|
||||
|
||||
public class DispatchStringCommandMountItem extends DispatchCommandMountItem {
|
||||
|
||||
private final int mSurfaceId;
|
||||
private final int mReactTag;
|
||||
@NonNull private final String mCommandId;
|
||||
private final @Nullable ReadableArray mCommandArgs;
|
||||
|
||||
public DispatchStringCommandMountItem(
|
||||
int reactTag, @NonNull String commandId, @Nullable ReadableArray commandArgs) {
|
||||
int surfaceId, int reactTag, @NonNull String commandId, @Nullable ReadableArray commandArgs) {
|
||||
mSurfaceId = surfaceId;
|
||||
mReactTag = reactTag;
|
||||
mCommandId = commandId;
|
||||
mCommandArgs = commandArgs;
|
||||
@@ -27,7 +29,7 @@ public class DispatchStringCommandMountItem extends DispatchCommandMountItem {
|
||||
|
||||
@Override
|
||||
public void execute(@NonNull MountingManager mountingManager) {
|
||||
mountingManager.receiveCommand(mReactTag, mCommandId, mCommandArgs);
|
||||
mountingManager.receiveCommand(mSurfaceId, mReactTag, mCommandId, mCommandArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+27
-28
@@ -8,10 +8,10 @@
|
||||
package com.facebook.react.fabric.mounting.mountitems;
|
||||
|
||||
import static com.facebook.react.fabric.FabricComponents.getFabricComponentName;
|
||||
import static com.facebook.react.fabric.FabricUIManager.ENABLE_FABRIC_LOGS;
|
||||
import static com.facebook.react.fabric.FabricUIManager.IS_DEVELOPMENT_ENVIRONMENT;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
import com.facebook.react.bridge.ReactMarker;
|
||||
@@ -19,8 +19,8 @@ import com.facebook.react.bridge.ReactMarkerConstants;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.fabric.events.EventEmitterWrapper;
|
||||
import com.facebook.react.fabric.mounting.MountingManager;
|
||||
import com.facebook.react.fabric.mounting.SurfaceMountingManager;
|
||||
import com.facebook.react.uimanager.StateWrapper;
|
||||
import com.facebook.react.uimanager.ThemedReactContext;
|
||||
import com.facebook.systrace.Systrace;
|
||||
|
||||
/**
|
||||
@@ -50,26 +50,18 @@ public class IntBufferBatchMountItem implements MountItem {
|
||||
static final int INSTRUCTION_UPDATE_EVENT_EMITTER = 256;
|
||||
static final int INSTRUCTION_UPDATE_PADDING = 512;
|
||||
|
||||
private final int mRootTag;
|
||||
private final int mSurfaceId;
|
||||
private final int mCommitNumber;
|
||||
|
||||
@NonNull private final ThemedReactContext mContext;
|
||||
|
||||
@NonNull private final int[] mIntBuffer;
|
||||
@NonNull private final Object[] mObjBuffer;
|
||||
|
||||
private final int mIntBufferLen;
|
||||
private final int mObjBufferLen;
|
||||
|
||||
public IntBufferBatchMountItem(
|
||||
int rootTag,
|
||||
@Nullable ThemedReactContext context,
|
||||
int[] intBuf,
|
||||
Object[] objBuf,
|
||||
int commitNumber) {
|
||||
mRootTag = rootTag;
|
||||
public IntBufferBatchMountItem(int surfaceId, int[] intBuf, Object[] objBuf, int commitNumber) {
|
||||
mSurfaceId = surfaceId;
|
||||
mCommitNumber = commitNumber;
|
||||
mContext = context;
|
||||
|
||||
mIntBuffer = intBuf;
|
||||
mObjBuffer = objBuf;
|
||||
@@ -119,13 +111,21 @@ public class IntBufferBatchMountItem implements MountItem {
|
||||
|
||||
@Override
|
||||
public void execute(@NonNull MountingManager mountingManager) {
|
||||
if (mContext == null) {
|
||||
SurfaceMountingManager surfaceMountingManager = mountingManager.getSurfaceManager(mSurfaceId);
|
||||
if (surfaceMountingManager == null) {
|
||||
FLog.e(
|
||||
TAG,
|
||||
"Cannot execute batch of %s MountItems; no context. Hopefully this is because StopSurface was called.",
|
||||
TAG);
|
||||
"Skipping batch of MountItems; no SurfaceMountingManager found for [%d].",
|
||||
mSurfaceId);
|
||||
return;
|
||||
}
|
||||
if (surfaceMountingManager.isStopped()) {
|
||||
FLog.e(TAG, "Skipping batch of MountItems; was stopped [%d].", mSurfaceId);
|
||||
return;
|
||||
}
|
||||
if (ENABLE_FABRIC_LOGS) {
|
||||
FLog.d(TAG, "Executing IntBufferBatchMountItem on surface [%d]", mSurfaceId);
|
||||
}
|
||||
|
||||
beginMarkers("mountViews");
|
||||
|
||||
@@ -137,26 +137,24 @@ public class IntBufferBatchMountItem implements MountItem {
|
||||
for (int k = 0; k < numInstructions; k++) {
|
||||
if (type == INSTRUCTION_CREATE) {
|
||||
String componentName = getFabricComponentName((String) mObjBuffer[j++]);
|
||||
mountingManager.createView(
|
||||
mContext,
|
||||
surfaceMountingManager.createView(
|
||||
componentName,
|
||||
mRootTag,
|
||||
mIntBuffer[i++],
|
||||
castToProps(mObjBuffer[j++]),
|
||||
castToState(mObjBuffer[j++]),
|
||||
mIntBuffer[i++] == 1);
|
||||
} else if (type == INSTRUCTION_DELETE) {
|
||||
mountingManager.deleteView(mRootTag, mIntBuffer[i++]);
|
||||
surfaceMountingManager.deleteView(mIntBuffer[i++]);
|
||||
} else if (type == INSTRUCTION_INSERT) {
|
||||
int tag = mIntBuffer[i++];
|
||||
int parentTag = mIntBuffer[i++];
|
||||
mountingManager.addViewAt(parentTag, tag, mIntBuffer[i++]);
|
||||
surfaceMountingManager.addViewAt(parentTag, tag, mIntBuffer[i++]);
|
||||
} else if (type == INSTRUCTION_REMOVE) {
|
||||
mountingManager.removeViewAt(mIntBuffer[i++], mIntBuffer[i++], mIntBuffer[i++]);
|
||||
surfaceMountingManager.removeViewAt(mIntBuffer[i++], mIntBuffer[i++], mIntBuffer[i++]);
|
||||
} else if (type == INSTRUCTION_UPDATE_PROPS) {
|
||||
mountingManager.updateProps(mIntBuffer[i++], castToProps(mObjBuffer[j++]));
|
||||
surfaceMountingManager.updateProps(mIntBuffer[i++], castToProps(mObjBuffer[j++]));
|
||||
} else if (type == INSTRUCTION_UPDATE_STATE) {
|
||||
mountingManager.updateState(mIntBuffer[i++], castToState(mObjBuffer[j++]));
|
||||
surfaceMountingManager.updateState(mIntBuffer[i++], castToState(mObjBuffer[j++]));
|
||||
} else if (type == INSTRUCTION_UPDATE_LAYOUT) {
|
||||
int reactTag = mIntBuffer[i++];
|
||||
int x = mIntBuffer[i++];
|
||||
@@ -166,13 +164,14 @@ public class IntBufferBatchMountItem implements MountItem {
|
||||
// The final buffer, layoutDirection, seems unused?
|
||||
i++;
|
||||
int displayType = mIntBuffer[i++];
|
||||
mountingManager.updateLayout(reactTag, x, y, width, height, displayType);
|
||||
surfaceMountingManager.updateLayout(reactTag, x, y, width, height, displayType);
|
||||
|
||||
} else if (type == INSTRUCTION_UPDATE_PADDING) {
|
||||
mountingManager.updatePadding(
|
||||
surfaceMountingManager.updatePadding(
|
||||
mIntBuffer[i++], mIntBuffer[i++], mIntBuffer[i++], mIntBuffer[i++], mIntBuffer[i++]);
|
||||
} else if (type == INSTRUCTION_UPDATE_EVENT_EMITTER) {
|
||||
mountingManager.updateEventEmitter(mIntBuffer[i++], castToEventEmitter(mObjBuffer[j++]));
|
||||
surfaceMountingManager.updateEventEmitter(
|
||||
mIntBuffer[i++], castToEventEmitter(mObjBuffer[j++]));
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid type argument to IntBufferBatchMountItem: " + type + " at index: " + i);
|
||||
@@ -184,7 +183,7 @@ public class IntBufferBatchMountItem implements MountItem {
|
||||
}
|
||||
|
||||
public int getRootTag() {
|
||||
return mRootTag;
|
||||
return mSurfaceId;
|
||||
}
|
||||
|
||||
public boolean shouldSchedule() {
|
||||
|
||||
+9
-19
@@ -17,30 +17,26 @@ import com.facebook.common.logging.FLog;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.fabric.mounting.MountingManager;
|
||||
import com.facebook.react.uimanager.StateWrapper;
|
||||
import com.facebook.react.uimanager.ThemedReactContext;
|
||||
|
||||
/** {@link MountItem} that is used to pre-allocate views for JS components. */
|
||||
public class PreAllocateViewMountItem implements MountItem {
|
||||
|
||||
@NonNull private final String mComponent;
|
||||
private final int mRootTag;
|
||||
private final int mSurfaceId;
|
||||
private final int mReactTag;
|
||||
private final @Nullable ReadableMap mProps;
|
||||
private final @Nullable StateWrapper mStateWrapper;
|
||||
private final @NonNull ThemedReactContext mContext;
|
||||
private final boolean mIsLayoutable;
|
||||
|
||||
public PreAllocateViewMountItem(
|
||||
@Nullable ThemedReactContext context,
|
||||
int rootTag,
|
||||
int surfaceId,
|
||||
int reactTag,
|
||||
@NonNull String component,
|
||||
@Nullable ReadableMap props,
|
||||
@NonNull StateWrapper stateWrapper,
|
||||
boolean isLayoutable) {
|
||||
mContext = context;
|
||||
mComponent = component;
|
||||
mRootTag = rootTag;
|
||||
mSurfaceId = surfaceId;
|
||||
mProps = props;
|
||||
mStateWrapper = stateWrapper;
|
||||
mReactTag = reactTag;
|
||||
@@ -48,7 +44,7 @@ public class PreAllocateViewMountItem implements MountItem {
|
||||
}
|
||||
|
||||
public int getRootTag() {
|
||||
return mRootTag;
|
||||
return mSurfaceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -56,15 +52,9 @@ public class PreAllocateViewMountItem implements MountItem {
|
||||
if (ENABLE_FABRIC_LOGS) {
|
||||
FLog.d(TAG, "Executing pre-allocation of: " + toString());
|
||||
}
|
||||
if (mContext == null) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot execute PreAllocateViewMountItem without Context for ReactTag: "
|
||||
+ mReactTag
|
||||
+ " and rootTag: "
|
||||
+ mRootTag);
|
||||
}
|
||||
mountingManager.preallocateView(
|
||||
mContext, mComponent, mRootTag, mReactTag, mProps, mStateWrapper, mIsLayoutable);
|
||||
mountingManager
|
||||
.getSurfaceManagerEnforced(mSurfaceId, "PreAllocateViewMountItem")
|
||||
.preallocateView(mComponent, mReactTag, mProps, mStateWrapper, mIsLayoutable);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -74,8 +64,8 @@ public class PreAllocateViewMountItem implements MountItem {
|
||||
.append(mReactTag)
|
||||
.append("] - component: ")
|
||||
.append(mComponent)
|
||||
.append(" rootTag: ")
|
||||
.append(mRootTag)
|
||||
.append(" surfaceId: ")
|
||||
.append(mSurfaceId)
|
||||
.append(" isLayoutable: ")
|
||||
.append(mIsLayoutable);
|
||||
|
||||
|
||||
+4
-2
@@ -16,10 +16,12 @@ public class SendAccessibilityEvent implements MountItem {
|
||||
|
||||
private final String TAG = "Fabric.SendAccessibilityEvent";
|
||||
|
||||
private final int mSurfaceId;
|
||||
private final int mReactTag;
|
||||
private final int mEventType;
|
||||
|
||||
public SendAccessibilityEvent(int reactTag, int eventType) {
|
||||
public SendAccessibilityEvent(int surfaceId, int reactTag, int eventType) {
|
||||
mSurfaceId = surfaceId;
|
||||
mReactTag = reactTag;
|
||||
mEventType = eventType;
|
||||
}
|
||||
@@ -27,7 +29,7 @@ public class SendAccessibilityEvent implements MountItem {
|
||||
@Override
|
||||
public void execute(@NonNull MountingManager mountingManager) {
|
||||
try {
|
||||
mountingManager.sendAccessibilityEvent(mReactTag, mEventType);
|
||||
mountingManager.sendAccessibilityEvent(mSurfaceId, mReactTag, mEventType);
|
||||
} catch (RetryableMountingLayerException e) {
|
||||
// Accessibility events are similar to commands in that they're imperative
|
||||
// calls from JS, disconnected from the commit lifecycle, and therefore
|
||||
|
||||
@@ -724,8 +724,7 @@ public class UIManagerModule extends ReactContextBaseJavaModule
|
||||
@ReactMethod
|
||||
public void dispatchViewManagerCommand(
|
||||
int reactTag, Dynamic commandId, @Nullable ReadableArray commandArgs) {
|
||||
// TODO: this is a temporary approach to support ViewManagerCommands in Fabric until
|
||||
// the dispatchViewManagerCommand() method is supported by Fabric JS API.
|
||||
// Fabric dispatchCommands should go through the JSI API - this will crash in Fabric.
|
||||
@Nullable
|
||||
UIManager uiManager =
|
||||
UIManagerHelper.getUIManager(
|
||||
|
||||
Reference in New Issue
Block a user