Skip to content

Commit 2725345

Browse files
authored
Merge pull request #148 from callstack/feat/rn-82-support
feat: support RN 0.82 & 0.81
2 parents 5fd209f + 61a18ca commit 2725345

File tree

18 files changed

+12518
-14277
lines changed

18 files changed

+12518
-14277
lines changed

android/src/main/java/com/callstack/reactnativebrownfield/ReactDelegateWrapper.kt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,15 @@ import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler
88

99
class ReactDelegateWrapper(
1010
private val activity: ComponentActivity?,
11-
private val reactHost: ReactHost,
11+
resolvedReactHost: ReactHost?,
1212
moduleName: String,
1313
launchOptions: Bundle?
14-
): ReactDelegate(activity, reactHost, moduleName, launchOptions){
14+
) : ReactDelegate(
15+
activity = activity!!,
16+
resolvedReactHost,
17+
appKey = moduleName,
18+
launchOptions = launchOptions,
19+
) {
1520
private lateinit var hardwareBackHandler: () -> Unit
1621
private val backBtnHandler = DefaultHardwareBackBtnHandler {
1722
hardwareBackHandler()
@@ -25,7 +30,7 @@ class ReactDelegateWrapper(
2530
hardwareBackHandler = backHandler
2631
}
2732

28-
override fun onHostResume() {
29-
reactHost.onHostResume(activity, backBtnHandler)
33+
fun onReactHostResume() {
34+
super.reactHost?.onHostResume(activity, backBtnHandler)
3035
}
3136
}
Lines changed: 116 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,25 @@
11
package com.callstack.reactnativebrownfield
22

33
import android.app.Application
4-
import android.content.Context
54
import android.os.Bundle
65
import android.widget.FrameLayout
76
import androidx.activity.OnBackPressedCallback
87
import androidx.fragment.app.FragmentActivity
98
import androidx.lifecycle.DefaultLifecycleObserver
109
import androidx.lifecycle.LifecycleOwner
1110
import com.callstack.reactnativebrownfield.utils.VersionUtils
11+
import com.facebook.react.ReactHost
1212
import com.facebook.react.ReactInstanceEventListener
13-
import com.facebook.react.ReactInstanceManager
14-
import com.facebook.react.ReactNativeHost
1513
import com.facebook.react.ReactPackage
16-
import com.facebook.react.ReactRootView
1714
import com.facebook.react.bridge.ReactContext
18-
import com.facebook.react.defaults.DefaultReactNativeHost
15+
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
16+
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
1917
import com.facebook.react.soloader.OpenSourceMergedSoMapping
2018
import com.facebook.soloader.SoLoader
2119
import java.util.concurrent.atomic.AtomicBoolean
22-
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
23-
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
2420

2521
fun interface OnJSBundleLoaded {
26-
operator fun invoke(initialized: Boolean)
22+
operator fun invoke(initialized: Boolean)
2723
}
2824

2925
/**
@@ -33,154 +29,136 @@ fun interface OnJSBundleLoaded {
3329
*/
3430
private const val RN_THRESHOLD_VERSION = "0.80.0"
3531

36-
class ReactNativeBrownfield private constructor(val reactNativeHost: ReactNativeHost) {
37-
companion object {
38-
private lateinit var instance: ReactNativeBrownfield
39-
private val initialized = AtomicBoolean()
32+
class ReactNativeBrownfield private constructor(val reactHost: ReactHost) {
33+
companion object {
34+
private lateinit var instance: ReactNativeBrownfield
35+
private val initialized = AtomicBoolean()
4036

41-
@JvmStatic
42-
val shared: ReactNativeBrownfield get() = instance
37+
@JvmStatic
38+
val shared: ReactNativeBrownfield get() = instance
4339

44-
private fun loadNativeLibs (application: Application) {
45-
val rnVersion = BuildConfig.RN_VERSION
40+
private fun loadNativeLibs(application: Application) {
41+
val rnVersion = BuildConfig.RN_VERSION
4642

47-
if (VersionUtils.isVersionLessThan(rnVersion, RN_THRESHOLD_VERSION)) {
48-
SoLoader.init(application.applicationContext, OpenSourceMergedSoMapping)
49-
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
50-
// If you opted-in for the New Architecture, we load the native entry point for this app.
51-
load()
43+
if (VersionUtils.isVersionLessThan(rnVersion, RN_THRESHOLD_VERSION)) {
44+
SoLoader.init(application.applicationContext, OpenSourceMergedSoMapping)
45+
load()
46+
}
5247
}
53-
}
54-
}
5548

56-
@JvmStatic
57-
@JvmOverloads
58-
fun initialize(application: Application, rnHost: ReactNativeHost, onJSBundleLoaded: OnJSBundleLoaded? = null) {
59-
if (!initialized.getAndSet(true)) {
60-
loadNativeLibs(application)
61-
instance = ReactNativeBrownfield(rnHost)
49+
@JvmStatic
50+
@JvmOverloads
51+
fun initialize(
52+
application: Application,
53+
reactHost: ReactHost,
54+
onJSBundleLoaded: OnJSBundleLoaded? = null
55+
) {
56+
if (!initialized.getAndSet(true)) {
57+
loadNativeLibs(application)
58+
instance = ReactNativeBrownfield(reactHost)
59+
60+
preloadReactNative {
61+
onJSBundleLoaded?.invoke(true)
62+
}
63+
}
64+
}
6265

63-
preloadReactNative {
64-
onJSBundleLoaded?.invoke(true)
66+
@JvmStatic
67+
@JvmOverloads
68+
fun initialize(
69+
application: Application,
70+
options: HashMap<String, Any>,
71+
onJSBundleLoaded: OnJSBundleLoaded? = null
72+
) {
73+
val reactHost: ReactHost by lazy {
74+
getDefaultReactHost(
75+
context = application,
76+
packageList = (options["packages"] as? List<*> ?: emptyList<ReactPackage>())
77+
.filterIsInstance<ReactPackage>(),
78+
jsRuntimeFactory = null
79+
)
80+
}
81+
82+
initialize(application, reactHost, onJSBundleLoaded)
6583
}
66-
}
67-
}
6884

69-
@JvmStatic
70-
@JvmOverloads
71-
fun initialize(application: Application, options: HashMap<String, Any>, onJSBundleLoaded: OnJSBundleLoaded? = null) {
72-
val reactNativeHost: ReactNativeHost =
73-
object : DefaultReactNativeHost(application) {
85+
@JvmStatic
86+
@JvmOverloads
87+
fun initialize(
88+
application: Application,
89+
packages: List<ReactPackage>,
90+
onJSBundleLoaded: OnJSBundleLoaded? = null
91+
) {
92+
val options = hashMapOf("packages" to packages, "mainModuleName" to "index")
7493

75-
override fun getJSMainModuleName(): String {
76-
return options["mainModuleName"] as? String ?: super.getJSMainModuleName()
77-
}
94+
initialize(application, options, onJSBundleLoaded)
95+
}
7896

79-
override fun getPackages(): List<ReactPackage> {
80-
return (options["packages"] as? List<*> ?: emptyList<ReactPackage>())
81-
.filterIsInstance<ReactPackage>()
82-
}
97+
private fun preloadReactNative(callback: ((Boolean) -> Unit)) {
98+
shared.reactHost.addReactInstanceEventListener(object :
99+
ReactInstanceEventListener {
100+
override fun onReactContextInitialized(context: ReactContext) {
101+
callback(true)
102+
shared.reactHost.removeReactInstanceEventListener(this)
103+
}
104+
})
105+
shared.reactHost.start()
106+
}
107+
}
83108

84-
override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
109+
fun createView(
110+
activity: FragmentActivity?,
111+
moduleName: String,
112+
reactDelegate: ReactDelegateWrapper? = null,
113+
launchOptions: Bundle? = null,
114+
): FrameLayout {
115+
val reactHost = shared.reactHost
116+
val resolvedDelegate =
117+
reactDelegate ?: ReactDelegateWrapper(activity, reactHost, moduleName, launchOptions)
118+
119+
val mBackPressedCallback: OnBackPressedCallback = object : OnBackPressedCallback(true) {
120+
override fun handleOnBackPressed() {
121+
// invoked for JS stack back navigation
122+
resolvedDelegate.onBackPressed()
123+
}
124+
}
125+
126+
// Register back press callback
127+
activity?.onBackPressedDispatcher?.addCallback(mBackPressedCallback)
128+
// invoked on the last RN screen exit
129+
resolvedDelegate.setHardwareBackHandler {
130+
mBackPressedCallback.isEnabled = false
131+
activity?.onBackPressedDispatcher?.onBackPressed()
132+
}
85133

86-
override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
87-
override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
134+
/**
135+
* When createView method is called in ReactNativeFragment, a reactDelegate
136+
* instance is required. In such a case, we use the lifeCycle events of the fragment.
137+
* When createView method is called elsewhere, then reactDelegate is not required.
138+
* In such a case, we set the lifeCycle observer.
139+
*/
140+
if (reactDelegate == null) {
141+
activity?.lifecycle?.addObserver(getLifeCycleObserver(resolvedDelegate))
88142
}
89143

90-
initialize(application, reactNativeHost, onJSBundleLoaded)
144+
resolvedDelegate.loadApp()
145+
return resolvedDelegate.reactRootView!!
91146
}
92147

93-
@JvmStatic
94-
@JvmOverloads
95-
fun initialize(application: Application, packages: List<ReactPackage>, onJSBundleLoaded: OnJSBundleLoaded? = null) {
96-
val options = hashMapOf("packages" to packages, "mainModuleName" to "index")
148+
private fun getLifeCycleObserver(reactDelegate: ReactDelegateWrapper): DefaultLifecycleObserver {
149+
return object : DefaultLifecycleObserver {
150+
override fun onResume(owner: LifecycleOwner) {
151+
reactDelegate.onReactHostResume()
152+
}
97153

98-
initialize(application, options, onJSBundleLoaded)
99-
}
154+
override fun onPause(owner: LifecycleOwner) {
155+
reactDelegate.onHostPause()
156+
}
100157

101-
private fun preloadReactNative(callback: ((Boolean) -> Unit)) {
102-
val reactInstanceManager = shared.reactNativeHost.reactInstanceManager
103-
reactInstanceManager.addReactInstanceEventListener(object :
104-
ReactInstanceEventListener {
105-
override fun onReactContextInitialized(reactContext: ReactContext) {
106-
callback(true)
107-
reactInstanceManager.removeReactInstanceEventListener(this)
158+
override fun onDestroy(owner: LifecycleOwner) {
159+
reactDelegate.onHostDestroy()
160+
owner.lifecycle.removeObserver(this) // Cleanup to avoid leaks
161+
}
108162
}
109-
})
110-
reactInstanceManager?.createReactContextInBackground()
111163
}
112-
}
113-
114-
fun createView(
115-
context: Context,
116-
activity: FragmentActivity?,
117-
moduleName: String,
118-
reactDelegate: ReactDelegateWrapper? = null,
119-
launchOptions: Bundle? = null,
120-
): FrameLayout {
121-
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
122-
val reactHost = getDefaultReactHost(
123-
context,
124-
shared.reactNativeHost
125-
)
126-
127-
val resolvedDelegate = reactDelegate ?: ReactDelegateWrapper(activity, reactHost, moduleName, launchOptions)
128-
129-
val mBackPressedCallback: OnBackPressedCallback = object : OnBackPressedCallback(true) {
130-
override fun handleOnBackPressed() {
131-
// invoked for JS stack back navigation
132-
resolvedDelegate.onBackPressed()
133-
}
134-
}
135-
136-
// Register back press callback
137-
activity?.onBackPressedDispatcher?.addCallback(mBackPressedCallback)
138-
// invoked on the last RN screen exit
139-
resolvedDelegate.setHardwareBackHandler {
140-
mBackPressedCallback.isEnabled = false
141-
activity?.onBackPressedDispatcher?.onBackPressed()
142-
}
143-
144-
/**
145-
* When createView method is called in ReactNativeFragment, a reactDelegate
146-
* instance is required. In such a case, we use the lifeCycle events of the fragment.
147-
* When createView method is called elsewhere, then reactDelegate is not required.
148-
* In such a case, we set the lifeCycle observer.
149-
*/
150-
if (reactDelegate == null) {
151-
activity?.lifecycle?.addObserver(getLifeCycleObserver(resolvedDelegate))
152-
}
153-
154-
resolvedDelegate.loadApp()
155-
return resolvedDelegate.reactRootView!!
156-
}
157-
158-
val instanceManager: ReactInstanceManager? = shared.reactNativeHost?.reactInstanceManager
159-
val reactView = ReactRootView(context)
160-
reactView.startReactApplication(
161-
instanceManager,
162-
moduleName,
163-
launchOptions,
164-
)
165-
166-
return reactView
167-
}
168-
169-
private fun getLifeCycleObserver(reactDelegate: ReactDelegateWrapper): DefaultLifecycleObserver {
170-
return object : DefaultLifecycleObserver {
171-
override fun onResume(owner: LifecycleOwner) {
172-
reactDelegate.onHostResume()
173-
}
174-
175-
override fun onPause(owner: LifecycleOwner) {
176-
reactDelegate.onHostPause()
177-
}
178-
179-
override fun onDestroy(owner: LifecycleOwner) {
180-
reactDelegate.onHostDestroy()
181-
owner.lifecycle.removeObserver(this) // Cleanup to avoid leaks
182-
}
183-
}
184-
}
185164
}
186-
Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
11
package com.callstack.reactnativebrownfield
22

33
import android.view.View
4-
import java.util.Collections
5-
64
import com.facebook.react.ReactPackage
75
import com.facebook.react.bridge.NativeModule
86
import com.facebook.react.bridge.ReactApplicationContext
9-
import com.facebook.react.uimanager.ViewManager
107
import com.facebook.react.uimanager.ReactShadowNode
8+
import com.facebook.react.uimanager.ViewManager
9+
import java.util.Collections
1110

1211

1312
class ReactNativeBrownfieldPackage : ReactPackage {
14-
override fun createViewManagers(reactContext: ReactApplicationContext): MutableList<ViewManager<View, ReactShadowNode<*>>> {
15-
return Collections.emptyList()
16-
}
13+
override fun createViewManagers(reactContext: ReactApplicationContext): MutableList<ViewManager<View, ReactShadowNode<*>>> {
14+
return Collections.emptyList()
15+
}
1716

18-
override fun createNativeModules(reactContext: ReactApplicationContext): MutableList<NativeModule> {
19-
val modules = ArrayList<NativeModule>()
20-
modules.add(ReactNativeBrownfieldModule(reactContext))
21-
return modules
22-
}
17+
override fun createNativeModules(reactContext: ReactApplicationContext): MutableList<NativeModule> {
18+
val modules = ArrayList<NativeModule>()
19+
modules.add(ReactNativeBrownfieldModule(reactContext))
20+
return modules
21+
}
2322
}

0 commit comments

Comments
 (0)