¿Cómo inyectar NativeModules no estáticos y bajo demanda en React Native Android?

En una aplicación híbrida en la que varias instancias de Activity / Fragment anidan en sus propios ReactRootView comparten una única instancia de ReactInstanceManager , no parece haber una forma oficial de inyectar un module nativo por instancia de Activity / Fragment . Por naturaleza, los modules nativos son únicos como los modules de JavaScript que residen en un file .js . Este no es el comportamiento previsto si el código JS que se ejecuta dentro de instancias diferentes del mismo Fragment desea acceder al lado de Java / Kotlin para interactuar con las properties locales del Fragment .

Intenté utilizar el método registerAdditionalPackages() de ReactInstanceManager pero falla con un error de afirmación como el siguiente si se usa simultáneamente por múltiples Fragment / Activity s.

"Extending native modules with non-matching application contexts."

No fue un problema de security de subprocesss, sino una consecuencia de layout de la implementación registerAdditionalPackages() . ¿Hay alguna otra manera y, de ser así, cómo accedes al module inyectado en el lado JS?

    Este problema me mantuvo despierto por días. Finalmente, tengo una solución. Espero que ayude a alguien en el futuro. La solución está en Kotlin, pero es bastante sencillo traducirla a Java. someVariable!! es una aserción "no nula", mayúsculas SomeObject() llamadas son creación de instancia, SomeType:SomeOtherType es inheritence o implementación, val someVar:SomeType es una statement de variable. El rest es lo mismo

    Pasos:

    1) Ejecute el siguiente código en el momento en que quiera inyectar un module a su RN en time de ejecución. Dentro de Activity.onCreate() o Fragment.onCreateView() hay algunos buenos candidatos. mReactInstanceManager es tu singleton, time de ejecución de reacción global. packageToInject definición de packageToInject se dará más adelante.

     synchronized(mReactInstanceManager!!.currentReactContext!!) { val nativeModuleRegistryBuilder = NativeModuleRegistryBuilder( mReactInstanceManager!!.currentReactContext as ReactApplicationContext?, mReactInstanceManager!!, false ) nativeModuleRegistryBuilder.processPackage(packageToInject) mReactInstanceManager!!.currentReactContext!!.catalystInstance!!.extendNativeModules(nativeModuleRegistryBuilder.build()) } 

    2) La instancia retenida dentro de packageToInject debe preparar como se muestra a continuación para tener un module único con un nombre único por instancia de Fragment / Activity . Haz estas classs internas de tu Fragment o 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) Pase el myFragmentOrActivity.hashCode().toString() como un apoyo cuando inicie mReactRootView.startReactApplication() en su Activity.onCreate() o Fragment.onCreateView() . Ponlo en el package que le das a mReactRootView.startReactApplication() como tercer argumento.

     val bundle = Bundle() bundle.putString("fragmentOrActivityHash", myFragmentOrActivity.hashCode().toString()) mReactRootView.startReactApplication(mReactInstanceManager, "MyRootComponent", bundle ) 

    4) Use el accesorio en su componente ( MyRootComponent en este ejemplo) para recuperar su puente específico. (Javascript)

     NativeModules[this.props.fragmentOrActivityHash].showToast("It works") 

    ¡Lucro!