[英]How to inject non static, on demand NativeModules into React Native Android?
In a hybrid app where multiple Activity
/ Fragment
instances nesting their own ReactRootView
s share a single ReactInstanceManager
instance, there seems to be no official way to inject a native module per Activity
/ Fragment
instance. 在一个混合应用程序中,多个嵌套他们自己的
ReactRootView
的Activity
/ Fragment
实例共享一个ReactInstanceManager
实例,似乎没有正式的方法为每个Activity
/ Fragment
实例注入一个本机模块。 By nature, native modules are singleton like javascript modules residing in a .js
file. 从本质上讲,本机模块与
.js
文件中的javascript模块一样,是单例的。 This is not the intended behavior if JS code running inside different instances of the same Fragment
wants to access Java/Kotlin side to interact with the local properties of the Fragment
. 如果在同一
Fragment
不同实例中运行的JS代码想要访问Java / Kotlin端以与Fragment
的本地属性进行交互,则这不是预期的行为。
I tried using registerAdditionalPackages()
method of ReactInstanceManager
but it fails with an assertion error like below if used simultaneously by multiple Fragment
s/ Activity
s. 我尝试使用
ReactInstanceManager
registerAdditionalPackages()
方法,但如果由多个Fragment
/ Activity
同时使用,则会失败,并出现如下所示的断言错误。
"Extending native modules with non-matching application contexts."
It wasn't a thread safety issue but a design consequence of registerAdditionalPackages()
implementation. 这不是线程安全问题,而是
registerAdditionalPackages()
实现的设计结果。 Is there another way, and if so, how do you access the injected module on the JS side? 还有另一种方法,如果是这样,如何在JS端访问注入的模块?
This problem kept me awake for days. 这个问题使我醒了好几天。 Finally, I have a solution.
最后,我有一个解决方案。 I hope it helps someone in the future.
我希望它对将来的人有所帮助。 The solution is in Kotlin but it is quite straightforward to translate it into Java.
该解决方案位于Kotlin中,但是将其转换为Java非常简单。
someVariable!!
is a "not null" assertion, uppercase SomeObject()
calls are instance creation, SomeType:SomeOtherType
is inheritence or implementation, val someVar:SomeType
is a variable declaration. 是“非空”声明,大写
SomeObject()
调用是实例创建, SomeType:SomeOtherType
是继承或实现, val someVar:SomeType
是变量声明。 The rest is the same. 其余部分相同。
Steps: 脚步:
1) Run the below code at the time you want to inject a module to your RN runtime. 1)在要将模块注入RN运行时时,运行以下代码。 Inside of
Activity.onCreate()
or Fragment.onCreateView()
are some good candidates. 在
Activity.onCreate()
或Fragment.onCreateView()
是一些不错的候选对象。 mReactInstanceManager
is your singleton, global react runtime. mReactInstanceManager
是您的单例全局反应运行时。 packageToInject
definition will be given later. packageToInject
定义将在以后给出。
synchronized(mReactInstanceManager!!.currentReactContext!!) {
val nativeModuleRegistryBuilder = NativeModuleRegistryBuilder(
mReactInstanceManager!!.currentReactContext as ReactApplicationContext?,
mReactInstanceManager!!,
false
)
nativeModuleRegistryBuilder.processPackage(packageToInject)
mReactInstanceManager!!.currentReactContext!!.catalystInstance!!.extendNativeModules(nativeModuleRegistryBuilder.build())
}
2) Instance held inside packageToInject
must be prepared like below to have a unique module with a unique name per Fragment
/ Activity
instance. 2)在
packageToInject
内packageToInject
实例必须按照以下方式进行准备,以具有每个Fragment
/ Activity
实例具有唯一名称的唯一模块。 Make these inner classes of your Fragment
or Activity
. 使这些内部类成为
Fragment
或Activity
。
class ReactManagerPackage : ReactPackage {
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
return emptyList()
}
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
val modules = ArrayList<NativeModule>()
modules.add(ReactBridge(reactContext))
return modules
}
}
class ReactBridge(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
override fun getName(): String {
return myFragmentOrActivity.hashCode().toString()
}
@ReactMethod
fun showToast(text: String) {
Toast.makeText(text, Toast.LENGTH_SHORT).show()
}
}
3) Pass the myFragmentOrActivity.hashCode().toString()
as a prop when you initiate mReactRootView.startReactApplication()
either in your Activity.onCreate()
or Fragment.onCreateView()
. 3)在
Activity.onCreate()
或Fragment.onCreateView()
启动mReactRootView.startReactApplication()
时,将myFragmentOrActivity.hashCode().toString()
作为道具mReactRootView.startReactApplication()
。 Put it in the bundle that you give to mReactRootView.startReactApplication()
as 3rd argument. 将其作为第三个参数放在提供给
mReactRootView.startReactApplication()
的捆绑软件中。
val bundle = Bundle()
bundle.putString("fragmentOrActivityHash", myFragmentOrActivity.hashCode().toString())
mReactRootView.startReactApplication(mReactInstanceManager, "MyRootComponent", bundle )
4) Use the prop in your component ( MyRootComponent
in this example) to retrieve your specific bridge. 4)使用组件中的prop(在本例中为
MyRootComponent
)来检索您的特定桥。 (Javascript) (JavaScript)
NativeModules[this.props.fragmentOrActivityHash].showToast("It works")
Profit! 利润!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.