Skip to content

Commit 4f8c87c

Browse files
cortinicoLuna Wei
authored and
Luna Wei
committed
[LOCAL] Fabric Interop - Properly dispatch integer commands (#38527) (#38835)
resolved: #38527
1 parent 7aa8cd5 commit 4f8c87c

File tree

4 files changed

+126
-15
lines changed

4 files changed

+126
-15
lines changed

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,14 @@
5353
import com.facebook.react.common.build.ReactBuildConfig;
5454
import com.facebook.react.common.mapbuffer.ReadableMapBuffer;
5555
import com.facebook.react.config.ReactFeatureFlags;
56-
import com.facebook.react.fabric.events.EventBeatManager;
5756
import com.facebook.react.fabric.events.EventEmitterWrapper;
5857
import com.facebook.react.fabric.events.FabricEventEmitter;
5958
import com.facebook.react.fabric.interop.InteropEventEmitter;
6059
import com.facebook.react.fabric.mounting.MountItemDispatcher;
6160
import com.facebook.react.fabric.mounting.MountingManager;
6261
import com.facebook.react.fabric.mounting.SurfaceMountingManager;
6362
import com.facebook.react.fabric.mounting.SurfaceMountingManager.ViewEvent;
63+
import com.facebook.react.fabric.mounting.mountitems.DispatchCommandMountItem;
6464
import com.facebook.react.fabric.mounting.mountitems.DispatchIntCommandMountItem;
6565
import com.facebook.react.fabric.mounting.mountitems.DispatchStringCommandMountItem;
6666
import com.facebook.react.fabric.mounting.mountitems.IntBufferBatchMountItem;
@@ -79,6 +79,7 @@
7979
import com.facebook.react.uimanager.UIManagerHelper;
8080
import com.facebook.react.uimanager.ViewManagerPropertyUpdater;
8181
import com.facebook.react.uimanager.ViewManagerRegistry;
82+
import com.facebook.react.uimanager.events.BatchEventDispatchedListener;
8283
import com.facebook.react.uimanager.events.EventCategoryDef;
8384
import com.facebook.react.uimanager.events.EventDispatcher;
8485
import com.facebook.react.uimanager.events.EventDispatcherImpl;
@@ -168,7 +169,7 @@ public void onFabricCommitEnd(DevToolsReactPerfLogger.FabricCommitPoint commitPo
168169
@NonNull private final MountItemDispatcher mMountItemDispatcher;
169170
@NonNull private final ViewManagerRegistry mViewManagerRegistry;
170171

171-
@NonNull private final EventBeatManager mEventBeatManager;
172+
@NonNull private final BatchEventDispatchedListener mBatchEventDispatchedListener;
172173

173174
@NonNull
174175
private final CopyOnWriteArrayList<UIManagerListener> mListeners = new CopyOnWriteArrayList<>();
@@ -208,16 +209,16 @@ public void executeItems(Queue<MountItem> items) {
208209
};
209210

210211
public FabricUIManager(
211-
ReactApplicationContext reactContext,
212-
ViewManagerRegistry viewManagerRegistry,
213-
EventBeatManager eventBeatManager) {
212+
@NonNull ReactApplicationContext reactContext,
213+
@NonNull ViewManagerRegistry viewManagerRegistry,
214+
@NonNull BatchEventDispatchedListener batchEventDispatchedListener) {
214215
mDispatchUIFrameCallback = new DispatchUIFrameCallback(reactContext);
215216
mReactApplicationContext = reactContext;
216217
mMountingManager = new MountingManager(viewManagerRegistry, mMountItemExecutor);
217218
mMountItemDispatcher =
218219
new MountItemDispatcher(mMountingManager, new MountItemDispatchListener());
219220
mEventDispatcher = new EventDispatcherImpl(reactContext);
220-
mEventBeatManager = eventBeatManager;
221+
mBatchEventDispatchedListener = batchEventDispatchedListener;
221222
mReactApplicationContext.addLifecycleEventListener(this);
222223

223224
mViewManagerRegistry = viewManagerRegistry;
@@ -385,7 +386,7 @@ public void stopSurface(final int surfaceID) {
385386
@Override
386387
public void initialize() {
387388
mEventDispatcher.registerEventEmitter(FABRIC, new FabricEventEmitter(this));
388-
mEventDispatcher.addBatchEventDispatchedListener(mEventBeatManager);
389+
mEventDispatcher.addBatchEventDispatchedListener(mBatchEventDispatchedListener);
389390
if (ENABLE_FABRIC_PERF_LOGS) {
390391
mDevToolsReactPerfLogger = new DevToolsReactPerfLogger();
391392
mDevToolsReactPerfLogger.addDevToolsReactPerfLoggerListener(FABRIC_PERF_LOGGER);
@@ -424,7 +425,7 @@ public void onCatalystInstanceDestroy() {
424425
// memory immediately.
425426
mDispatchUIFrameCallback.stop();
426427

427-
mEventDispatcher.removeBatchEventDispatchedListener(mEventBeatManager);
428+
mEventDispatcher.removeBatchEventDispatchedListener(mBatchEventDispatchedListener);
428429
mEventDispatcher.unregisterEventEmitter(FABRIC);
429430

430431
mReactApplicationContext.unregisterComponentCallbacks(mViewManagerRegistry);
@@ -1039,8 +1040,16 @@ public void dispatchCommand(
10391040
final int reactTag,
10401041
final String commandId,
10411042
@Nullable final ReadableArray commandArgs) {
1042-
mMountItemDispatcher.dispatchCommandMountItem(
1043-
new DispatchStringCommandMountItem(surfaceId, reactTag, commandId, commandArgs));
1043+
if (ReactFeatureFlags.unstable_useFabricInterop) {
1044+
// For Fabric Interop, we check if the commandId is an integer. If it is, we use the integer
1045+
// overload of dispatchCommand. Otherwise, we use the string overload.
1046+
// and the events won't be correctly dispatched.
1047+
mMountItemDispatcher.dispatchCommandMountItem(
1048+
createDispatchCommandMountItemForInterop(surfaceId, reactTag, commandId, commandArgs));
1049+
} else {
1050+
mMountItemDispatcher.dispatchCommandMountItem(
1051+
new DispatchStringCommandMountItem(surfaceId, reactTag, commandId, commandArgs));
1052+
}
10441053
}
10451054

10461055
@Override
@@ -1200,6 +1209,24 @@ public void didDispatchMountItems() {
12001209
}
12011210
}
12021211

1212+
/**
1213+
* Util function that takes care of handling commands for Fabric Interop. If the command is a
1214+
* string that represents a number (say "42"), it will be parsed as an integer and the
1215+
* corresponding dispatch command mount item will be created.
1216+
*/
1217+
/* package */ DispatchCommandMountItem createDispatchCommandMountItemForInterop(
1218+
final int surfaceId,
1219+
final int reactTag,
1220+
final String commandId,
1221+
@Nullable final ReadableArray commandArgs) {
1222+
try {
1223+
int commandIdInteger = Integer.parseInt(commandId);
1224+
return new DispatchIntCommandMountItem(surfaceId, reactTag, commandIdInteger, commandArgs);
1225+
} catch (NumberFormatException e) {
1226+
return new DispatchStringCommandMountItem(surfaceId, reactTag, commandId, commandArgs);
1227+
}
1228+
}
1229+
12031230
private class DispatchUIFrameCallback extends GuardedFrameCallback {
12041231

12051232
private volatile boolean mIsMountingEnabled = true;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
package com.facebook.react.fabric
9+
10+
import com.facebook.react.bridge.ReactApplicationContext
11+
import com.facebook.react.uimanager.ViewManagerRegistry
12+
import com.facebook.react.uimanager.events.BatchEventDispatchedListener
13+
import com.facebook.testutils.fakes.FakeBatchEventDispatchedListener
14+
import com.facebook.testutils.shadows.ShadowSoLoader
15+
import org.junit.Assert.assertEquals
16+
import org.junit.Before
17+
import org.junit.Test
18+
import org.junit.runner.RunWith
19+
import org.robolectric.RobolectricTestRunner
20+
import org.robolectric.RuntimeEnvironment
21+
import org.robolectric.annotation.Config
22+
23+
@RunWith(RobolectricTestRunner::class)
24+
@Config(shadows = [ShadowSoLoader::class])
25+
class FabricUIManagerTest {
26+
27+
private lateinit var reactContext: ReactApplicationContext
28+
private lateinit var viewManagerRegistry: ViewManagerRegistry
29+
private lateinit var batchEventDispatchedListener: BatchEventDispatchedListener
30+
private lateinit var underTest: FabricUIManager
31+
32+
@Before
33+
fun setup() {
34+
reactContext = ReactApplicationContext(RuntimeEnvironment.getApplication())
35+
viewManagerRegistry = ViewManagerRegistry(emptyList())
36+
batchEventDispatchedListener = FakeBatchEventDispatchedListener()
37+
underTest = FabricUIManager(reactContext, viewManagerRegistry, batchEventDispatchedListener)
38+
}
39+
40+
@Test
41+
fun createDispatchCommandMountItemForInterop_withValidString_returnsStringEvent() {
42+
val command = underTest.createDispatchCommandMountItemForInterop(11, 1, "anEvent", null)
43+
44+
// DispatchStringCommandMountItem is package private so we can `as` check it.
45+
val className = command::class.java.name.substringAfterLast(".")
46+
assertEquals("DispatchStringCommandMountItem", className)
47+
}
48+
49+
@Test
50+
fun createDispatchCommandMountItemForInterop_withValidInt_returnsIntEvent() {
51+
val command = underTest.createDispatchCommandMountItemForInterop(11, 1, "42", null)
52+
53+
// DispatchIntCommandMountItem is package private so we can `as` check it.
54+
val className = command::class.java.name.substringAfterLast(".")
55+
assertEquals("DispatchIntCommandMountItem", className)
56+
}
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
package com.facebook.testutils.fakes
9+
10+
import com.facebook.react.uimanager.events.BatchEventDispatchedListener
11+
12+
/** A fake [BatchEventDispatchedListener] for testing that does nothing. */
13+
class FakeBatchEventDispatchedListener : BatchEventDispatchedListener {
14+
override fun onBatchEventDispatched() {
15+
// do nothing
16+
}
17+
}

packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/component/MyLegacyViewManager.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
public class MyLegacyViewManager extends SimpleViewManager<MyNativeView> {
2828

2929
public static final String REACT_CLASS = "RNTMyLegacyNativeView";
30-
private static final Integer COMMAND_CHANGE_BACKGROUND_COLOR = 42;
30+
private static final int COMMAND_CHANGE_BACKGROUND_COLOR = 42;
3131
private final ReactApplicationContext mCallerContext;
3232

3333
public MyLegacyViewManager(ReactApplicationContext reactContext) {
@@ -71,8 +71,7 @@ public final Map<String, Object> getExportedViewConstants() {
7171

7272
@Override
7373
public final Map<String, Object> getExportedCustomBubblingEventTypeConstants() {
74-
Map<String, Object> eventTypeConstants = new HashMap<String, Object>();
75-
eventTypeConstants.putAll(
74+
return new HashMap<>(
7675
MapBuilder.<String, Object>builder()
7776
.put(
7877
"onColorChanged",
@@ -81,18 +80,29 @@ public final Map<String, Object> getExportedCustomBubblingEventTypeConstants() {
8180
MapBuilder.of(
8281
"bubbled", "onColorChanged", "captured", "onColorChangedCapture")))
8382
.build());
84-
return eventTypeConstants;
8583
}
8684

8785
@Override
8886
public void receiveCommand(
8987
@NonNull MyNativeView view, String commandId, @Nullable ReadableArray args) {
90-
if (commandId.contentEquals(COMMAND_CHANGE_BACKGROUND_COLOR.toString())) {
88+
if (commandId.contentEquals("changeBackgroundColor")) {
9189
int sentColor = Color.parseColor(args.getString(0));
9290
view.setBackgroundColor(sentColor);
9391
}
9492
}
9593

94+
@SuppressWarnings("deprecation") // We intentionally want to test against the legacy API here.
95+
@Override
96+
public void receiveCommand(
97+
@NonNull MyNativeView view, int commandId, @Nullable ReadableArray args) {
98+
switch (commandId) {
99+
case COMMAND_CHANGE_BACKGROUND_COLOR:
100+
int sentColor = Color.parseColor(args.getString(0));
101+
view.setBackgroundColor(sentColor);
102+
break;
103+
}
104+
}
105+
96106
@Nullable
97107
@Override
98108
public Map<String, Integer> getCommandsMap() {

0 commit comments

Comments
 (0)