ソースを参照

feat(runtime): 新增 MultipleReactActivityDelegate 以支持分包加载- 添加 MultipleReactActivityDelegate 类,用于处理多个 React 活动
- 实现 ReactHostHelper 类,辅助加载bundle
- 修改 BuzActivity 和 MainApplication 以支持新的分包加载逻辑
- 更新 App.tsx,添加新功能按钮
- 在 package.json 中添加 --active-arch-only 参数以优化性能
- 在 gradle.properties 中启用配置缓存

xuqm 1 週間 前
コミット
bf61fdc1ac

+ 4 - 0
android/app/src/main/assets/app.android.bundle

@@ -0,0 +1,4 @@
+__d(function(g,r,i,a,m,e,d){var n=r(d[0]),p=r(d[1]),t=n(r(d[2]));p.AppRegistry.registerComponent(r(d[3]).Apps.App,function(){return t.default})},10000000,[5,3,10000001,491]);
+__d(function(g,_r,_i,a,m,_e,d){Object.defineProperty(_e,"__esModule",{value:!0}),_e.default=void 0;var e=_r(d[0]),t=(function(e,t){if("function"==typeof WeakMap)var n=new WeakMap,r=new WeakMap;return(function(e,t){if(!t&&e&&e.__esModule)return e;var o,i,u={__proto__:null,default:e};if(null===e||"object"!=typeof e&&"function"!=typeof e)return u;if(o=t?r:n){if(o.has(e))return o.get(e);o.set(e,u)}for(var s in e)"default"!==s&&{}.hasOwnProperty.call(e,s)&&((i=(o=Object.defineProperty)&&Object.getOwnPropertyDescriptor(e,s))&&(i.get||i.set)?o(u,s,i):u[s]=e[s]);return u})(e,t)})(_r(d[1])),n=t,r=_r(d[2]);var o=e.StyleSheet.create({container:{flex:1}});_e.default=function(){var i='dark'===(0,e.useColorScheme)();return(0,r.jsxs)(e.View,{style:o.container,children:[(0,r.jsx)(e.StatusBar,{barStyle:i?'light-content':'dark-content'}),(0,r.jsx)(e.View,{style:{height:100}}),(0,r.jsx)(e.Button,{title:'\u8fdb\u5165\u4e92\u8054\u7f51\u533b\u9662',onPress:function(){n.pushByName('hospital',{})}}),(0,r.jsx)(e.View,{style:{height:15}}),(0,r.jsx)(e.Button,{title:'\u8fdb\u5165\u533b\u7f51\u7b7e',onPress:function(){n.pushByName(t.Apps.Ywq,{})}})]})}},10000001,[3,491,243]);
+__r(108);
+__r(10000000);

ファイルの差分が大きいため隠しています
+ 1 - 1
android/app/src/main/assets/common.android.bundle


+ 0 - 0
bundle/drawable-mdpi/node_modules_reactnative_newappscreen_src_assets_reactdark.png → android/app/src/main/assets/drawable-mdpi/node_modules_reactnative_newappscreen_src_assets_reactdark.png


+ 0 - 0
bundle/drawable-mdpi/node_modules_reactnative_newappscreen_src_assets_reactlight.png → android/app/src/main/assets/drawable-mdpi/node_modules_reactnative_newappscreen_src_assets_reactlight.png


+ 4 - 0
android/app/src/main/assets/hospital.android.bundle

@@ -0,0 +1,4 @@
+__d(function(g,r,i,a,m,e,d){var t=r(d[0]),n=r(d[1]),p=t(r(d[2]));n.AppRegistry.registerComponent(r(d[3]).Apps.Hospital,function(){return p.default})},10000000,[5,3,10000001,491]);
+__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var t=r(d[0]),n=r(d[1]);var o=t.StyleSheet.create({container:{flex:1}});e.default=function(){var l='dark'===(0,t.useColorScheme)();return(0,n.jsxs)(t.View,{style:o.container,children:[(0,n.jsx)(t.StatusBar,{barStyle:l?'light-content':'dark-content'}),(0,n.jsx)(t.View,{style:{height:100}}),(0,n.jsx)(t.Text,{children:"\u4e92\u8054\u7f51\u533b\u9662"}),(0,n.jsx)(t.Button,{title:'\u8fd4\u56de',onPress:function(){(0,r(d[2]).pop)()}})]})}},10000001,[3,243,491]);
+__r(108);
+__r(10000000);

+ 0 - 0
bundle/raw/keep.xml → android/app/src/main/assets/raw/keep.xml


+ 4 - 0
android/app/src/main/assets/ywq.android.bundle

