xuqm 3 周之前
父节点
当前提交
16eb89066b

+ 4 - 0
android/app/build.gradle

@@ -110,6 +110,10 @@ android {
 dependencies {
     // The version of react-native is set by the React Native Gradle Plugin
     implementation("com.facebook.react:react-android")
+    implementation 'androidx.appcompat:appcompat:1.7.1'
+    implementation 'com.google.android.material:material:1.12.0'
+    implementation 'androidx.activity:activity:1.10.1'
+    implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
 
     if (hermesEnabled.toBoolean()) {
         implementation("com.facebook.react:hermes-android")

+ 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
+    }
+}

+ 4 - 2
android/app/src/main/java/com/metromultibundlerdemo/MainActivity.kt

@@ -4,6 +4,7 @@ import com.facebook.react.ReactActivity
 import com.facebook.react.ReactActivityDelegate
 import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
 import com.facebook.react.defaults.DefaultReactActivityDelegate
+import com.multiplebundle.MultipleReactActivityDelegate
 
 class MainActivity : ReactActivity() {
 
@@ -11,12 +12,13 @@ class MainActivity : ReactActivity() {
    * Returns the name of the main component registered from JavaScript. This is used to schedule
    * rendering of the component.
    */
-  override fun getMainComponentName(): String = "MetroMultibundlerDemo"
+  override fun getMainComponentName(): String = "MetroMultibundlerDemo1"
 
   /**
    * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]
    * which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
    */
   override fun createReactActivityDelegate(): ReactActivityDelegate =
-      DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)
+      MultipleReactActivityDelegate(this, mainComponentName, fabricEnabled)
+
 }

+ 28 - 26
android/app/src/main/java/com/metromultibundlerdemo/MainApplication.kt

@@ -14,31 +14,33 @@ import com.facebook.soloader.SoLoader
 
 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(MyReactNativePackage())
-            }
-
-        override fun getJSMainModuleName(): String = "index"
-
-        override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
-
-        override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
-        override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
-      }
-
-  override val reactHost: ReactHost
-    get() = getDefaultReactHost(applicationContext, reactNativeHost)
-
-  override fun onCreate() {
-    super.onCreate()
-    SoLoader.init(this, OpenSourceMergedSoMapping)
-    if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
-      // If you opted-in for the New Architecture, we load the native entry point for this app.
-      load()
+    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(MyReactNativePackage())
+                }
+
+            override fun getJSMainModuleName(): String = "commom"
+            override fun getBundleAssetName(): String = "common.android.bundle"
+
+
+            override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
+
+            override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
+            override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
+        }
+
+    override val reactHost: ReactHost
+        get() = getDefaultReactHost(applicationContext, reactNativeHost)
+
+    override fun onCreate() {
+        super.onCreate()
+        SoLoader.init(this, OpenSourceMergedSoMapping)
+        if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
+            // If you opted-in for the New Architecture, we load the native entry point for this app.
+            load()
+        }
     }
-  }
 }

+ 183 - 0
android/app/src/main/java/com/metromultibundlerdemo/MultipleReactActivityDelegate.kt

@@ -0,0 +1,183 @@
+package com.multiplebundle
+
+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
+import com.facebook.react.runtime.ReactHostHelper
+import com.facebook.react.runtime.ReactHostImpl
+import com.facebook.systrace.Systrace.traceSection
+
+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)
+
+        traceSection(
+            0L,
+            "ReactActivityDelegate.onCreate::init",
+            Runnable {
+                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!!
+}

+ 10 - 2
android/build.gradle

@@ -8,8 +8,9 @@ buildscript {
         kotlinVersion = "2.0.21"
     }
     repositories {
-        google()
-        mavenCentral()
+        maven {
+            url 'https://nexus-inner.51trust.com/repository/android/'
+        }
     }
     dependencies {
         classpath("com.android.tools.build:gradle")
@@ -17,5 +18,12 @@ buildscript {
         classpath("org.jetbrains.kotlin:kotlin-gradle-plugin")
     }
 }
+allprojects {
+    repositories {
+        maven {
+            url 'https://nexus-inner.51trust.com/repository/android/'
+        }
+    }
+}
 
 apply plugin: "com.facebook.react.rootproject"

+ 48 - 0
build.js

@@ -0,0 +1,48 @@
+const fs = require('fs');
+
+const clean = function (file) {
+  fs.writeFileSync(file, JSON.stringify({}));
+};
+
+const hasBuildInfo = function (file, path) {
+  const cacheFile = require(file);
+  return Boolean(cacheFile[path]);
+};
+
+const writeBuildInfo = function (file, path, id) {
+  const cacheFile = require(file);
+  cacheFile[path] = id;
+  fs.writeFileSync(file, JSON.stringify(cacheFile));
+};
+
+const getCacheFile = function (file, path) {
+  const cacheFile = require(file);
+  return cacheFile[path] || 0;
+};
+
+const isPwdFile = (path) => {
+  const cwd = __dirname.split('/').splice(-1, 1).toString();
+
+  const pathArray = path.split('/');
+  const map = new Map();
+  const reverseMap = new Map();
+
+  pathArray.forEach((it, indx) => {
+    map.set(it, indx);
+    reverseMap.set(indx, it);
+  });
+
+  if (pathArray.length - 2 == map.get(cwd)) {
+    return reverseMap.get(pathArray.length - 1).replace(/\.js/, '');
+  }
+
+  return '';
+};
+
+module.exports = {
+  hasBuildInfo,
+  writeBuildInfo,
+  getCacheFile,
+  clean,
+  isPwdFile,
+};

+ 6 - 0
common.js

@@ -0,0 +1,6 @@
+// 这里是存放 全模块的 通用 配置,它们会被打入 base 基础包中, 不要随意的添加! 这会让基础包变得越来越大
+import  'react';
+import  'react-native';
+// import 'react-native-device-info';
+
+console.log('init common -------->');

+ 1 - 0
config/bundleBuInfo.json

@@ -0,0 +1 @@
+{}

文件差异内容过多而无法显示
+ 0 - 0
config/bundleCommonInfo.json


+ 1 - 1
index.js

@@ -6,4 +6,4 @@ import {AppRegistry} from 'react-native';
 import App from './App';
 import {name as appName} from './app.json';
 
-AppRegistry.registerComponent(appName, () => App);
+AppRegistry.registerComponent("MetroMultibundlerDemo1", () => App);

+ 35 - 0
metro.common.config.js

@@ -0,0 +1,35 @@
+const { hasBuildInfo, writeBuildInfo, clean } = require('./build.js');
+const {mergeConfig, getDefaultConfig} = require('@react-native/metro-config');
+
+function createModuleIdFactory() {
+  const fileToIdMap = new Map();
+  let nextId = 0;
+  clean('./config/bundleCommonInfo.json');
+
+  // 如果是业务 模块请以 10000000 来自增命名
+  return (path) => {
+    let id = fileToIdMap.get(path);
+
+    if (typeof id !== 'number') {
+      id = nextId++;
+      fileToIdMap.set(path, id);
+
+      !hasBuildInfo('./config/bundleCommonInfo.json', path) &&
+        writeBuildInfo(
+          './config/bundleCommonInfo.json',
+          path,
+          fileToIdMap.get(path)
+        );
+    }
+
+    return id;
+  };
+}
+
+const config = {
+  serializer: {
+    createModuleIdFactory: createModuleIdFactory, // 给 bundle 一个id 避免冲突 cli 源码中这个id 是从1 开始 自增的
+  },
+};
+
+module.exports = mergeConfig(getDefaultConfig(__dirname), config);

+ 48 - 0
metro.main.config.js

@@ -0,0 +1,48 @@
+const { hasBuildInfo, getCacheFile, isPwdFile } = require('./build.js');
+const bundleBuInfo = require('./config/bundleBuInfo.json');
+const {mergeConfig, getDefaultConfig} = require('@react-native/metro-config');
+function postProcessModulesFilter(module) {
+  if (
+    module.path.indexOf('__prelude__') >= 0 ||
+    module.path.indexOf('polyfills') >= 0
+  ) {
+    return false;
+  }
+
+  return !hasBuildInfo('./config/bundleCommonInfo.json', module.path);
+}
+
+// 不要使用 string 会导致 bundle 体积陡增
+function createModuleIdFactory() {
+  // 如果是业务 模块请以 10000000 来自增命名
+  const fileToIdMap = new Map();
+  let nextId = 10000000;
+  let isFirst = false;
+
+  return (path) => {
+    if (getCacheFile('./config/bundleCommonInfo.json', path)) {
+      return getCacheFile('./config/bundleCommonInfo.json', path);
+    }
+
+    if (!isFirst && isPwdFile(path)) {
+      nextId = bundleBuInfo[isPwdFile(path)];
+      isFirst = true;
+    }
+
+    let id = fileToIdMap.get(path);
+    if (typeof id !== 'number') {
+      id = nextId++;
+      fileToIdMap.set(path, id);
+    }
+    return id;
+  };
+}
+
+const config = {
+  serializer: {
+    createModuleIdFactory: createModuleIdFactory, // 给 bundle 一个id 避免冲突 cli 源码中这个id 是从1 开始 自增的
+    processModuleFilter: postProcessModulesFilter, // 返回false 就不会build 进去
+  },
+};
+
+module.exports = mergeConfig(getDefaultConfig(__dirname), config);

+ 9 - 3
package.json

@@ -7,11 +7,17 @@
     "ios": "react-native run-ios",
     "lint": "eslint .",
     "start": "react-native start",
-    "test": "jest"
+    "test": "jest",
+    "build:common": "react-native bundle --platform android --dev false --entry-file ./common.js --bundle-output ./android/app/src/main/assets/common.android.bundle --assets-dest ./android/app/src/main/assets   --config ./metro.common.config.js  --minify true --reset-cache",
+    "build:index": "react-native bundle --platform android --dev false --entry-file ./index.js --bundle-output ./android/app/src/main/assets/index.android.bundle --assets-dest ./android/app/src/main/assets  --config ./metro.main.config.js  --minify true --reset-cache",
+
+    "build:common-ios": "react-native bundle --platform ios --dev false --entry-file ./common.js --bundle-output ./bundle/common.ios.bundle   --config ./metro.common.config.js  --minify false --reset-cache",
+    "build:index-ios": "react-native bundle --entry-file ./index.js --bundle-output ./bundle/index.ios.bundle --platform ios --assets-dest ./bundle  --config ./metro.main.config.js --minify false --dev false"
   },
   "dependencies": {
     "react": "19.0.0",
-    "react-native": "0.79.0"
+    "react-native": "0.79.0",
+    "react-native-device-info": "^14.0.4"
   },
   "devDependencies": {
     "@babel/core": "^7.25.2",
@@ -36,4 +42,4 @@
   "engines": {
     "node": ">=18"
   }
-}
+}

部分文件因为文件数量过多而无法显示