forked from facebook/react-native
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Immediately dispatch events to the shared C++ infrastructure to suppo…
…rt interruptability (facebook#39380) Summary: Pull Request resolved: facebook#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
- Loading branch information
1 parent
eddefec
commit daa2bc7
Showing
3 changed files
with
196 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
187 changes: 187 additions & 0 deletions
187
...ReactAndroid/src/main/java/com/facebook/react/uimanager/events/FabricEventDispatcher.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} | ||
}); | ||
} | ||
} | ||
} | ||
} |