You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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
Copy file name to clipboardExpand all lines: packages/react-native/ReactAndroid/api/ReactAndroid.api
+15-2Lines changed: 15 additions & 2 deletions
Original file line number
Diff line number
Diff line change
@@ -239,6 +239,9 @@ public abstract interface class com/facebook/react/ReactHost {
239
239
public abstract fun reload (Ljava/lang/String;)Lcom/facebook/react/interfaces/TaskInterface;
240
240
public abstract fun removeBeforeDestroyListener (Lkotlin/jvm/functions/Function0;)V
241
241
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
242
245
public abstract fun start ()Lcom/facebook/react/interfaces/TaskInterface;
243
246
}
244
247
@@ -1898,7 +1901,7 @@ public final class com/facebook/react/devsupport/DefaultDevLoadingViewImplementa
1898
1901
public class com/facebook/react/devsupport/DevServerHelper {
1899
1902
public fun <init> (Lcom/facebook/react/modules/debug/interfaces/DeveloperSettings;Landroid/content/Context;Lcom/facebook/react/packagerconnection/PackagerConnectionSettings;)V
1900
1903
public final fun closeInspectorConnection ()V
1901
-
public final fun closePackagerConnection ()V
1904
+
public final fun closePackagerConnection ()Landroid/os/AsyncTask;
1902
1905
public final fun disableDebugger ()V
1903
1906
public final fun downloadBundleFromURL (Lcom/facebook/react/devsupport/interfaces/DevBundleDownloadListener;Ljava/io/File;Ljava/lang/String;Lcom/facebook/react/devsupport/BundleDownloader$BundleInfo;)V
1904
1907
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
1925
1928
1926
1929
public abstract class com/facebook/react/devsupport/DevSupportManagerBase : com/facebook/react/devsupport/interfaces/DevSupportManager {
1927
1930
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
1929
1933
public fun addCustomDevOption (Ljava/lang/String;Lcom/facebook/react/devsupport/interfaces/DevOptionHandler;)V
1930
1934
public fun createRootView (Ljava/lang/String;)Landroid/view/View;
1931
1935
public fun createSurfaceDelegate (Ljava/lang/String;)Lcom/facebook/react/common/SurfaceDelegate;
@@ -1935,6 +1939,7 @@ public abstract class com/facebook/react/devsupport/DevSupportManagerBase : com/
1935
1939
protected final fun getApplicationContext ()Landroid/content/Context;
1936
1940
public fun getCurrentActivity ()Landroid/app/Activity;
1937
1941
public final fun getCurrentReactContext ()Lcom/facebook/react/bridge/ReactContext;
1942
+
public fun getCustomBundleFilePath ()Ljava/lang/String;
1938
1943
public final fun getDevLoadingViewManager ()Lcom/facebook/react/devsupport/interfaces/DevLoadingViewManager;
1939
1944
public final fun getDevServerHelper ()Lcom/facebook/react/devsupport/DevServerHelper;
1940
1945
public final fun getDevSettings ()Lcom/facebook/react/modules/debug/interfaces/DeveloperSettings;
@@ -1964,10 +1969,12 @@ public abstract class com/facebook/react/devsupport/DevSupportManagerBase : com/
1964
1969
public fun reloadJSFromServer (Ljava/lang/String;Lcom/facebook/react/devsupport/interfaces/BundleLoadCallback;)V
1965
1970
public fun reloadSettings ()V
1966
1971
public fun setAdditionalOptionForPackager (Ljava/lang/String;Ljava/lang/String;)V
1972
+
public fun setCustomBundleFilePath (Ljava/lang/String;)V
1967
1973
public final fun setDevLoadingViewManager (Lcom/facebook/react/devsupport/interfaces/DevLoadingViewManager;)V
1968
1974
public final fun setDevSupportEnabled (Z)V
1969
1975
public fun setFpsDebugEnabled (Z)V
1970
1976
public fun setHotModuleReplacementEnabled (Z)V
1977
+
public final fun setJsAppBundleName (Ljava/lang/String;)V
1971
1978
public final fun setLastErrorCookie (I)V
1972
1979
public final fun setLastErrorStack ([Lcom/facebook/react/devsupport/interfaces/StackFrame;)V
1973
1980
public final fun setLastErrorTitle (Ljava/lang/String;)V
@@ -2131,6 +2138,7 @@ public abstract interface class com/facebook/react/devsupport/interfaces/DevSupp
2131
2138
public abstract fun downloadBundleResourceFromUrlSync (Ljava/lang/String;Ljava/io/File;)Ljava/io/File;
2132
2139
public abstract fun getCurrentActivity ()Landroid/app/Activity;
2133
2140
public abstract fun getCurrentReactContext ()Lcom/facebook/react/bridge/ReactContext;
2141
+
public fun getCustomBundleFilePath ()Ljava/lang/String;
2134
2142
public abstract fun getDevSettings ()Lcom/facebook/react/modules/debug/interfaces/DeveloperSettings;
2135
2143
public abstract fun getDevSupportEnabled ()Z
2136
2144
public abstract fun getDownloadedJSBundleFile ()Ljava/lang/String;
@@ -2155,6 +2163,7 @@ public abstract interface class com/facebook/react/devsupport/interfaces/DevSupp
2155
2163
public abstract fun reloadJSFromServer (Ljava/lang/String;Lcom/facebook/react/devsupport/interfaces/BundleLoadCallback;)V
2156
2164
public abstract fun reloadSettings ()V
2157
2165
public abstract fun setAdditionalOptionForPackager (Ljava/lang/String;Ljava/lang/String;)V
2166
+
public fun setCustomBundleFilePath (Ljava/lang/String;)V
2158
2167
public abstract fun setDevSupportEnabled (Z)V
2159
2168
public abstract fun setFpsDebugEnabled (Z)V
2160
2169
public abstract fun setHotModuleReplacementEnabled (Z)V
@@ -2993,6 +3002,8 @@ public class com/facebook/react/packagerconnection/PackagerConnectionSettings {
2993
3002
public fun resetDebugServerHost ()V
2994
3003
public final fun setAdditionalOptionForPackager (Ljava/lang/String;Ljava/lang/String;)V
2995
3004
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;
2996
3007
}
2997
3008
2998
3009
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
3071
3082
public fun reload (Ljava/lang/String;)Lcom/facebook/react/interfaces/TaskInterface;
3072
3083
public fun removeBeforeDestroyListener (Lkotlin/jvm/functions/Function0;)V
3073
3084
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
3074
3087
public fun start ()Lcom/facebook/react/interfaces/TaskInterface;
Copy file name to clipboardExpand all lines: packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportManager.kt
+4Lines changed: 4 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -36,6 +36,10 @@ public interface DevSupportManager : JSExceptionHandler {
Copy file name to clipboardExpand all lines: packages/react-native/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/PackagerConnectionSettings.kt
+17-15Lines changed: 17 additions & 15 deletions
Original file line number
Diff line number
Diff line change
@@ -4,30 +4,24 @@
4
4
* This source code is licensed under the MIT license found in the
5
5
* LICENSE file in the root directory of this source tree.
6
6
*/
7
-
8
-
@file:Suppress("DEPRECATION") // PreferenceManager should be migrated to androidx
0 commit comments