From 2fbbdbb2ce897e8da3f471b08b93f167d566db1d Mon Sep 17 00:00:00 2001 From: Joshua Gross Date: Fri, 22 Jan 2021 19:29:00 -0800 Subject: [PATCH] Create V2 EventEmitter (ModernEventEmitter) interface that surfaceId can be passed into Summary: Create V2 EventEmitter that surfaceId can be passed into, with a backwards-compat layer, and some debug-only logging to help assist with migration. Changelog: [Changed][Android] RCTEventEmitter has been deprecated in favor of RCTModernEventEmitter for optimal Fabric support; RCTEventEmitter will continue to work in Fabric, but there are minor perf implications. Reviewed By: mdvacca Differential Revision: D26027104 fbshipit-source-id: 784ca092bbc88d19c82f6c42537c34460d96de86 --- .../com/facebook/react/bridge/UIManager.java | 12 +++++++ .../react/fabric/FabricUIManager.java | 16 ++++++++-- .../fabric/events/FabricEventEmitter.java | 12 +++++-- .../fabric/mounting/MountingManager.java | 5 +-- .../react/uimanager/UIManagerModule.java | 12 +++++-- .../react/uimanager/events/Event.java | 32 +++++++++++++++++-- .../uimanager/events/RCTEventEmitter.java | 9 ++++++ .../events/RCTModernEventEmitter.java | 24 ++++++++++++++ 8 files changed, 111 insertions(+), 11 deletions(-) create mode 100644 ReactAndroid/src/main/java/com/facebook/react/uimanager/events/RCTModernEventEmitter.java diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManager.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManager.java index 56a237f9f12..40776a84e82 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManager.java @@ -125,8 +125,20 @@ public interface UIManager extends JSIModule, PerformanceCounter { * @param eventName name of the event * @param event parameters */ + @Deprecated void receiveEvent(int reactTag, String eventName, @Nullable WritableMap event); + /** + * This method dispatches events from RN Android code to JS. The delivery of this event will not + * be queued in EventDispatcher class. + * + * @param surfaceId + * @param reactTag tag + * @param eventName name of the event + * @param event parameters + */ + void receiveEvent(int surfaceId, int reactTag, String eventName, @Nullable WritableMap event); + /** Resolves Direct Event name exposed to JS from the one known to the Native side. */ @Deprecated @Nullable diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index b3ef0937492..c5aef40652d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -53,6 +53,7 @@ import com.facebook.react.bridge.UIManager; import com.facebook.react.bridge.UIManagerListener; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.bridge.WritableMap; +import com.facebook.react.common.build.ReactBuildConfig; import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.fabric.events.EventBeatManager; import com.facebook.react.fabric.events.EventEmitterWrapper; @@ -90,8 +91,8 @@ import java.util.concurrent.CopyOnWriteArrayList; @SuppressLint("MissingNativeLoadLibrary") public class FabricUIManager implements UIManager, LifecycleEventListener { + public static final String TAG = FabricUIManager.class.getSimpleName(); - public static final String TAG = "FabricUIManager"; // The IS_DEVELOPMENT_ENVIRONMENT variable is used to log extra data when running fabric in a // development environment. DO NOT ENABLE THIS ON PRODUCTION OR YOU WILL BE FIRED! public static final boolean IS_DEVELOPMENT_ENVIRONMENT = false; @@ -895,7 +896,18 @@ public class FabricUIManager implements UIManager, LifecycleEventListener { @Override public void receiveEvent(int reactTag, String eventName, @Nullable WritableMap params) { - EventEmitterWrapper eventEmitter = mMountingManager.getEventEmitter(reactTag); + receiveEvent(-1, reactTag, eventName, params); + } + + @Override + public void receiveEvent( + int surfaceId, int reactTag, String eventName, @Nullable WritableMap params) { + if (ReactBuildConfig.DEBUG && surfaceId == -1) { + FLog.d(TAG, "Emitted event without surfaceId: [%d] %s", reactTag, eventName); + } + + EventEmitterWrapper eventEmitter = mMountingManager.getEventEmitter(surfaceId, reactTag); + if (eventEmitter == null) { // This can happen if the view has disappeared from the screen (because of async events) FLog.d(TAG, "Unable to invoke event: " + eventName + " for reactTag: " + reactTag); diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/events/FabricEventEmitter.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/events/FabricEventEmitter.java index 6b9578b442c..6d0654db38a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/events/FabricEventEmitter.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/events/FabricEventEmitter.java @@ -23,12 +23,12 @@ import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableNativeArray; import com.facebook.react.bridge.WritableNativeMap; import com.facebook.react.fabric.FabricUIManager; -import com.facebook.react.uimanager.events.RCTEventEmitter; +import com.facebook.react.uimanager.events.RCTModernEventEmitter; import com.facebook.systrace.Systrace; import java.util.HashSet; import java.util.Set; -public class FabricEventEmitter implements RCTEventEmitter { +public class FabricEventEmitter implements RCTModernEventEmitter { private static final String TAG = "FabricEventEmitter"; @@ -40,10 +40,16 @@ public class FabricEventEmitter implements RCTEventEmitter { @Override public void receiveEvent(int reactTag, @NonNull String eventName, @Nullable WritableMap params) { + receiveEvent(-1, reactTag, eventName, params); + } + + @Override + public void receiveEvent( + int surfaceId, int reactTag, String eventName, @Nullable WritableMap params) { Systrace.beginSection( Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "FabricEventEmitter.receiveEvent('" + eventName + "')"); - mUIManager.receiveEvent(reactTag, eventName, params); + mUIManager.receiveEvent(surfaceId, reactTag, eventName, params); Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java index 94c1c5ecf14..c31cf4db49a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java @@ -311,8 +311,9 @@ public class MountingManager { @AnyThread @ThreadConfined(ANY) - public @Nullable EventEmitterWrapper getEventEmitter(int reactTag) { - SurfaceMountingManager surfaceMountingManager = getSurfaceManagerForView(reactTag); + public @Nullable EventEmitterWrapper getEventEmitter(int surfaceId, int reactTag) { + SurfaceMountingManager surfaceMountingManager = + (surfaceId == -1 ? getSurfaceManagerForView(reactTag) : getSurfaceManager(surfaceId)); if (surfaceMountingManager == null) { return null; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java index 5c1383d0e17..9ef007a6587 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java @@ -87,6 +87,7 @@ import java.util.concurrent.CopyOnWriteArrayList; @ReactModule(name = UIManagerModule.NAME) public class UIManagerModule extends ReactContextBaseJavaModule implements OnBatchCompleteListener, LifecycleEventListener, UIManager { + public static final String TAG = UIManagerModule.class.getSimpleName(); /** Enables lazy discovery of a specific {@link ViewManager} by its name. */ public interface ViewManagerResolver { @@ -993,9 +994,16 @@ public class UIManagerModule extends ReactContextBaseJavaModule } @Override - public void receiveEvent(int targetTag, String eventName, @Nullable WritableMap event) { + public void receiveEvent(int reactTag, String eventName, @Nullable WritableMap event) { + receiveEvent(-1, reactTag, eventName, event); + } + + @Override + public void receiveEvent( + int surfaceId, int reactTag, String eventName, @Nullable WritableMap event) { + assert ViewUtil.getUIManagerType(reactTag) == DEFAULT; getReactApplicationContext() .getJSModule(RCTEventEmitter.class) - .receiveEvent(targetTag, eventName, event); + .receiveEvent(reactTag, eventName, event); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/Event.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/Event.java index 7d36c049ed9..621700d4d52 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/Event.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/Event.java @@ -21,18 +21,30 @@ public abstract class Event { private static int sUniqueID = 0; private boolean mInitialized; + private int mSurfaceId; private int mViewTag; private long mTimestampMs; private int mUniqueID = sUniqueID++; protected Event() {} + @Deprecated protected Event(int viewTag) { init(viewTag); } - /** This method needs to be called before event is sent to event dispatcher. */ + protected Event(int surfaceId, int viewTag) { + init(surfaceId, viewTag); + } + + @Deprecated protected void init(int viewTag) { + init(-1, viewTag); + } + + /** This method needs to be called before event is sent to event dispatcher. */ + protected void init(int surfaceId, int viewTag) { + mSurfaceId = surfaceId; mViewTag = viewTag; mTimestampMs = SystemClock.uptimeMillis(); mInitialized = true; @@ -43,6 +55,11 @@ public abstract class Event { return mViewTag; } + /** @return the surfaceId for the view that generated this event */ + public final int getSurfaceId() { + return mSurfaceId; + } + /** * @return the time at which the event happened in the {@link android.os.SystemClock#uptimeMillis} * base. @@ -100,6 +117,17 @@ public abstract class Event { /** @return the name of this event as registered in JS */ public abstract String getEventName(); - /** Dispatch this event to JS using the given event emitter. */ + /** + * Dispatch this event to JS using the given event emitter. Compatible with old and new renderer. + */ + @Deprecated public abstract void dispatch(RCTEventEmitter rctEventEmitter); + + /** + * Dispatch this event to JS using a V2 EventEmitter. Events must explicitly override this, by + * default it uses the V1 dispatcher. + */ + public void dispatchV2(RCTModernEventEmitter rctEventEmitter) { + dispatch(rctEventEmitter); + } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/RCTEventEmitter.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/RCTEventEmitter.java index da06d7257d5..fc2ba315806 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/RCTEventEmitter.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/RCTEventEmitter.java @@ -14,7 +14,16 @@ import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; @DoNotStrip +@Deprecated public interface RCTEventEmitter extends JavaScriptModule { + /** + * Deprecated in favor of RCTModernEventEmitter.receiveEvent. + * + * @param targetTag + * @param eventName + * @param event + */ + @Deprecated void receiveEvent(int targetTag, String eventName, @Nullable WritableMap event); void receiveTouches(String eventName, WritableArray touches, WritableArray changedIndices); diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/RCTModernEventEmitter.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/RCTModernEventEmitter.java new file mode 100644 index 00000000000..a05f2c83b7d --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/events/RCTModernEventEmitter.java @@ -0,0 +1,24 @@ +/* + * 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.uimanager.events; + +import androidx.annotation.Nullable; +import com.facebook.react.bridge.WritableMap; + +/** + * This is a transitional replacement for RCTEventEmitter that works with Fabric and non-Fabric + * renderers. RCTEventEmitter works with Fabric as well, but there are negative perf implications + * and it should be avoided. + * + *

This interface will *also* be deleted in the distant future and be replaced with a new + * interface that doesn't need the old `receiveEvent` method at all. But for the foreseeable future, + * this is the recommended interface to use for EventEmitters. + */ +public interface RCTModernEventEmitter extends RCTEventEmitter { + void receiveEvent(int surfaceId, int targetTag, String eventName, @Nullable WritableMap event); +}