@@ -0,0 +1,4 @@
+__d(function(g,r,i,a,m,e,d){var n=r(d[0]),t=r(d[1]),p=n(r(d[2]));t.AppRegistry.registerComponent(r(d[3]).Apps.Ywq,function(){return p.default})},10000000,[5,3,10000001,491]);
+__d(function(g,r,i,a,m,e,d){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;var t=r(d[0]),n=r(d[1]);var o=t.StyleSheet.create({container:{flex:1}});e.default=function(){var l='dark'===(0,t.useColorScheme)();return(0,n.jsxs)(t.View,{style:o.container,children:[(0,n.jsx)(t.StatusBar,{barStyle:l?'light-content':'dark-content'}),(0,n.jsx)(t.View,{style:{height:100}}),(0,n.jsx)(t.Button,{title:'onConfirm',onPress:function(){}})]})}},10000001,[3,243]);
+__r(108);
+__r(10000000);

+ 175 - 0
android/app/src/main/java/com/facebook/react/runtime/MultipleReactActivityDelegate.kt

@@ -0,0 +1,175 @@
+package com.facebook.react.runtime
+
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.res.Configuration
+import android.os.Build
+import android.os.Bundle
+import android.util.Log
+import android.view.KeyEvent
+import com.facebook.react.ReactActivity
+import com.facebook.react.ReactDelegate
+import com.facebook.react.ReactInstanceEventListener
+import com.facebook.react.ReactInstanceManager
+import com.facebook.react.ReactRootView
+import com.facebook.react.bridge.JSBundleLoader
+import com.facebook.react.bridge.ReactContext
+import com.facebook.react.common.annotations.DeprecatedInNewArchitecture
+import com.facebook.react.defaults.DefaultReactActivityDelegate
+import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags.enableBridgelessArchitecture
+
+class MultipleReactActivityDelegate(
+    activity: ReactActivity,
+    mainComponentName: String,
+    fabricEnabled: Boolean,
+) : DefaultReactActivityDelegate(activity, mainComponentName, fabricEnabled) {
+    private var mReactDelegate: ReactDelegate? = null
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        val helper = ReactHostHelper(reactHost as ReactHostImpl)
+
+
+        val mainComponentName = this.mainComponentName
+        val launchOptions = this.composeLaunchOptions()
+        val activity = reactActivity
+        if (Build.VERSION.SDK_INT >= 26 && this.isWideColorGamutEnabled) {
+            activity.window.colorMode = ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT
+        }
+
+        if (enableBridgelessArchitecture()) {
+            this.mReactDelegate =
+                ReactDelegate(
+                    this.plainActivity,
+                    this.reactHost,
+                    mainComponentName,
+                    launchOptions,
+                )
+
+            reactHost?.start()?.waitForCompletion()
+            val result =
+                helper.loadBundle(
+                    JSBundleLoader.createAssetLoader(this.reactActivity, "assets://index.android.bundle", false),
+                )
+
+            Log.i("TestApp", "load biz bundle ==> $result")
+            this.loadApp(mainComponentName)
+        } else {
+            this.mReactDelegate =
+                object : ReactDelegate(
+                    this.plainActivity,
+                    this.reactNativeHost,
+                    mainComponentName,
+                    launchOptions,
+                    this.isFabricEnabled,
+                ) {
+                    override fun createRootView(): ReactRootView {
+                        var rootView: ReactRootView? = this@MultipleReactActivityDelegate.createRootView()
+                        if (rootView == null) {
+                            rootView = super.createRootView()
+                        }
+
+                        return rootView!!
+                    }
+                }
+
+            reactNativeHost.reactInstanceManager.addReactInstanceEventListener(
+                object : ReactInstanceEventListener {
+                    override fun onReactContextInitialized(context: ReactContext) {
+                        Log.i("TestApp", "Multiple onReactContextInitialized")
+
+                        val instance = reactNativeHost.reactInstanceManager.currentReactContext?.catalystInstance
+                        instance?.loadScriptFromAssets(context.assets, "assets://index.android.bundle", false)
+                        Log.i("TestApp", "loaded biz bundle")
+                        if (mainComponentName != null) {
+                            try {
+                                this@MultipleReactActivityDelegate.loadApp(mainComponentName)
+                            } catch (e: Exception) {
+                                Log.e("TestApp", "load app $mainComponentName")
+                            }
+                        }
+                    }
+                },
+            )
+            reactNativeHost.reactInstanceManager.createReactContextInBackground()
+        }
+    }
+
+    private fun loadAppOldWay() {
+    }
+
+    override fun getReactDelegate(): ReactDelegate = mReactDelegate!!
+
+    @DeprecatedInNewArchitecture(message = "Use getReactHost()")
+    override fun getReactInstanceManager(): ReactInstanceManager = mReactDelegate!!.reactInstanceManager
+
+    override fun loadApp(appKey: String?) {
+        mReactDelegate!!.loadApp(appKey)
+        plainActivity.setContentView(mReactDelegate!!.reactRootView)
+    }
+
+    override fun onUserLeaveHint() {
+        if (mReactDelegate != null) {
+            mReactDelegate!!.onUserLeaveHint()
+        }
+    }
+
+    override fun onPause() {
+        mReactDelegate!!.onHostPause()
+    }
+
+    override fun onResume() {
+        mReactDelegate!!.onHostResume()
+//        if (mPermissionsCallback != null) {
+//            mPermissionsCallback!!.invoke()
+//            mPermissionsCallback = null
+//        }
+    }
+
+    override fun onDestroy() {
+        mReactDelegate!!.onHostDestroy()
+    }
+
+    override fun onActivityResult(
+        requestCode: Int,
+        resultCode: Int,
+        data: Intent?,
+    ) {
+        mReactDelegate!!.onActivityResult(requestCode, resultCode, data, true)
+    }
+
+    override fun onKeyDown(
+        keyCode: Int,
+        event: KeyEvent?,
+    ): Boolean = mReactDelegate!!.onKeyDown(keyCode, event)
+
+    override fun onKeyUp(
+        keyCode: Int,
+        event: KeyEvent?,
+    ): Boolean = mReactDelegate!!.shouldShowDevMenuOrReload(keyCode, event)
+
+    override fun onKeyLongPress(
+        keyCode: Int,
+        event: KeyEvent?,
+    ): Boolean = mReactDelegate!!.onKeyLongPress(keyCode)
+
+    override fun onBackPressed(): Boolean = mReactDelegate!!.onBackPressed()
+
+    override fun onNewIntent(intent: Intent?): Boolean = mReactDelegate!!.onNewIntent(intent)
+
+    override fun onWindowFocusChanged(hasFocus: Boolean) {
+        mReactDelegate!!.onWindowFocusChanged(hasFocus)
+    }
+
+    override fun onConfigurationChanged(newConfig: Configuration?) {
+        mReactDelegate!!.onConfigurationChanged(newConfig)
+    }
+
+    /**
+     * Get the current [ReactContext] from ReactHost or ReactInstanceManager
+     *
+     *
+     * Do not store a reference to this, if the React instance is reloaded or destroyed, this
+     * context will no longer be valid.
+     */
+    override fun getCurrentReactContext(): ReactContext = mReactDelegate!!.currentReactContext!!
+}

