Skip to content

Commit cc89cb2

Browse files
coadofacebook-github-bot
authored andcommitted
Android: Add setBundleSource method to ReactHost for changing bundle URL at runtime (facebook#54139)
Summary: Following the [RFC](react-native-community/discussions-and-proposals#933), this PR adds new `setBundleSource` methods to `ReactHost` for modifying bundle URL at runtime. The first one with signature: ```Kotlin public fun setBundleSource(debugServerHost: String, moduleName: String, queryBuilder: (Map<String, String>) -> Map<String, String> = { it }) ``` takes debugServerHost (set in packager connection settings), moduleName (set in DevSupportManager's jsAppBundleName), and queryBuilder (set in packager connection settings). Before updating settings, the packager connection is closed to reset the packager client, which will be newly created during reload with updated configuration. The second one for loading bundle from the file takes single `filePath` argument: ```Kotlin public fun setBundleSource(filePath: String) ``` It sets `customBundleFilePath` in `DevSupportManager` which has priority over other methods of loading the bundle in `jsBundleLoader` and reloads `ReactHost`. ## Changelog: [ANDROID][ADDED] - added new `setBundleSource` method to `ReactHost` for changing bundle URL at runtime. Test Plan: Started with running two Metro instances on ports `8081` and `8082` (first with white background, second with blue). Created a native button that toggles `debugServerHost` port and invokes `setBundleSource`. https://github.com/user-attachments/assets/7afe2cbc-6fef-44bc-930c-e9f9c4edd2bd For setting bundle file path, generated JS bundle with different background comparing to the one serving by Metro. Moved file to the `app/files` directory in android emulator and configured native button to invoke a `setBundleSource(filePath)`. https://github.com/user-attachments/assets/5e59d7b7-c6ae-475c-94e3-50d4ac69cf24 <details> <summary>code:</summary> Changing debug server host: `RNTesterActivity.kt`: ```Kotlin package com.facebook.react.uiapp import android.content.res.Configuration import android.graphics.Color import android.os.Bundle import android.view.View import android.widget.Button import android.widget.FrameLayout import androidx.core.graphics.drawable.toDrawable import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import com.facebook.common.logging.FLog import com.facebook.react.FBRNTesterEndToEndHelper import com.facebook.react.ReactActivity import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled import com.facebook.react.defaults.DefaultReactActivityDelegate import java.io.FileDescriptor import java.io.PrintWriter internal class RNTesterActivity : ReactActivity() { private var activePort = "8081" class RNTesterActivityDelegate(val activity: ReactActivity, mainComponentName: String) : DefaultReactActivityDelegate(activity, mainComponentName, fabricEnabled) { private val PARAM_ROUTE = "route" private lateinit var initialProps: Bundle override fun onCreate(savedInstanceState: Bundle?) { // Get remote param before calling super which uses it val bundle = activity.intent?.extras if (bundle != null && bundle.containsKey(PARAM_ROUTE)) { val routeUri = "rntester://example/${bundle.getString(PARAM_ROUTE)}Example" initialProps = Bundle().apply { putString("exampleFromAppetizeParams", routeUri) } } FBRNTesterEndToEndHelper.onCreate(activity.application) super.onCreate(savedInstanceState) } override fun getLaunchOptions() = if (this::initialProps.isInitialized) initialProps else Bundle() } private fun getButtonText(): String { return "Port: $activePort" } private fun setupPortButton(onClick: () -> Unit) { val portButton = Button(this).apply { text = getButtonText() setBackgroundColor(Color.rgb(0, 123, 255)) // Blue background setTextColor(Color.WHITE) setPadding(32, 16, 32, 16) textSize = 16f elevation = 8f } // Get the root view and add button to it val rootView = this.findViewById<FrameLayout>(android.R.id.content) val layoutParams = FrameLayout.LayoutParams( FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT ).apply { gravity = android.view.Gravity.TOP or android.view.Gravity.CENTER_HORIZONTAL topMargin = 200 // 50dp from top } rootView.addView(portButton, layoutParams) portButton.setOnClickListener { onClick() portButton.text = getButtonText() } } // set background color so it will show below transparent system bars on forced edge-to-edge private fun maybeUpdateBackgroundColor() { val isDarkMode = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES val color = if (isDarkMode) { Color.rgb(11, 6, 0) } else { Color.rgb(243, 248, 255) } window?.setBackgroundDrawable(color.toDrawable()) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) fullyDrawnReporter.addReporter() maybeUpdateBackgroundColor() reactDelegate?.reactHost?.let { setupPortButton { activePort = if (activePort == "8081") "8082" else "8081" reactHost.setBundleSource("10.0.2.2:$activePort", "js/RNTesterApp.android") // reactHost.setBundleSource("/data/user/0/com.facebook.react.uiapp/files/android.bundle") } } // register insets listener to update margins on the ReactRootView to avoid overlap w/ system // bars reactDelegate?.reactRootView?.let { rootView -> val insetsType: Int = WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() val windowInsetsListener = { view: View, windowInsets: WindowInsetsCompat -> val insets = windowInsets.getInsets(insetsType) (view.layoutParams as FrameLayout.LayoutParams).apply { setMargins(insets.left, insets.top, insets.right, insets.bottom) } WindowInsetsCompat.CONSUMED } ViewCompat.setOnApplyWindowInsetsListener(rootView, windowInsetsListener) } } override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) // update background color on UI mode change maybeUpdateBackgroundColor() } override fun createReactActivityDelegate() = RNTesterActivityDelegate(this, mainComponentName) override fun getMainComponentName() = "RNTesterApp" override fun dump( prefix: String, fd: FileDescriptor?, writer: PrintWriter, args: Array<String>?, ) { FBRNTesterEndToEndHelper.maybeDump(prefix, writer, args) } } ``` </detail> Differential Revision: D84713639 Pulled By: coado
1 parent bc90648 commit cc89cb2

File tree

7 files changed

+105
-22
lines changed

7 files changed

+105
-22
lines changed

packages/react-native/ReactAndroid/api/ReactAndroid.api

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,9 @@ public abstract interface class com/facebook/react/ReactHost {
239239
public abstract fun reload (Ljava/lang/String;)Lcom/facebook/react/interfaces/TaskInterface;
240240
public abstract fun removeBeforeDestroyListener (Lkotlin/jvm/functions/Function0;)V
241241
public abstract fun removeReactInstanceEventListener (Lcom/facebook/react/ReactInstanceEventListener;)V
242+
public fun setBundleSource (Ljava/lang/String;)V
243+
public fun setBundleSource (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
244+
public static synthetic fun setBundleSource$default (Lcom/facebook/react/ReactHost;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
242245
public abstract fun start ()Lcom/facebook/react/interfaces/TaskInterface;
243246
}
244247

@@ -1898,7 +1901,7 @@ public final class com/facebook/react/devsupport/DefaultDevLoadingViewImplementa
18981901
public class com/facebook/react/devsupport/DevServerHelper {
18991902
public fun <init> (Lcom/facebook/react/modules/debug/interfaces/DeveloperSettings;Landroid/content/Context;Lcom/facebook/react/packagerconnection/PackagerConnectionSettings;)V
19001903
public final fun closeInspectorConnection ()V
1901-
public final fun closePackagerConnection ()V
1904+
public final fun closePackagerConnection ()Landroid/os/AsyncTask;
19021905
public final fun disableDebugger ()V
19031906
public final fun downloadBundleFromURL (Lcom/facebook/react/devsupport/interfaces/DevBundleDownloadListener;Ljava/io/File;Ljava/lang/String;Lcom/facebook/react/devsupport/BundleDownloader$BundleInfo;)V
19041907
public final fun downloadBundleFromURL (Lcom/facebook/react/devsupport/interfaces/DevBundleDownloadListener;Ljava/io/File;Ljava/lang/String;Lcom/facebook/react/devsupport/BundleDownloader$BundleInfo;Lokhttp3/Request$Builder;)V
@@ -1925,7 +1928,8 @@ public abstract interface class com/facebook/react/devsupport/DevServerHelper$Pa
19251928

19261929
public abstract class com/facebook/react/devsupport/DevSupportManagerBase : com/facebook/react/devsupport/interfaces/DevSupportManager {
19271930
public static final field Companion Lcom/facebook/react/devsupport/DevSupportManagerBase$Companion;
1928-
public fun <init> (Landroid/content/Context;Lcom/facebook/react/devsupport/ReactInstanceDevHelper;Ljava/lang/String;ZLcom/facebook/react/devsupport/interfaces/RedBoxHandler;Lcom/facebook/react/devsupport/interfaces/DevBundleDownloadListener;ILjava/util/Map;Lcom/facebook/react/common/SurfaceDelegateFactory;Lcom/facebook/react/devsupport/interfaces/DevLoadingViewManager;Lcom/facebook/react/devsupport/interfaces/PausedInDebuggerOverlayManager;)V
1931+
public fun <init> (Landroid/content/Context;Lcom/facebook/react/devsupport/ReactInstanceDevHelper;Ljava/lang/String;ZLcom/facebook/react/devsupport/interfaces/RedBoxHandler;Lcom/facebook/react/devsupport/interfaces/DevBundleDownloadListener;ILjava/util/Map;Lcom/facebook/react/common/SurfaceDelegateFactory;Lcom/facebook/react/devsupport/interfaces/DevLoadingViewManager;Lcom/facebook/react/devsupport/interfaces/PausedInDebuggerOverlayManager;Ljava/lang/String;)V
1932+
public synthetic fun <init> (Landroid/content/Context;Lcom/facebook/react/devsupport/ReactInstanceDevHelper;Ljava/lang/String;ZLcom/facebook/react/devsupport/interfaces/RedBoxHandler;Lcom/facebook/react/devsupport/interfaces/DevBundleDownloadListener;ILjava/util/Map;Lcom/facebook/react/common/SurfaceDelegateFactory;Lcom/facebook/react/devsupport/interfaces/DevLoadingViewManager;Lcom/facebook/react/devsupport/interfaces/PausedInDebuggerOverlayManager;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
19291933
public fun addCustomDevOption (Ljava/lang/String;Lcom/facebook/react/devsupport/interfaces/DevOptionHandler;)V
19301934
public fun createRootView (Ljava/lang/String;)Landroid/view/View;
19311935
public fun createSurfaceDelegate (Ljava/lang/String;)Lcom/facebook/react/common/SurfaceDelegate;
@@ -1935,6 +1939,7 @@ public abstract class com/facebook/react/devsupport/DevSupportManagerBase : com/
19351939
protected final fun getApplicationContext ()Landroid/content/Context;
19361940
public fun getCurrentActivity ()Landroid/app/Activity;
19371941
public final fun getCurrentReactContext ()Lcom/facebook/react/bridge/ReactContext;
1942+
public fun getCustomBundleFilePath ()Ljava/lang/String;
19381943
public final fun getDevLoadingViewManager ()Lcom/facebook/react/devsupport/interfaces/DevLoadingViewManager;
19391944
public final fun getDevServerHelper ()Lcom/facebook/react/devsupport/DevServerHelper;
19401945
public final fun getDevSettings ()Lcom/facebook/react/modules/debug/interfaces/DeveloperSettings;
@@ -1964,10 +1969,12 @@ public abstract class com/facebook/react/devsupport/DevSupportManagerBase : com/
19641969
public fun reloadJSFromServer (Ljava/lang/String;Lcom/facebook/react/devsupport/interfaces/BundleLoadCallback;)V
19651970
public fun reloadSettings ()V
19661971
public fun setAdditionalOptionForPackager (Ljava/lang/String;Ljava/lang/String;)V
1972+
public fun setCustomBundleFilePath (Ljava/lang/String;)V
19671973
public final fun setDevLoadingViewManager (Lcom/facebook/react/devsupport/interfaces/DevLoadingViewManager;)V
19681974
public final fun setDevSupportEnabled (Z)V
19691975
public fun setFpsDebugEnabled (Z)V
19701976
public fun setHotModuleReplacementEnabled (Z)V
1977+
public final fun setJsAppBundleName (Ljava/lang/String;)V
19711978
public final fun setLastErrorCookie (I)V
19721979
public final fun setLastErrorStack ([Lcom/facebook/react/devsupport/interfaces/StackFrame;)V
19731980
public final fun setLastErrorTitle (Ljava/lang/String;)V
@@ -2131,6 +2138,7 @@ public abstract interface class com/facebook/react/devsupport/interfaces/DevSupp
21312138
public abstract fun downloadBundleResourceFromUrlSync (Ljava/lang/String;Ljava/io/File;)Ljava/io/File;
21322139
public abstract fun getCurrentActivity ()Landroid/app/Activity;
21332140
public abstract fun getCurrentReactContext ()Lcom/facebook/react/bridge/ReactContext;
2141+
public fun getCustomBundleFilePath ()Ljava/lang/String;
21342142
public abstract fun getDevSettings ()Lcom/facebook/react/modules/debug/interfaces/DeveloperSettings;
21352143
public abstract fun getDevSupportEnabled ()Z
21362144
public abstract fun getDownloadedJSBundleFile ()Ljava/lang/String;
@@ -2155,6 +2163,7 @@ public abstract interface class com/facebook/react/devsupport/interfaces/DevSupp
21552163
public abstract fun reloadJSFromServer (Ljava/lang/String;Lcom/facebook/react/devsupport/interfaces/BundleLoadCallback;)V
21562164
public abstract fun reloadSettings ()V
21572165
public abstract fun setAdditionalOptionForPackager (Ljava/lang/String;Ljava/lang/String;)V
2166+
public fun setCustomBundleFilePath (Ljava/lang/String;)V
21582167
public abstract fun setDevSupportEnabled (Z)V
21592168
public abstract fun setFpsDebugEnabled (Z)V
21602169
public abstract fun setHotModuleReplacementEnabled (Z)V
@@ -2993,6 +3002,8 @@ public class com/facebook/react/packagerconnection/PackagerConnectionSettings {
29933002
public fun resetDebugServerHost ()V
29943003
public final fun setAdditionalOptionForPackager (Ljava/lang/String;Ljava/lang/String;)V
29953004
public fun setDebugServerHost (Ljava/lang/String;)V
3005+
public final fun setPackagerOptionsUpdater (Lkotlin/jvm/functions/Function1;)V
3006+
public final fun updatePackagerOptions (Ljava/util/Map;)Ljava/util/Map;
29963007
}
29973008

29983009
public final class com/facebook/react/packagerconnection/ReconnectingWebSocket : okhttp3/WebSocketListener {
@@ -3071,6 +3082,8 @@ public final class com/facebook/react/runtime/ReactHostImpl : com/facebook/react
30713082
public fun reload (Ljava/lang/String;)Lcom/facebook/react/interfaces/TaskInterface;
30723083
public fun removeBeforeDestroyListener (Lkotlin/jvm/functions/Function0;)V
30733084
public fun removeReactInstanceEventListener (Lcom/facebook/react/ReactInstanceEventListener;)V
3085+
public fun setBundleSource (Ljava/lang/String;)V
3086+
public fun setBundleSource (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
30743087
public fun start ()Lcom/facebook/react/interfaces/TaskInterface;
30753088
}
30763089

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactHost.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,4 +189,21 @@ public interface ReactHost {
189189

190190
/** Remove a listener previously added with [addReactInstanceEventListener]. */
191191
public fun removeReactInstanceEventListener(listener: ReactInstanceEventListener)
192+
193+
/** Sets the source of the bundle to be loaded from the file system. */
194+
public fun setBundleSource(filePath: String): Unit = Unit
195+
196+
/**
197+
* Sets the source of the bundle to be loaded from the packager server and updates the packager
198+
* connection.
199+
*
200+
* @param debugServerHost host and port of the server, for example "localhost:8081"
201+
* @param moduleName the module name to load, for example "js/RNTesterApp.android"
202+
* @param queryBuilder a function that takes current packager options and returns updated options
203+
*/
204+
public fun setBundleSource(
205+
debugServerHost: String,
206+
moduleName: String,
207+
queryBuilder: (Map<String, String>) -> Map<String, String> = { it },
208+
): Unit = Unit
192209
}

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.kt

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -196,16 +196,18 @@ public open class DevServerHelper(
196196
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
197197
}
198198

199-
public fun closePackagerConnection() {
200-
object : AsyncTask<Void, Void, Void>() {
199+
public fun closePackagerConnection(): AsyncTask<Void, Void, Void> {
200+
val task =
201+
object : AsyncTask<Void, Void, Void>() {
201202
@Deprecated("This class needs to be rewritten to don't use AsyncTasks")
202203
override fun doInBackground(vararg params: Void): Void? {
203204
packagerClient?.close()
204205
packagerClient = null
205206
return null
206207
}
207208
}
208-
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
209+
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
210+
return task
209211
}
210212

211213
public fun openInspectorConnection() {
@@ -280,7 +282,11 @@ public open class DevServerHelper(
280282
): String {
281283
val dev = devMode
282284
val additionalOptionsBuilder = StringBuilder()
283-
for ((key, value) in packagerConnectionSettings.additionalOptionsForPackager) {
285+
val packagerOptions =
286+
packagerConnectionSettings.updatePackagerOptions(
287+
packagerConnectionSettings.additionalOptionsForPackager
288+
)
289+
for ((key, value) in packagerOptions) {
284290
if (value.isEmpty()) {
285291
continue
286292
}

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ import java.util.Locale
8585
public abstract class DevSupportManagerBase(
8686
protected val applicationContext: Context,
8787
public val reactInstanceDevHelper: ReactInstanceDevHelper,
88-
@get:JvmName("getJSAppBundleName") public val jsAppBundleName: String?,
88+
@get:JvmName("getJSAppBundleName") public var jsAppBundleName: String?,
8989
enableOnCreate: Boolean,
9090
public override val redBoxHandler: RedBoxHandler?,
9191
private val devBundleDownloadListener: DevBundleDownloadListener?,
@@ -94,6 +94,7 @@ public abstract class DevSupportManagerBase(
9494
private val surfaceDelegateFactory: SurfaceDelegateFactory?,
9595
public var devLoadingViewManager: DevLoadingViewManager?,
9696
private var pausedInDebuggerOverlayManager: PausedInDebuggerOverlayManager?,
97+
private var customBundleFilePathField: String? = null,
9798
) : DevSupportManager {
9899

99100
public interface CallbackWithBundleLoader {
@@ -132,6 +133,12 @@ public abstract class DevSupportManagerBase(
132133
reloadSettings()
133134
}
134135

136+
override var customBundleFilePath: String?
137+
get() = customBundleFilePathField
138+
set(value) {
139+
customBundleFilePathField = value
140+
}
141+
135142
override val sourceMapUrl: String
136143
get() = jsAppBundleName?.let { devServerHelper.getSourceMapUrl(it) } ?: ""
137144

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportManager.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ public interface DevSupportManager : JSExceptionHandler {
3636
public val currentActivity: Activity?
3737
public val currentReactContext: ReactContext?
3838

39+
public var customBundleFilePath: String?
40+
get() = null
41+
set(value) = Unit
42+
3943
public var devSupportEnabled: Boolean
4044

4145
public fun showNewJavaError(message: String?, e: Throwable)

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/PackagerConnectionSettings.kt

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,24 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*/
7-
8-
@file:Suppress("DEPRECATION") // PreferenceManager should be migrated to androidx
9-
107
package com.facebook.react.packagerconnection
118

129
import android.content.Context
13-
import android.content.SharedPreferences
14-
import android.preference.PreferenceManager
1510
import com.facebook.common.logging.FLog
1611
import com.facebook.react.modules.systeminfo.AndroidInfoHelpers
1712

1813
public open class PackagerConnectionSettings(private val appContext: Context) {
19-
private val preferences: SharedPreferences =
20-
PreferenceManager.getDefaultSharedPreferences(appContext)
2114
public val packageName: String = appContext.packageName
2215
private val _additionalOptionsForPackager: MutableMap<String, String> = mutableMapOf()
16+
private var _packagerOptionsUpdater: (Map<String, String>) -> Map<String, String> = { it }
17+
private var cachedHost: String? = null
2318

2419
public open var debugServerHost: String
2520
get() {
26-
// Check host setting first. If empty try to detect emulator type and use default
21+
// Check cached host first. If empty try to detect emulator type and use default
2722
// hostname for those
28-
val hostFromSettings = preferences.getString(PREFS_DEBUG_SERVER_HOST_KEY, null)
29-
if (!hostFromSettings.isNullOrEmpty()) {
30-
return hostFromSettings
23+
cachedHost?.let {
24+
return it
3125
}
3226
val host = AndroidInfoHelpers.getServerHost(appContext)
3327
if (host == AndroidInfoHelpers.DEVICE_LOCALHOST) {
@@ -36,20 +30,29 @@ public open class PackagerConnectionSettings(private val appContext: Context) {
3630
"You seem to be running on device. Run '${AndroidInfoHelpers.getAdbReverseTcpCommand(appContext)}' to forward the debug server's port to the device.",
3731
)
3832
}
33+
34+
cachedHost = host
3935
return host
4036
}
4137
set(host) {
4238
if (host.isEmpty()) {
43-
preferences.edit().remove(PREFS_DEBUG_SERVER_HOST_KEY).apply()
39+
cachedHost = null
4440
} else {
45-
preferences.edit().putString(PREFS_DEBUG_SERVER_HOST_KEY, host).apply()
41+
cachedHost = host
4642
}
4743
}
4844

4945
public open fun resetDebugServerHost() {
50-
preferences.edit().remove(PREFS_DEBUG_SERVER_HOST_KEY).apply()
46+
cachedHost = null
5147
}
5248

49+
public fun setPackagerOptionsUpdater(updater: (Map<String, String>) -> Map<String, String>) {
50+
_packagerOptionsUpdater = updater
51+
}
52+
53+
public fun updatePackagerOptions(options: Map<String, String>): Map<String, String> =
54+
_packagerOptionsUpdater(options)
55+
5356
public fun setAdditionalOptionForPackager(key: String, value: String) {
5457
_additionalOptionsForPackager[key] = value
5558
}
@@ -59,6 +62,5 @@ public open class PackagerConnectionSettings(private val appContext: Context) {
5962

6063
private companion object {
6164
private val TAG = PackagerConnectionSettings::class.java.simpleName
62-
private const val PREFS_DEBUG_SERVER_HOST_KEY = "debug_http_host"
6365
}
6466
}

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.kt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ import java.util.concurrent.atomic.AtomicInteger
7373
import java.util.concurrent.atomic.AtomicReference
7474
import kotlin.Unit
7575
import kotlin.concurrent.Volatile
76+
import kotlinx.coroutines.CoroutineScope
77+
import kotlinx.coroutines.Dispatchers
78+
import kotlinx.coroutines.launch
7679

7780
/**
7881
* A ReactHost is an object that manages a single [ReactInstance]. A ReactHost can be constructed
@@ -643,6 +646,29 @@ public class ReactHostImpl(
643646
}
644647
}
645648

649+
@ThreadConfined(value = ThreadConfined.UI)
650+
override fun setBundleSource(filePath: String) {
651+
devSupportManager.customBundleFilePath = filePath
652+
reload("Change bundle source")
653+
}
654+
655+
@ThreadConfined(value = ThreadConfined.UI)
656+
override fun setBundleSource(
657+
debugServerHost: String,
658+
moduleName: String,
659+
queryBuilder: (Map<String, String>) -> Map<String, String>,
660+
) {
661+
CoroutineScope(Dispatchers.Default).launch {
662+
(devSupportManager as DevSupportManagerBase).devServerHelper.closePackagerConnection()
663+
devSupportManager.devSettings.packagerConnectionSettings.let { it ->
664+
it.setPackagerOptionsUpdater(queryBuilder)
665+
it.debugServerHost = debugServerHost
666+
}
667+
devSupportManager.jsAppBundleName = moduleName
668+
reload("Changed bundle source")
669+
}
670+
}
671+
646672
@ThreadConfined(ThreadConfined.UI)
647673
override fun onConfigurationChanged(context: Context) {
648674
val currentReactContext = this.currentReactContext
@@ -1057,6 +1083,14 @@ public class ReactHostImpl(
10571083
get() {
10581084
stateTracker.enterState("getJSBundleLoader()")
10591085

1086+
if (devSupportManager.customBundleFilePath != null) {
1087+
return try {
1088+
Task.forResult(JSBundleLoader.createFileLoader(devSupportManager.customBundleFilePath!!))
1089+
} catch (e: Exception) {
1090+
Task.forError(e)
1091+
}
1092+
}
1093+
10601094
if (useDevSupport && allowPackagerServerAccess) {
10611095
return isMetroRunning.onSuccessTask(
10621096
{ task ->

0 commit comments

Comments
 (0)