Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/five-signs-fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@callstack/react-native-brownfield': patch
'@callstack/brownfield-navigation': patch
'@callstack/brownie': patch
---

fix: make sure android libs are loaded for expo apps
Empty file.
7 changes: 5 additions & 2 deletions docs/docs/docs/getting-started/android.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -185,14 +185,17 @@ import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative

object ReactNativeHostManager {
fun initialize(application: Application, onJSBundleLoaded: OnJSBundleLoaded? = null) {
loadReactNative(application) // **Only required for RN >= 0.80.0**

val packageList = PackageList(application).packages
ReactNativeBrownfield.initialize(application, packageList, onJSBundleLoaded)
}
}
```

:::warning Important
The `ReactNativeBrownfield::initialize(Application, ReactHost, OnJSBundleLoaded? = null)` function variant is deprecated and shouldn't be used any more. It will be removed in a future release.
This version is unsafe because passing a pre-constructed `ReactHost` can trigger `SoLoader` (e.g., via `ExpoReactHostFactory`) before native libraries are fully loaded, leading to potential runtime crashes.
:::

## 5. Add Build Config Fields

Add to `reactnativeapp/build.gradle.kts`:
Expand Down
4 changes: 2 additions & 2 deletions packages/brownfield-navigation/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,15 @@
"@babel/core": "^7.25.2",
"@babel/preset-env": "^7.25.3",
"@babel/runtime": "^7.25.0",
"@react-native/babel-preset": "0.82.1",
"@react-native/babel-preset": "0.83.2",
"@types/jest": "^30.0.0",
"@types/react": "^19.1.1",
"eslint": "^9.28.0",
"globals": "^16.2.0",
"import": "^0.0.6",
"nodemon": "^3.1.14",
"react": "19.1.1",
"react-native": "0.82.1",
"react-native": "0.83.2",
"react-native-builder-bob": "^0.41.0",
"typescript": "5.9.3"
},
Expand Down
6 changes: 3 additions & 3 deletions packages/brownie/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,15 @@
"@babel/preset-env": "^7.25.3",
"@babel/preset-typescript": "^7.27.1",
"@babel/runtime": "^7.25.0",
"@react-native/babel-preset": "0.82.1",
"@react-native/eslint-config": "0.82.1",
"@react-native/babel-preset": "0.83.2",
"@react-native/eslint-config": "0.83.2",
"@types/node": "^25.5.0",
"@types/react": "^19.1.1",
"eslint": "^9.39.3",
"globals": "^17.3.0",
"import": "^0.0.6",
"nodemon": "^3.1.14",
"react-native": "0.82.1",
"react-native": "0.83.2",
"react-native-builder-bob": "^0.41.0",
"typescript": "5.9.3"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ class ReactNativeBrownfield private constructor(val reactHost: ReactHost) {
private lateinit var instance: ReactNativeBrownfield
private val initialized = AtomicBoolean()
private val nativeLibsLoaded = AtomicBoolean()
private const val LOG_TAG = "ReactNativeBrownfield"

@JvmStatic
val shared: ReactNativeBrownfield get() = instance
Expand All @@ -50,6 +49,12 @@ class ReactNativeBrownfield private constructor(val reactHost: ReactHost) {
load()
}

@Deprecated(
message = "Unsafe when reactHost construction triggers SoLoader (e.g. ExpoReactHostFactory): " +
"the parameter is evaluated by the caller before loadNativeLibs() runs. " +
"Use initialize(application, onJSBundleLoaded) { reactHostFactory } instead.",
replaceWith = ReplaceWith("initialize(application, onJSBundleLoaded) { reactHost }")
Comment thread
artus9033 marked this conversation as resolved.
)
@JvmStatic
@JvmOverloads
fun initialize(
Expand All @@ -59,25 +64,32 @@ class ReactNativeBrownfield private constructor(val reactHost: ReactHost) {
) {
if (!initialized.getAndSet(true)) {
loadNativeLibs(application)
instance = ReactNativeBrownfield(reactHost)

preloadReactNative {
onJSBundleLoaded?.invoke(true)
}
installAndPreload(reactHost, onJSBundleLoaded)
}
}

@JvmStatic
fun initialize(
application: Application,
onJSBundleLoaded: OnJSBundleLoaded? = null,
reactHostFactory: () -> ReactHost
) {
if (!initialized.getAndSet(true)) {
loadNativeLibs(application)
installAndPreload(reactHostFactory(), onJSBundleLoaded)
}
}

@JvmStatic
@JvmOverloads
fun initialize(
application: Application,
options: HashMap<String, Any>,
onJSBundleLoaded: OnJSBundleLoaded? = null
) {
loadNativeLibs(application)

val reactHost: ReactHost by lazy {
getDefaultReactHost(
if (!initialized.getAndSet(true)) {
loadNativeLibs(application)
val reactHost = getDefaultReactHost(
context = application,
packageList = (options["packages"] as? List<*> ?: emptyList<ReactPackage>())
.filterIsInstance<ReactPackage>(),
Expand All @@ -89,9 +101,8 @@ class ReactNativeBrownfield private constructor(val reactHost: ReactHost) {
?: ReactBuildConfig.DEBUG,
jsRuntimeFactory = null
)
installAndPreload(reactHost, onJSBundleLoaded)
}

initialize(application, reactHost, onJSBundleLoaded)
}

@JvmStatic
Expand All @@ -116,6 +127,13 @@ class ReactNativeBrownfield private constructor(val reactHost: ReactHost) {
})
shared.reactHost.start()
}

private fun installAndPreload(reactHost: ReactHost, onJSBundleLoaded: OnJSBundleLoaded?) {
instance = ReactNativeBrownfield(reactHost)
preloadReactNative {
onJSBundleLoaded?.invoke(true)
}
}
}

/**
Expand Down
4 changes: 2 additions & 2 deletions packages/react-native-brownfield/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
"@babel/preset-env": "^7.25.3",
"@babel/runtime": "^7.25.0",
"@expo/config-plugins": "^54.0.4",
"@react-native/babel-preset": "0.82.1",
"@react-native/babel-preset": "0.83.2",
"@types/jest": "^30.0.0",
"@types/react": "^19.1.1",
"@vitest/coverage-v8": "^4.1.0",
Expand All @@ -102,7 +102,7 @@
"import": "^0.0.6",
"nodemon": "^3.1.14",
"react": "19.1.1",
"react-native": "0.82.1",
"react-native": "0.83.2",
"react-native-builder-bob": "^0.41.0",
"typescript": "5.9.3",
"vitest": "^4.1.4"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,19 @@ import android.content.res.Configuration
import com.callstack.reactnativebrownfield.OnJSBundleLoaded
import com.callstack.reactnativebrownfield.ReactNativeBrownfield
import com.facebook.react.PackageList
import com.facebook.react.ReactHost
import expo.modules.ApplicationLifecycleDispatcher
import expo.modules.ExpoReactHostFactory

object ReactNativeHostManager {
fun initialize(application: Application, onJSBundleLoaded: OnJSBundleLoaded? = null) {
ApplicationLifecycleDispatcher.onApplicationCreate(application)

val reactHost: ReactHost by lazy {
ReactNativeBrownfield.initialize(application, onJSBundleLoaded) {
ExpoReactHostFactory.getDefaultReactHost(
context = application.applicationContext,
packageList = PackageList(application).packages,
)
}

ReactNativeBrownfield.initialize(application, reactHost, onJSBundleLoaded)
}

fun onConfigurationChanged(application: Application, newConfig: Configuration) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import android.content.res.Configuration
import com.callstack.reactnativebrownfield.OnJSBundleLoaded
import com.callstack.reactnativebrownfield.ReactNativeBrownfield
import com.facebook.react.PackageList
import com.facebook.react.ReactHost
import com.facebook.react.ReactPackage
import com.facebook.react.defaults.DefaultReactNativeHost
import expo.modules.ApplicationLifecycleDispatcher
Expand Down Expand Up @@ -34,15 +33,12 @@ object ReactNativeHostManager {
override fun getBundleAssetName(): String = "index.android.bundle"
})


val reactHost: ReactHost by lazy {
ReactNativeBrownfield.initialize(application, onJSBundleLoaded) {
ExpoReactHostFactory.createFromReactNativeHost(
context = application.applicationContext,
reactNativeHost = reactNativeHost
)
}

ReactNativeBrownfield.initialize(application, reactHost, onJSBundleLoaded)
}

fun onConfigurationChanged(application: Application, newConfig: Configuration) {
Expand Down
Loading
Loading