+ 24 - 0
android/app/src/main/java/com/facebook/react/runtime/ReactHostHelper.kt

@@ -0,0 +1,24 @@
+package com.facebook.react.runtime
+
+import com.facebook.react.bridge.JSBundleLoader
+import com.facebook.react.interfaces.TaskInterface
+import com.facebook.react.runtime.internal.bolts.Task
+import kotlin.coroutines.Continuation
+import kotlin.jvm.internal.Intrinsics
+
+class ReactHostHelper(
+    private val delegate: ReactHostImpl,
+) {
+    fun loadBundle(bundleLoader: JSBundleLoader): Boolean? {
+        Intrinsics.checkNotNullParameter(bundleLoader, "bundlerLoader")
+        val task = delegate.loadBundle(bundleLoader)
+
+
+        task.waitForCompletion()
+
+        return task.getResult()
+    }
+    fun getOrCreateReactInstance() {
+        delegate.isInstanceInitialized
+    }
+}

+ 6 - 1
android/app/src/main/java/com/trust/ywx/BuzActivity.kt

@@ -6,6 +6,7 @@ import com.trust.ywx.specs.navigation.NavigationHelper
 import com.facebook.react.ReactActivityDelegate
 import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
 import com.facebook.react.defaults.DefaultReactActivityDelegate
+import com.facebook.react.runtime.MultipleReactActivityDelegate
 
 class BuzActivity : ReactActivity() {
 
@@ -20,7 +21,11 @@ class BuzActivity : ReactActivity() {
      * which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
      */
     override fun createReactActivityDelegate(): ReactActivityDelegate =
-        DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)
+        if (BuildConfig.BUILD_TYPE == "debug") DefaultReactActivityDelegate(
+            this,
+            mainComponentName,
+            fabricEnabled
+        ) else MultipleReactActivityDelegate(this, mainComponentName, fabricEnabled)
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)

