Skip to content

Commit c5a5d77

Browse files
committed
Respect Fabric event coalescing keys on Android
1 parent eedfdef commit c5a5d77

18 files changed

Lines changed: 388 additions & 27 deletions

File tree

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

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,8 +1108,6 @@ public void receiveEvent(
11081108
* that C++ may coalesce the event optionally. Otherwise, coalescing can happen in Java before
11091109
* emitting.
11101110
*
1111-
* <p>{@code customCoalesceKey} is currently unused.
1112-
*
11131111
* @param surfaceId
11141112
* @param reactTag
11151113
* @param eventName
@@ -1142,8 +1140,6 @@ public void receiveEvent(
11421140
* that C++ may coalesce the event optionally. Otherwise, coalescing can happen in Java before
11431141
* emitting.
11441142
*
1145-
* <p>{@code customCoalesceKey} is currently unused.
1146-
*
11471143
* @param surfaceId
11481144
* @param reactTag
11491145
* @param eventName
@@ -1179,8 +1175,6 @@ public void receiveEvent(
11791175
* that C++ may coalesce the event optionally. Otherwise, coalescing can happen in Java before
11801176
* emitting.
11811177
*
1182-
* <p>{@code customCoalesceKey} is currently unused.
1183-
*
11841178
* @param surfaceId
11851179
* @param reactTag
11861180
* @param eventName
@@ -1200,6 +1194,28 @@ public void receiveEvent(
12001194
@EventCategoryDef int eventCategory,
12011195
boolean experimentalIsSynchronous,
12021196
long eventTimestamp) {
1197+
receiveEvent(
1198+
surfaceId,
1199+
reactTag,
1200+
eventName,
1201+
canCoalesceEvent,
1202+
params,
1203+
eventCategory,
1204+
experimentalIsSynchronous,
1205+
eventTimestamp,
1206+
0);
1207+
}
1208+
1209+
public void receiveEvent(
1210+
int surfaceId,
1211+
int reactTag,
1212+
String eventName,
1213+
boolean canCoalesceEvent,
1214+
@Nullable WritableMap params,
1215+
@EventCategoryDef int eventCategory,
1216+
boolean experimentalIsSynchronous,
1217+
long eventTimestamp,
1218+
int customCoalesceKey) {
12031219

12041220
if (ReactBuildConfig.DEBUG && surfaceId == View.NO_ID) {
12051221
FLog.d(TAG, "Emitted event without surfaceId: [%d] %s", reactTag, eventName);
@@ -1225,7 +1241,14 @@ public void receiveEvent(
12251241
}
12261242

12271243
mMountingManager.dispatchEvent(
1228-
surfaceId, reactTag, eventName, canCoalesceEvent, params, eventCategory, eventTimestamp);
1244+
surfaceId,
1245+
reactTag,
1246+
eventName,
1247+
canCoalesceEvent,
1248+
params,
1249+
eventCategory,
1250+
eventTimestamp,
1251+
customCoalesceKey);
12291252
}
12301253

12311254
@Override

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/events/EventEmitterWrapper.kt

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ internal class EventEmitterWrapper private constructor() : HybridClassBase() {
4040
eventName: String,
4141
params: NativeMap?,
4242
eventTimestamp: Long,
43+
customCoalesceKey: Int,
4344
)
4445

4546
/**
@@ -82,10 +83,29 @@ internal class EventEmitterWrapper private constructor() : HybridClassBase() {
8283
*/
8384
@Synchronized
8485
fun dispatchUnique(eventName: String, params: WritableMap?, eventTimestamp: Long) {
86+
dispatchUnique(eventName, params, eventTimestamp, 0)
87+
}
88+
89+
/**
90+
* Invokes the execution of the C++ EventEmitter. C++ will coalesce events sent to the same target
91+
* with the same event name and coalescing key.
92+
*
93+
* @param eventName [String] name of the event to execute.
94+
* @param params [WritableMap] payload of the event
95+
* @param eventTimestamp timestamp when the event was triggered (in milliseconds since boot)
96+
* @param customCoalesceKey key that determines which same-name events can coalesce together
97+
*/
98+
@Synchronized
99+
fun dispatchUnique(
100+
eventName: String,
101+
params: WritableMap?,
102+
eventTimestamp: Long,
103+
customCoalesceKey: Int,
104+
) {
85105
if (!isValid) {
86106
return
87107
}
88-
dispatchUniqueEvent(eventName, params as NativeMap?, eventTimestamp)
108+
dispatchUniqueEvent(eventName, params as NativeMap?, eventTimestamp, customCoalesceKey)
89109
}
90110

91111
@Synchronized

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/events/FabricEventEmitter.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ internal class FabricEventEmitter(private val uiManager: FabricUIManager) : RCTM
5858
category,
5959
false,
6060
eventTimestamp,
61+
customCoalesceKey,
6162
)
6263
} finally {
6364
Systrace.endSection(Systrace.TRACE_TAG_REACT)

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ internal class MountingManager(
335335
params: WritableMap?,
336336
@EventCategoryDef eventCategory: Int,
337337
eventTimestamp: Long,
338+
customCoalesceKey: Int = 0,
338339
) {
339340
val smm = getSurfaceMountingManager(surfaceId, reactTag)
340341
if (smm == null) {
@@ -347,7 +348,15 @@ internal class MountingManager(
347348
)
348349
return
349350
}
350-
smm.dispatchEvent(reactTag, eventName, canCoalesceEvent, params, eventCategory, eventTimestamp)
351+
smm.dispatchEvent(
352+
reactTag,
353+
eventName,
354+
canCoalesceEvent,
355+
params,
356+
eventCategory,
357+
eventTimestamp,
358+
customCoalesceKey,
359+
)
351360
}
352361

353362
private fun getSurfaceMountingManager(surfaceId: Int, reactTag: Int): SurfaceMountingManager? =

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.kt

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,6 +1204,7 @@ internal constructor(
12041204
params: WritableMap?,
12051205
@EventCategoryDef eventCategory: Int,
12061206
eventTimestamp: Long,
1207+
customCoalesceKey: Int = 0,
12071208
) {
12081209
val viewState = registryGet(reactTag)
12091210
if (viewState == null) {
@@ -1225,7 +1226,14 @@ internal constructor(
12251226
viewState.pendingEventQueue
12261227
?: LinkedList<PendingViewEvent>().also { viewState.pendingEventQueue = it }
12271228
queue.add(
1228-
PendingViewEvent(eventName, params, eventCategory, canCoalesceEvent, eventTimestamp)
1229+
PendingViewEvent(
1230+
eventName,
1231+
params,
1232+
eventCategory,
1233+
canCoalesceEvent,
1234+
eventTimestamp,
1235+
customCoalesceKey,
1236+
)
12291237
)
12301238
return
12311239
}
@@ -1241,7 +1249,7 @@ internal constructor(
12411249

12421250
checkNotNull(emitter)
12431251
if (canCoalesceEvent) {
1244-
emitter.dispatchUnique(eventName, params, eventTimestamp)
1252+
emitter.dispatchUnique(eventName, params, eventTimestamp, customCoalesceKey)
12451253
} else {
12461254
emitter.dispatch(eventName, params, eventCategory, eventTimestamp)
12471255
}
@@ -1286,10 +1294,11 @@ internal constructor(
12861294
@field:EventCategoryDef private val eventCategory: Int,
12871295
private val canCoalesceEvent: Boolean,
12881296
private val eventTimestamp: Long,
1297+
private val customCoalesceKey: Int,
12891298
) {
12901299
fun dispatch(eventEmitter: EventEmitterWrapper) {
12911300
if (canCoalesceEvent) {
1292-
eventEmitter.dispatchUnique(eventName, params, eventTimestamp)
1301+
eventEmitter.dispatchUnique(eventName, params, eventTimestamp, customCoalesceKey)
12931302
} else {
12941303
eventEmitter.dispatch(eventName, params, eventCategory, eventTimestamp)
12951304
}

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/RCTModernEventEmitter.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public interface RCTModernEventEmitter : RCTEventEmitter {
3030

3131
@Deprecated("Use the overload with eventTimestamp parameter instead.")
3232
public fun receiveEvent(surfaceId: Int, targetTag: Int, eventName: String, params: WritableMap?) {
33-
// We assume this event can't be coalesced. `customCoalesceKey` has no meaning in Fabric.
33+
// We assume this event can't be coalesced.
3434
receiveEvent(surfaceId, targetTag, eventName, false, 0, params, EventCategoryDef.UNSPECIFIED)
3535
}
3636

packages/react-native/ReactAndroid/src/main/jni/react/fabric/EventEmitterWrapper.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,17 @@ void EventEmitterWrapper::dispatchEventSynchronously(
6666
void EventEmitterWrapper::dispatchUniqueEvent(
6767
std::string eventName,
6868
NativeMap* payload,
69-
jlong eventTimestamp) {
69+
jlong eventTimestamp,
70+
int customCoalesceKey) {
7071
// It is marginal, but possible for this to be constructed without a valid
7172
// EventEmitter. In those cases, make sure we noop/blackhole events instead of
7273
// crashing.
7374
if (eventEmitter != nullptr) {
7475
eventEmitter->dispatchUniqueEvent(
7576
std::move(eventName),
7677
(payload != nullptr) ? payload->consume() : folly::dynamic::object(),
77-
highResTimeStampFromMillis(eventTimestamp));
78+
highResTimeStampFromMillis(eventTimestamp),
79+
customCoalesceKey);
7880
}
7981
}
8082

packages/react-native/ReactAndroid/src/main/jni/react/fabric/EventEmitterWrapper.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ class EventEmitterWrapper : public jni::HybridClass<EventEmitterWrapper> {
2727

2828
void dispatchEvent(std::string eventName, NativeMap *payload, int category, jlong eventTimestamp);
2929
void dispatchEventSynchronously(std::string eventName, NativeMap *params, jlong eventTimestamp);
30-
void dispatchUniqueEvent(std::string eventName, NativeMap *payload, jlong eventTimestamp);
30+
void dispatchUniqueEvent(
31+
std::string eventName,
32+
NativeMap *payload,
33+
jlong eventTimestamp,
34+
int customCoalesceKey);
3135
};
3236

3337
} // namespace facebook::react

packages/react-native/ReactAndroid/src/test/java/com/facebook/react/fabric/SurfaceMountingManagerEventOrderingTest.kt

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ class SurfaceMountingManagerEventOrderingTest {
167167
)
168168

169169
verify(emitter, never()).dispatch(any(), any(), any(), any())
170-
verify(emitter, never()).dispatchUnique(any(), any(), any())
170+
verify(emitter, never()).dispatchUnique(any(), any(), any(), any())
171171
verifyNoMoreInteractions(emitter)
172172
}
173173

@@ -179,10 +179,21 @@ class SurfaceMountingManagerEventOrderingTest {
179179

180180
smm.dispatchEvent(reactTag, "topScroll", true, null, EventCategoryDef.UNSPECIFIED, 0L)
181181

182-
verify(emitter).dispatchUnique("topScroll", null, 0L)
182+
verify(emitter).dispatchUnique("topScroll", null, 0L, 0)
183183
verify(emitter, never()).dispatch("topScroll", null, EventCategoryDef.UNSPECIFIED, 0L)
184184
}
185185

186+
@Test
187+
fun dispatchEvent_forwardsCoalescingKey_forCoalesceableEvents() {
188+
val smm = startSurfaceWithView()
189+
val emitter: EventEmitterWrapper = mock()
190+
smm.updateEventEmitter(reactTag, emitter)
191+
192+
smm.dispatchEvent(reactTag, "topScroll", true, null, EventCategoryDef.UNSPECIFIED, 0L, 7)
193+
194+
verify(emitter).dispatchUnique("topScroll", null, 0L, 7)
195+
}
196+
186197
@Test
187198
fun dispatchEvent_coalesceableEvents_drainedFromQueueUseDispatchUnique() {
188199
val smm = startSurfaceWithView()
@@ -192,7 +203,19 @@ class SurfaceMountingManagerEventOrderingTest {
192203
val emitter: EventEmitterWrapper = mock()
193204
smm.updateEventEmitter(reactTag, emitter)
194205

195-
verify(emitter).dispatchUnique("topScroll", null, 1L)
206+
verify(emitter).dispatchUnique("topScroll", null, 1L, 0)
207+
}
208+
209+
@Test
210+
fun dispatchEvent_coalesceableEvents_drainedFromQueueForwardCoalescingKey() {
211+
val smm = startSurfaceWithView()
212+
213+
smm.dispatchEvent(reactTag, "topScroll", true, null, EventCategoryDef.UNSPECIFIED, 1L, 9)
214+
215+
val emitter: EventEmitterWrapper = mock()
216+
smm.updateEventEmitter(reactTag, emitter)
217+
218+
verify(emitter).dispatchUnique("topScroll", null, 1L, 9)
196219
}
197220

198221
/** After a drain cycle, subsequent events should dispatch directly (fast path). */
@@ -308,7 +331,7 @@ class SurfaceMountingManagerEventOrderingTest {
308331

309332
val ordered = inOrder(emitter)
310333
ordered.verify(emitter).dispatch("topChange", null, EventCategoryDef.UNSPECIFIED, 1L)
311-
ordered.verify(emitter).dispatchUnique("topScroll", null, 2L)
334+
ordered.verify(emitter).dispatchUnique("topScroll", null, 2L, 0)
312335
ordered.verify(emitter).dispatch("topFocus", null, EventCategoryDef.UNSPECIFIED, 3L)
313336
}
314337

0 commit comments

Comments
 (0)