mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Shortcut emitDeviceEvent in bridgeless (2nd attempt) (#44590)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/44590 emitDeviceEvent is frequently used for perf-critical operations such as sending network responses from native to JS. We don't need to go through JavaScriptModule Proxy (which is missing caching in bridgeless) and instead can immediately invoke the callable JS module. Changelog: [Internal] Reviewed By: philIip Differential Revision: D57435750 fbshipit-source-id: 1c120073ac80afd95deb8e3e6f1c00c2d3d80133
This commit is contained in:
committed by
Facebook GitHub Bot
parent
1343313dc6
commit
b2ced62d33
+9
@@ -148,6 +148,8 @@ class BridgelessReactContext extends ReactApplicationContext implements EventDis
|
||||
&& mInteropModuleRegistry.shouldReturnInteropModule(jsInterface)) {
|
||||
return mInteropModuleRegistry.getInteropModule(jsInterface);
|
||||
}
|
||||
|
||||
// TODO T189052462: ReactContext caches JavaScriptModule instances
|
||||
JavaScriptModule interfaceProxy =
|
||||
(JavaScriptModule)
|
||||
Proxy.newProxyInstance(
|
||||
@@ -157,6 +159,13 @@ class BridgelessReactContext extends ReactApplicationContext implements EventDis
|
||||
return (T) interfaceProxy;
|
||||
}
|
||||
|
||||
/** Shortcut RCTDeviceEventEmitter.emit since it's frequently used */
|
||||
@Override
|
||||
public void emitDeviceEvent(String eventName, @Nullable Object args) {
|
||||
mReactHost.callFunctionOnModule(
|
||||
"RCTDeviceEventEmitter", "emit", Arguments.fromJavaArgs(new Object[] {eventName, args}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends NativeModule> boolean hasNativeModule(Class<T> nativeModuleInterface) {
|
||||
return mReactHost.hasNativeModule(nativeModuleInterface);
|
||||
|
||||
+30
-9
@@ -9,23 +9,31 @@ package com.facebook.react.runtime
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import com.facebook.react.bridge.WritableNativeArray
|
||||
import com.facebook.react.fabric.FabricUIManager
|
||||
import com.facebook.react.uimanager.UIManagerModule
|
||||
import com.facebook.testutils.shadows.ShadowArguments
|
||||
import com.facebook.testutils.shadows.ShadowNativeArray
|
||||
import com.facebook.testutils.shadows.ShadowSoLoader
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.ArgumentMatchers
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.Mockito.doReturn
|
||||
import org.mockito.Mockito.eq
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.times
|
||||
import org.mockito.Mockito.verify
|
||||
import org.robolectric.Robolectric
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
/** Tests [BridgelessReactContext] */
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(shadows = [ShadowSoLoader::class])
|
||||
@Config(
|
||||
shadows = [ShadowSoLoader::class, ShadowArguments::class, ShadowNativeArray.Writable::class])
|
||||
class BridgelessReactContextTest {
|
||||
private lateinit var context: Context
|
||||
private lateinit var reactHost: ReactHostImpl
|
||||
@@ -34,30 +42,43 @@ class BridgelessReactContextTest {
|
||||
@Before
|
||||
fun setUp() {
|
||||
context = Robolectric.buildActivity(Activity::class.java).create().get()
|
||||
reactHost = Mockito.mock(ReactHostImpl::class.java)
|
||||
reactHost = mock(ReactHostImpl::class.java)
|
||||
bridgelessReactContext = BridgelessReactContext(context, reactHost)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getNativeModuleTest() {
|
||||
val mUiManagerModule = Mockito.mock(UIManagerModule::class.java)
|
||||
val mUiManagerModule = mock(UIManagerModule::class.java)
|
||||
doReturn(mUiManagerModule)
|
||||
.`when`(reactHost)
|
||||
.getNativeModule(ArgumentMatchers.any<Class<UIManagerModule>>())
|
||||
val uiManagerModule = bridgelessReactContext.getNativeModule(UIManagerModule::class.java)
|
||||
Assertions.assertThat(uiManagerModule).isEqualTo(mUiManagerModule)
|
||||
assertThat(uiManagerModule).isEqualTo(mUiManagerModule)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getFabricUIManagerTest() {
|
||||
val fabricUiManager = Mockito.mock(FabricUIManager::class.java)
|
||||
val fabricUiManager = mock(FabricUIManager::class.java)
|
||||
doReturn(fabricUiManager).`when`(reactHost).uiManager
|
||||
Assertions.assertThat(bridgelessReactContext.getFabricUIManager()).isEqualTo(fabricUiManager)
|
||||
assertThat(bridgelessReactContext.getFabricUIManager()).isEqualTo(fabricUiManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getCatalystInstanceTest() {
|
||||
Assertions.assertThat(bridgelessReactContext.getCatalystInstance())
|
||||
assertThat(bridgelessReactContext.getCatalystInstance())
|
||||
.isInstanceOf(BridgelessCatalystInstance::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEmitDeviceEvent() {
|
||||
bridgelessReactContext.emitDeviceEvent("onNetworkResponseReceived", mapOf("foo" to "bar"))
|
||||
|
||||
val argsCapture = ArgumentCaptor.forClass(WritableNativeArray::class.java)
|
||||
verify(reactHost, times(1))
|
||||
.callFunctionOnModule(eq("RCTDeviceEventEmitter"), eq("emit"), argsCapture.capture())
|
||||
|
||||
val argsList = ShadowNativeArray.getContents(argsCapture.value)
|
||||
assertThat(argsList[0]).isEqualTo("onNetworkResponseReceived")
|
||||
@Suppress("UNCHECKED_CAST") assertThat((argsList[1] as Map<Any, Any>)["foo"]).isEqualTo("bar")
|
||||
}
|
||||
}
|
||||
|
||||
+14
@@ -8,14 +8,28 @@
|
||||
package com.facebook.testutils.shadows
|
||||
|
||||
import com.facebook.react.bridge.Arguments
|
||||
import com.facebook.react.bridge.JavaOnlyArray
|
||||
import com.facebook.react.bridge.JavaOnlyMap
|
||||
import com.facebook.react.bridge.WritableArray
|
||||
import com.facebook.react.bridge.WritableMap
|
||||
import com.facebook.react.bridge.WritableNativeArray
|
||||
import org.robolectric.annotation.Implementation
|
||||
import org.robolectric.annotation.Implements
|
||||
import org.robolectric.shadow.api.Shadow
|
||||
|
||||
@Implements(Arguments::class)
|
||||
class ShadowArguments {
|
||||
|
||||
companion object {
|
||||
@JvmStatic @Implementation fun createArray(): WritableArray = JavaOnlyArray()
|
||||
|
||||
@JvmStatic @Implementation fun createMap(): WritableMap = JavaOnlyMap()
|
||||
|
||||
@JvmStatic
|
||||
@Implementation
|
||||
fun fromJavaArgs(args: Array<Any?>): WritableNativeArray =
|
||||
WritableNativeArray().apply {
|
||||
(Shadow.extract(this) as ShadowNativeArray).contents = args.toList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.testutils.shadows
|
||||
|
||||
import com.facebook.react.bridge.NativeArray
|
||||
import com.facebook.react.bridge.ReadableNativeArray
|
||||
import com.facebook.react.bridge.WritableNativeArray
|
||||
import org.robolectric.annotation.Implements
|
||||
import org.robolectric.shadow.api.Shadow
|
||||
|
||||
// Mockito can't mock native methods, so shadow the entire class instead
|
||||
@Implements(NativeArray::class)
|
||||
public open class ShadowNativeArray {
|
||||
public var contents: List<Any?> = mutableListOf()
|
||||
|
||||
@Implements(ReadableNativeArray::class) public class Readable : ShadowNativeArray() {}
|
||||
|
||||
@Implements(WritableNativeArray::class) public class Writable : ShadowNativeArray() {}
|
||||
|
||||
public companion object {
|
||||
public fun getContents(array: NativeArray): List<Any?> =
|
||||
(Shadow.extract(array) as ShadowNativeArray).contents
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user