+ 22 - 18
android/app/src/main/java/com/trust/ywx/MainApplication.kt

@@ -13,27 +13,31 @@ import com.trust.ywx.specs.navigation.NavigationPackage
 
 class MainApplication : Application(), ReactApplication {
 
-  override val reactNativeHost: ReactNativeHost =
-      object : DefaultReactNativeHost(this) {
-        override fun getPackages(): List<ReactPackage> =
-            PackageList(this).packages.apply {
-              // Packages that cannot be autolinked yet can be added manually here, for example:
-               add(NavigationPackage())
-            }
+    override val reactNativeHost: ReactNativeHost =
+        object : DefaultReactNativeHost(this) {
+            override fun getPackages(): List<ReactPackage> =
+                PackageList(this).packages.apply {
+                    // Packages that cannot be autolinked yet can be added manually here, for example:
+                    add(NavigationPackage())
+                }
 
-        override fun getJSMainModuleName(): String = "index"
+            override fun getJSMainModuleName(): String =
+                if (BuildConfig.BUILD_TYPE == "debug") "index" else "commom"
 
-        override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
+            override fun getBundleAssetName(): String =
+                if (BuildConfig.BUILD_TYPE == "debug") "index.android.bundle" else "common.android.bundle"
 
-        override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
-        override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
-      }
+            override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
 
-  override val reactHost: ReactHost
-    get() = getDefaultReactHost(applicationContext, reactNativeHost)
+            override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
+            override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
+        }
 
-  override fun onCreate() {
-    super.onCreate()
-    loadReactNative(this)
-  }
+    override val reactHost: ReactHost
+        get() = getDefaultReactHost(applicationContext, reactNativeHost)
+
+    override fun onCreate() {
+        super.onCreate()
+        loadReactNative(this)
+    }
 }

+ 1 - 0
android/gradle.properties

@@ -37,3 +37,4 @@ newArchEnabled=true
 # Use this property to enable or disable the Hermes JS engine.
 # If set to false, you will be using JSC instead.
 hermesEnabled=true
+org.gradle.configuration-cache=true

+ 0 - 4
bundle/app.android.bundle

@@ -1,4 +0,0 @@
-__d(function(g,r,i,a,m,e,d){var n=r(d[0]),t=r(d[1]),p=n(r(d[2]));t.AppRegistry.registerComponent("app",function(){return p.default})},10000000,[5,3,10000001]);
-__d(function(g,_r,_i,a,m,_e,d){Object.defineProperty(_e,"__esModule",{value:!0}),_e.default=void 0;var e=_r(d[0]),t=(function(e,t){if("function"==typeof WeakMap)var n=new WeakMap,r=new WeakMap;return(function(e,t){if(!t&&e&&e.__esModule)return e;var o,i,u={__proto__:null,default:e};if(null===e||"object"!=typeof e&&"function"!=typeof e)return u;if(o=t?r:n){if(o.has(e))return o.get(e);o.set(e,u)}for(var l in e)"default"!==l&&{}.hasOwnProperty.call(e,l)&&((i=(o=Object.defineProperty)&&Object.getOwnPropertyDescriptor(e,l))&&(i.get||i.set)?o(u,l,i):u[l]=e[l]);return u})(e,t)})(_r(d[1])),n=_r(d[2]);var r=e.StyleSheet.create({container:{flex:1}});_e.default=function(){var o='dark'===(0,e.useColorScheme)();return(0,n.jsxs)(e.View,{style:r.container,children:[(0,n.jsx)(e.StatusBar,{barStyle:o?'light-content':'dark-content'}),(0,n.jsx)(e.View,{style:{height:100}}),(0,n.jsx)(e.Button,{title:'\u8fdb\u5165\u4e92\u8054\u7f51\u533b\u9662',onPress:function(){console.log('>>>>',t),t.pushByName('hospital',{})}})]})}},10000001,[3,491,243]);
-__r(108);
-__r(10000000);

+ 1 - 1
package.json

@@ -3,7 +3,7 @@
   "version": "0.0.1",
   "private": true,
   "scripts": {
-    "android": "react-native run-android",
+    "android": "react-native run-android --active-arch-only",
     "ios": "react-native run-ios",
     "lint": "eslint .",
     "start": "react-native start",

+ 1 - 0
src/app/App.tsx

@@ -28,6 +28,7 @@ function App() {
           navigation.pushByName('hospital', {});
         }}
       />
+      <View style={{ height: 15 }} />
       <Button
         title={'进入医网签'}
         onPress={() => {

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません