package com.yourpackage // ⚠️ 修改为你的包名 import android.util.Log import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactContextBaseJavaModule import com.facebook.react.bridge.ReactMethod import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.JSBundleLoader import com.facebook.react.module.annotations.ReactModule /** * ModuleLoader Native 模块 * 用于在 React Native 运行时中动态加载子 bundle * * 支持新架构(ReactHostImpl)和旧架构(CatalystInstance) * * 集成步骤: * 1. 将此文件复制到你的 Android 项目的 java/com/yourpackage/ 目录 * 2. 修改包名为你的实际包名 * 3. 在 MainApplication.kt 中注册 ModuleLoaderPackage */ @ReactModule(name = ModuleLoaderModule.NAME) class ModuleLoaderModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { companion object { const val NAME = "ModuleLoader" private const val TAG = "ModuleLoaderModule" // 缓存反射获取的类和方法,避免重复反射 private var reactHostImplClass: Class<*>? = null private var reactInstanceClass: Class<*>? = null init { try { reactHostImplClass = Class.forName("com.facebook.react.runtime.ReactHostImpl") reactInstanceClass = Class.forName("com.facebook.react.runtime.ReactInstance") } catch (e: ClassNotFoundException) { Log.w(TAG, "New architecture classes not found, will use old architecture") } } } // 记录已加载的 bundle,避免重复加载 private val loadedBundles = mutableSetOf() override fun getName(): String = NAME /** * 加载子 bundle * * @param bundleId 模块 ID(如 "home", "details", "settings") * @param bundlePath bundle 文件路径(如 "modules/home.bundle") * @param promise 加载结果回调 */ @ReactMethod fun loadBusinessBundle(bundleId: String, bundlePath: String, promise: Promise) { Log.d(TAG, "loadBusinessBundle: bundleId=$bundleId, bundlePath=$bundlePath") // 已经加载过的 bundle,直接返回成功 if (loadedBundles.contains(bundleId)) { Log.d(TAG, "Bundle already loaded: $bundleId") val result = Arguments.createMap().apply { putBoolean("success", true) } promise.resolve(result) return } try { // 获取 ReactApplication val application = reactContext.applicationContext as? android.app.Application val reactApplication = application as? com.facebook.react.ReactApplication if (reactApplication == null) { Log.e(TAG, "Application is not a ReactApplication") val result = Arguments.createMap().apply { putBoolean("success", false) putString("errorMessage", "Application is not a ReactApplication") } promise.resolve(result) return } val reactHost = reactApplication.reactHost // 构建 assets 路径 val assetUrl = "assets://$bundlePath" Log.d(TAG, "Loading bundle from: $assetUrl") // 创建 JSBundleLoader val bundleLoader = JSBundleLoader.createAssetLoader( reactContext, assetUrl, false // 异步加载 ) // 检查是否是新架构(ReactHostImpl) val hostImplClass = reactHostImplClass if (reactHost != null && hostImplClass != null && hostImplClass.isInstance(reactHost)) { loadBundleViaReflection(reactHost, bundleLoader, bundleId, promise) } else { // 旧架构回退方案:使用 CatalystInstance Log.d(TAG, "Falling back to CatalystInstance (old architecture)") loadBundleWithCatalystInstance(bundleId, bundlePath, promise) } } catch (e: Exception) { Log.e(TAG, "Failed to load bundle: $bundleId", e) val result = Arguments.createMap().apply { putBoolean("success", false) putString("errorMessage", e.message ?: "Unknown error") } promise.resolve(result) } } /** * 新架构方案:完全通过反射获取 ReactInstance 并加载 bundle */ private fun loadBundleViaReflection( reactHost: Any, bundleLoader: JSBundleLoader, bundleId: String, promise: Promise ) { try { val hostImplClass = reactHostImplClass ?: throw Exception("ReactHostImpl class not found") val instanceClass = reactInstanceClass ?: throw Exception("ReactInstance class not found") // 通过反射获取 private 的 reactInstance 字段 val reactInstanceField = hostImplClass.getDeclaredField("reactInstance") reactInstanceField.isAccessible = true val reactInstance = reactInstanceField.get(reactHost) if (reactInstance == null) { Log.e(TAG, "ReactInstance is null") val result = Arguments.createMap().apply { putBoolean("success", false) putString("errorMessage", "ReactInstance not available") } promise.resolve(result) return } // 通过反射调用 ReactInstance.loadJSBundle 方法 val loadJSBundleMethod = instanceClass.getMethod("loadJSBundle", JSBundleLoader::class.java) loadJSBundleMethod.invoke(reactInstance, bundleLoader) // 标记为已加载 loadedBundles.add(bundleId) Log.d(TAG, "Bundle loaded successfully via ReactInstance: $bundleId") val result = Arguments.createMap().apply { putBoolean("success", true) } promise.resolve(result) } catch (e: Exception) { Log.e(TAG, "Failed to load bundle via ReactInstance: $bundleId", e) val result = Arguments.createMap().apply { putBoolean("success", false) putString("errorMessage", e.message ?: "Unknown error") } promise.resolve(result) } } /** * 旧架构回退方案:使用 CatalystInstance 加载 bundle */ @Suppress("DEPRECATION") private fun loadBundleWithCatalystInstance(bundleId: String, bundlePath: String, promise: Promise) { try { val catalystInstance = reactContext.catalystInstance if (catalystInstance == null) { Log.e(TAG, "CatalystInstance is null") val result = Arguments.createMap().apply { putBoolean("success", false) putString("errorMessage", "CatalystInstance not available") } promise.resolve(result) return } val assetPath = "assets://$bundlePath" catalystInstance.loadScriptFromAssets( reactContext.assets, assetPath, false ) loadedBundles.add(bundleId) Log.d(TAG, "Bundle loaded successfully via CatalystInstance: $bundleId") val result = Arguments.createMap().apply { putBoolean("success", true) } promise.resolve(result) } catch (e: Exception) { Log.e(TAG, "Failed to load bundle via CatalystInstance: $bundleId", e) val result = Arguments.createMap().apply { putBoolean("success", false) putString("errorMessage", e.message ?: "Unknown error") } promise.resolve(result) } } /** * 检查 bundle 是否已加载 */ @ReactMethod fun isBundleLoaded(bundleId: String, promise: Promise) { promise.resolve(loadedBundles.contains(bundleId)) } /** * 获取已加载的 bundle 列表 */ @ReactMethod fun getLoadedBundles(promise: Promise) { val array = Arguments.createArray() loadedBundles.forEach { array.pushString(it) } promise.resolve(array) } }