11package com.callstack.reactnativebrownfield
22
33import android.app.Application
4- import android.content.Context
54import android.os.Bundle
65import android.widget.FrameLayout
76import androidx.activity.OnBackPressedCallback
87import androidx.fragment.app.FragmentActivity
98import androidx.lifecycle.DefaultLifecycleObserver
109import androidx.lifecycle.LifecycleOwner
1110import com.callstack.reactnativebrownfield.utils.VersionUtils
11+ import com.facebook.react.ReactHost
1212import com.facebook.react.ReactInstanceEventListener
13- import com.facebook.react.ReactInstanceManager
14- import com.facebook.react.ReactNativeHost
1513import com.facebook.react.ReactPackage
16- import com.facebook.react.ReactRootView
1714import 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
1917import com.facebook.react.soloader.OpenSourceMergedSoMapping
2018import com.facebook.soloader.SoLoader
2119import java.util.concurrent.atomic.AtomicBoolean
22- import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
23- import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
2420
2521fun 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 */
3430private 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-
0 commit comments