mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Immediately dispatch events to the shared C++ infrastructure to support interruptability (#39380)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/39380 Changelog: [Internal][Added] - Created a new event dispatching pipeline that immediately moves events over to the C++ queue, along with the onFrame that triggers the event beat ticking mechanism. Reviewed By: sammy-SC Differential Revision: D49012996 fbshipit-source-id: 0bc9067e5b019f308ec1f45ca8bd83fd195b37ce
This commit is contained in:
committed by
Facebook GitHub Bot
parent
eddefec5f8
commit
daa2bc7865
+3
@@ -163,4 +163,7 @@ public class ReactFeatureFlags {
|
||||
|
||||
/** Default state updates and events to async batched priority. */
|
||||
public static boolean enableDefaultAsyncBatchedPriority = false;
|
||||
|
||||
/** Utilize shared Event C++ pipeline with fabric's renderer */
|
||||
public static boolean enableFabricSharedEventPipeline = false;
|
||||
}
|
||||
|
||||
+6
-1
@@ -83,6 +83,7 @@ import com.facebook.react.uimanager.events.BatchEventDispatchedListener;
|
||||
import com.facebook.react.uimanager.events.EventCategoryDef;
|
||||
import com.facebook.react.uimanager.events.EventDispatcher;
|
||||
import com.facebook.react.uimanager.events.EventDispatcherImpl;
|
||||
import com.facebook.react.uimanager.events.FabricEventDispatcher;
|
||||
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
||||
import com.facebook.react.views.text.TextLayoutManager;
|
||||
import com.facebook.react.views.text.TextLayoutManagerMapBuffer;
|
||||
@@ -220,7 +221,11 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
mMountingManager = new MountingManager(viewManagerRegistry, mMountItemExecutor);
|
||||
mMountItemDispatcher =
|
||||
new MountItemDispatcher(mMountingManager, new MountItemDispatchListener());
|
||||
mEventDispatcher = new EventDispatcherImpl(reactContext);
|
||||
if (ReactFeatureFlags.enableFabricSharedEventPipeline) {
|
||||
mEventDispatcher = new FabricEventDispatcher(reactContext);
|
||||
} else {
|
||||
mEventDispatcher = new EventDispatcherImpl(reactContext);
|
||||
}
|
||||
mBatchEventDispatchedListener = batchEventDispatchedListener;
|
||||
mReactApplicationContext.addLifecycleEventListener(this);
|
||||
|
||||
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and 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.uimanager.events;
|
||||
|
||||
import com.facebook.react.bridge.LifecycleEventListener;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
import com.facebook.react.modules.core.ChoreographerCompat;
|
||||
import com.facebook.react.modules.core.ReactChoreographer;
|
||||
import com.facebook.react.uimanager.common.UIManagerType;
|
||||
import com.facebook.systrace.Systrace;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* A singleton class that overrides {@link EventDispatcher} with no-op methods, to be used by
|
||||
* callers that expect an EventDispatcher when the instance doesn't exist.
|
||||
*/
|
||||
public class FabricEventDispatcher implements EventDispatcher, LifecycleEventListener {
|
||||
private final ReactEventEmitter mReactEventEmitter;
|
||||
private final ReactApplicationContext mReactContext;
|
||||
private final CopyOnWriteArrayList<EventDispatcherListener> mListeners =
|
||||
new CopyOnWriteArrayList<>();
|
||||
private final CopyOnWriteArrayList<BatchEventDispatchedListener> mPostEventDispatchListeners =
|
||||
new CopyOnWriteArrayList<>();
|
||||
private final FabricEventDispatcher.ScheduleDispatchFrameCallback mCurrentFrameCallback =
|
||||
new FabricEventDispatcher.ScheduleDispatchFrameCallback();
|
||||
|
||||
public FabricEventDispatcher(ReactApplicationContext reactContext) {
|
||||
mReactContext = reactContext;
|
||||
mReactContext.addLifecycleEventListener(this);
|
||||
mReactEventEmitter = new ReactEventEmitter(mReactContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchEvent(Event event) {
|
||||
event.dispatchModern(mReactEventEmitter);
|
||||
for (EventDispatcherListener listener : mListeners) {
|
||||
listener.onEventDispatch(event);
|
||||
}
|
||||
|
||||
event.dispose();
|
||||
maybePostFrameCallbackFromNonUI();
|
||||
}
|
||||
|
||||
public void dispatchAllEvents() {
|
||||
maybePostFrameCallbackFromNonUI();
|
||||
}
|
||||
|
||||
private void maybePostFrameCallbackFromNonUI() {
|
||||
if (mReactEventEmitter != null) {
|
||||
// If the host activity is paused, the frame callback may not be currently
|
||||
// posted. Ensure that it is so that this event gets delivered promptly.
|
||||
mCurrentFrameCallback.maybePostFromNonUI();
|
||||
} else {
|
||||
// No JS application has started yet, or resumed. This can happen when a ReactRootView is
|
||||
// added to view hierarchy, but ReactContext creation has not completed yet. In this case, any
|
||||
// touch event dispatch will hit this codepath, and we simply queue them so that they
|
||||
// are dispatched once ReactContext creation completes and JS app is running.
|
||||
}
|
||||
}
|
||||
|
||||
/** Add a listener to this EventDispatcher. */
|
||||
public void addListener(EventDispatcherListener listener) {
|
||||
mListeners.add(listener);
|
||||
}
|
||||
|
||||
/** Remove a listener from this EventDispatcher. */
|
||||
public void removeListener(EventDispatcherListener listener) {
|
||||
mListeners.remove(listener);
|
||||
}
|
||||
|
||||
public void addBatchEventDispatchedListener(BatchEventDispatchedListener listener) {
|
||||
mPostEventDispatchListeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeBatchEventDispatchedListener(BatchEventDispatchedListener listener) {
|
||||
mPostEventDispatchListeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHostResume() {
|
||||
maybePostFrameCallbackFromNonUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHostPause() {
|
||||
stopFrameCallback();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHostDestroy() {
|
||||
stopFrameCallback();
|
||||
}
|
||||
|
||||
public void onCatalystInstanceDestroyed() {
|
||||
UiThreadUtil.runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
stopFrameCallback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void stopFrameCallback() {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
mCurrentFrameCallback.stop();
|
||||
}
|
||||
|
||||
public void registerEventEmitter(@UIManagerType int uiManagerType, RCTEventEmitter eventEmitter) {
|
||||
mReactEventEmitter.register(uiManagerType, eventEmitter);
|
||||
}
|
||||
|
||||
public void registerEventEmitter(
|
||||
@UIManagerType int uiManagerType, RCTModernEventEmitter eventEmitter) {
|
||||
mReactEventEmitter.register(uiManagerType, eventEmitter);
|
||||
}
|
||||
|
||||
public void unregisterEventEmitter(@UIManagerType int uiManagerType) {
|
||||
mReactEventEmitter.unregister(uiManagerType);
|
||||
}
|
||||
|
||||
private class ScheduleDispatchFrameCallback extends ChoreographerCompat.FrameCallback {
|
||||
private volatile boolean mIsPosted = false;
|
||||
private boolean mShouldStop = false;
|
||||
|
||||
@Override
|
||||
public void doFrame(long frameTimeNanos) {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
|
||||
if (mShouldStop) {
|
||||
mIsPosted = false;
|
||||
} else {
|
||||
post();
|
||||
}
|
||||
|
||||
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "BatchEventDispatchedListeners");
|
||||
try {
|
||||
for (BatchEventDispatchedListener listener : mPostEventDispatchListeners) {
|
||||
listener.onBatchEventDispatched();
|
||||
}
|
||||
} finally {
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
mShouldStop = true;
|
||||
}
|
||||
|
||||
public void maybePost() {
|
||||
if (!mIsPosted) {
|
||||
mIsPosted = true;
|
||||
post();
|
||||
}
|
||||
}
|
||||
|
||||
private void post() {
|
||||
ReactChoreographer.getInstance()
|
||||
.postFrameCallback(ReactChoreographer.CallbackType.TIMERS_EVENTS, mCurrentFrameCallback);
|
||||
}
|
||||
|
||||
public void maybePostFromNonUI() {
|
||||
if (mIsPosted) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We should only hit this slow path when we receive events while the host activity is paused.
|
||||
if (mReactContext.isOnUiQueueThread()) {
|
||||
maybePost();
|
||||
} else {
|
||||
mReactContext.runOnUiQueueThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
maybePost();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user