5
5
import android .os .AsyncTask ;
6
6
import android .os .Handler ;
7
7
import android .os .Looper ;
8
- import android .provider .Settings ;
9
8
import android .view .View ;
10
9
10
+ import androidx .annotation .OptIn ;
11
+
11
12
import com .facebook .react .ReactApplication ;
13
+ import com .facebook .react .ReactDelegate ;
14
+ import com .facebook .react .ReactHost ;
12
15
import com .facebook .react .ReactInstanceManager ;
16
+ import com .facebook .react .ReactActivity ;
13
17
import com .facebook .react .ReactRootView ;
14
18
import com .facebook .react .bridge .Arguments ;
15
19
import com .facebook .react .bridge .JSBundleLoader ;
20
24
import com .facebook .react .bridge .ReactMethod ;
21
25
import com .facebook .react .bridge .ReadableMap ;
22
26
import com .facebook .react .bridge .WritableMap ;
27
+ import com .facebook .react .common .annotations .UnstableReactNativeAPI ;
23
28
import com .facebook .react .modules .core .ChoreographerCompat ;
24
29
import com .facebook .react .modules .core .DeviceEventManagerModule ;
25
30
import com .facebook .react .modules .core .ReactChoreographer ;
31
+ import com .facebook .react .runtime .ReactHostDelegate ;
26
32
27
33
import org .json .JSONArray ;
28
34
import org .json .JSONException ;
29
35
import org .json .JSONObject ;
30
36
31
37
import java .io .IOException ;
32
38
import java .lang .reflect .Field ;
39
+ import java .lang .reflect .Method ;
33
40
import java .util .ArrayList ;
34
41
import java .util .Date ;
35
42
import java .util .HashMap ;
36
- import java .util .List ;
37
43
import java .util .Map ;
38
44
import java .util .UUID ;
39
45
@@ -120,15 +126,38 @@ private void setJSBundle(ReactInstanceManager instanceManager, String latestJSBu
120
126
latestJSBundleLoader = JSBundleLoader .createFileLoader (latestJSBundleFile );
121
127
}
122
128
123
- Field bundleLoaderField = instanceManager .getClass ().getDeclaredField ("mBundleLoader" );
124
- bundleLoaderField .setAccessible (true );
125
- bundleLoaderField .set (instanceManager , latestJSBundleLoader );
129
+ ReactHost reactHost = resolveReactHost ();
130
+ if (reactHost == null ) {
131
+ // Bridge, Old Architecture and RN < 0.74 (we support Bridgeless >= 0.74)
132
+ setJSBundleLoaderBridge (instanceManager , latestJSBundleLoader );
133
+ return ;
134
+ }
135
+
136
+ // Bridgeless (RN >= 0.74)
137
+ setJSBundleLoaderBridgeless (reactHost , latestJSBundleLoader );
126
138
} catch (Exception e ) {
127
139
CodePushUtils .log ("Unable to set JSBundle - CodePush may not support this version of React Native" );
128
140
throw new IllegalAccessException ("Could not setJSBundle" );
129
141
}
130
142
}
131
143
144
+ private void setJSBundleLoaderBridge (ReactInstanceManager instanceManager , JSBundleLoader latestJSBundleLoader ) throws NoSuchFieldException , IllegalAccessException {
145
+ Field bundleLoaderField = instanceManager .getClass ().getDeclaredField ("mBundleLoader" );
146
+ bundleLoaderField .setAccessible (true );
147
+ bundleLoaderField .set (instanceManager , latestJSBundleLoader );
148
+ }
149
+
150
+ @ OptIn (markerClass = UnstableReactNativeAPI .class )
151
+ private void setJSBundleLoaderBridgeless (ReactHost reactHost , JSBundleLoader latestJSBundleLoader ) throws NoSuchFieldException , IllegalAccessException {
152
+ Field mReactHostDelegateField = reactHost .getClass ().getDeclaredField ("mReactHostDelegate" );
153
+ mReactHostDelegateField .setAccessible (true );
154
+ ReactHostDelegate reactHostDelegate = (ReactHostDelegate ) mReactHostDelegateField .get (reactHost );
155
+ assert reactHostDelegate != null ;
156
+ Field jsBundleLoaderField = reactHostDelegate .getClass ().getDeclaredField ("jsBundleLoader" );
157
+ jsBundleLoaderField .setAccessible (true );
158
+ jsBundleLoaderField .set (reactHostDelegate , latestJSBundleLoader );
159
+ }
160
+
132
161
private void loadBundle () {
133
162
clearLifecycleEventListener ();
134
163
try {
@@ -156,12 +185,22 @@ private void loadBundle() {
156
185
@ Override
157
186
public void run () {
158
187
try {
159
- // We don't need to resetReactRootViews anymore
160
- // due the issue https://github.com/facebook/react-native/issues/14533
161
- // has been fixed in RN 0.46.0
162
- //resetReactRootViews(instanceManager);
188
+ // reload method introduced in RN 0.74 (https://github.com/reactwg/react-native-new-architecture/discussions/174)
189
+ // so, we need to check if reload method exists and call it
190
+ try {
191
+ ReactDelegate reactDelegate = resolveReactDelegate ();
192
+ if (reactDelegate == null ) {
193
+ throw new NoSuchMethodException ("ReactDelegate doesn't have reload method in RN < 0.74" );
194
+ }
195
+
196
+ resetReactRootViews (reactDelegate );
163
197
164
- instanceManager .recreateReactContextInBackground ();
198
+ Method reloadMethod = reactDelegate .getClass ().getMethod ("reload" );
199
+ reloadMethod .invoke (reactDelegate );
200
+ } catch (NoSuchMethodException e ) {
201
+ // RN < 0.74 calls ReactInstanceManager.recreateReactContextInBackground() directly
202
+ instanceManager .recreateReactContextInBackground ();
203
+ }
165
204
mCodePush .initializeUpdateAfterRestart ();
166
205
} catch (Exception e ) {
167
206
// The recreation method threw an unknown exception
@@ -179,18 +218,19 @@ public void run() {
179
218
}
180
219
}
181
220
182
- // This workaround has been implemented in order to fix https://github.com/facebook/react-native/issues/14533
183
- // resetReactRootViews allows to call recreateReactContextInBackground without any exceptions
184
- // This fix also relates to https://github.com/microsoft/react-native-code-push/issues/878
185
- private void resetReactRootViews (ReactInstanceManager instanceManager ) throws NoSuchFieldException , IllegalAccessException {
186
- Field mAttachedRootViewsField = instanceManager .getClass ().getDeclaredField ("mAttachedRootViews" );
187
- mAttachedRootViewsField .setAccessible (true );
188
- List <ReactRootView > mAttachedRootViews = (List <ReactRootView >)mAttachedRootViewsField .get (instanceManager );
189
- for (ReactRootView reactRootView : mAttachedRootViews ) {
190
- reactRootView .removeAllViews ();
191
- reactRootView .setId (View .NO_ID );
221
+ // Fix freezing that occurs when reloading the app (RN >= 0.77.1 Old Architecture)
222
+ // - "Trying to add a root view with an explicit id (11) already set.
223
+ // React Native uses the id field to track react tags and will overwrite this field.
224
+ // If that is fine, explicitly overwrite the id field to View.NO_ID before calling addRootView."
225
+ private void resetReactRootViews (ReactDelegate reactDelegate ) {
226
+ ReactActivity currentActivity = (ReactActivity ) getCurrentActivity ();
227
+ if (currentActivity != null ) {
228
+ ReactRootView reactRootView = reactDelegate .getReactRootView ();
229
+ if (reactRootView != null ) {
230
+ reactRootView .removeAllViews ();
231
+ reactRootView .setId (View .NO_ID );
232
+ }
192
233
}
193
- mAttachedRootViewsField .set (instanceManager , mAttachedRootViews );
194
234
}
195
235
196
236
private void clearLifecycleEventListener () {
@@ -201,6 +241,36 @@ private void clearLifecycleEventListener() {
201
241
}
202
242
}
203
243
244
+ private ReactDelegate resolveReactDelegate () {
245
+ ReactActivity currentActivity = (ReactActivity ) getCurrentActivity ();
246
+ if (currentActivity == null ) {
247
+ return null ;
248
+ }
249
+
250
+ try {
251
+ Method getReactDelegateMethod = currentActivity .getClass ().getMethod ("getReactDelegate" );
252
+ return (ReactDelegate ) getReactDelegateMethod .invoke (currentActivity );
253
+ } catch (Exception e ) {
254
+ // RN < 0.74 doesn't have getReactDelegate method
255
+ return null ;
256
+ }
257
+ }
258
+
259
+ private ReactHost resolveReactHost () {
260
+ ReactDelegate reactDelegate = resolveReactDelegate ();
261
+ if (reactDelegate == null ) {
262
+ return null ;
263
+ }
264
+
265
+ try {
266
+ Field reactHostField = reactDelegate .getClass ().getDeclaredField ("mReactHost" );
267
+ reactHostField .setAccessible (true );
268
+ return (ReactHost ) reactHostField .get (reactDelegate );
269
+ } catch (Exception e ) {
270
+ return null ;
271
+ }
272
+ }
273
+
204
274
// Use reflection to find the ReactInstanceManager. See #556 for a proposal for a less brittle way to approach this.
205
275
private ReactInstanceManager resolveInstanceManager () throws NoSuchFieldException , IllegalAccessException {
206
276
ReactInstanceManager instanceManager = CodePush .getReactInstanceManager ();
@@ -492,7 +562,7 @@ protected Void doInBackground(Void... params) {
492
562
return null ;
493
563
}
494
564
}
495
-
565
+
496
566
promise .resolve ("" );
497
567
} catch (CodePushUnknownException e ) {
498
568
CodePushUtils .log (e );
0 commit comments