1
1
package com .microsoft .codepush .react ;
2
2
3
3
import android .app .Activity ;
4
- import android .content .Context ;
5
4
import android .os .AsyncTask ;
6
5
import android .os .Handler ;
7
6
import android .os .Looper ;
8
7
import android .provider .Settings ;
9
- import android .view .Choreographer ;
10
8
11
- import com .facebook .react .ReactActivity ;
9
+ import com .facebook .react .ReactApplication ;
12
10
import com .facebook .react .ReactInstanceManager ;
13
11
import com .facebook .react .bridge .Arguments ;
14
12
import com .facebook .react .bridge .LifecycleEventListener ;
18
16
import com .facebook .react .bridge .ReactMethod ;
19
17
import com .facebook .react .bridge .ReadableMap ;
20
18
import com .facebook .react .bridge .WritableMap ;
21
- import com .facebook .react .modules .core .DeviceEventManagerModule ;
22
19
import com .facebook .react .modules .core .ChoreographerCompat ;
20
+ import com .facebook .react .modules .core .DeviceEventManagerModule ;
23
21
import com .facebook .react .modules .core .ReactChoreographer ;
24
22
25
23
import org .json .JSONArray ;
@@ -44,9 +42,6 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
44
42
private CodePushTelemetryManager mTelemetryManager ;
45
43
private CodePushUpdateManager mUpdateManager ;
46
44
47
- private static final String REACT_APPLICATION_CLASS_NAME = "com.facebook.react.ReactApplication" ;
48
- private static final String REACT_NATIVE_HOST_CLASS_NAME = "com.facebook.react.ReactNativeHost" ;
49
-
50
45
public CodePushNativeModule (ReactApplicationContext reactContext , CodePush codePush , CodePushUpdateManager codePushUpdateManager , CodePushTelemetryManager codePushTelemetryManager , SettingsManager settingsManager ) {
51
46
super (reactContext );
52
47
@@ -100,7 +95,7 @@ public void run() {
100
95
101
96
// Use reflection to find and set the appropriate fields on ReactInstanceManager. See #556 for a proposal for a less brittle way
102
97
// to approach this.
103
- private void setJSBundle (ReactInstanceManager instanceManager , String latestJSBundleFile ) throws NoSuchFieldException , IllegalAccessException {
98
+ private void setJSBundle (ReactInstanceManager instanceManager , String latestJSBundleFile ) throws IllegalAccessException {
104
99
try {
105
100
Field bundleLoaderField = instanceManager .getClass ().getDeclaredField ("mBundleLoader" );
106
101
Class <?> jsBundleLoaderClass = Class .forName ("com.facebook.react.cxxbridge.JSBundleLoader" );
@@ -109,7 +104,7 @@ private void setJSBundle(ReactInstanceManager instanceManager, String latestJSBu
109
104
? "createAssetLoader" : "createFileLoader" ;
110
105
111
106
Method [] methods = jsBundleLoaderClass .getDeclaredMethods ();
112
- for (Method method : methods ) {
107
+ for (Method method : methods ) {
113
108
if (method .getName ().equals (createFileLoaderMethodName )) {
114
109
createFileLoaderMethod = method ;
115
110
break ;
@@ -127,7 +122,7 @@ private void setJSBundle(ReactInstanceManager instanceManager, String latestJSBu
127
122
// RN >= v0.34
128
123
latestJSBundleLoader = createFileLoaderMethod .invoke (jsBundleLoaderClass , latestJSBundleFile );
129
124
} else if (numParameters == 2 ) {
130
- // RN >= v0.31 && RN < v0.34 or AssetLoader instance
125
+ // AssetLoader instance
131
126
latestJSBundleLoader = createFileLoaderMethod .invoke (jsBundleLoaderClass , getReactApplicationContext (), latestJSBundleFile );
132
127
} else {
133
128
throw new NoSuchMethodException ("Could not find a recognized 'createFileLoader' method" );
@@ -136,14 +131,13 @@ private void setJSBundle(ReactInstanceManager instanceManager, String latestJSBu
136
131
bundleLoaderField .setAccessible (true );
137
132
bundleLoaderField .set (instanceManager , latestJSBundleLoader );
138
133
} catch (Exception e ) {
139
- // RN < v0.31
140
- Field jsBundleField = instanceManager .getClass ().getDeclaredField ("mJSBundleFile" );
141
- jsBundleField .setAccessible (true );
142
- jsBundleField .set (instanceManager , latestJSBundleFile );
134
+ CodePushUtils .log ("Unable to set JSBundle - CodePush may not support this version of React Native" );
135
+ throw new IllegalAccessException ("Could not setJSBundle" );
143
136
}
144
137
}
145
138
146
139
private void loadBundle () {
140
+ clearLifecycleEventListener ();
147
141
mCodePush .clearDebugCacheIfNeeded ();
148
142
try {
149
143
// #1) Get the ReactInstanceManager instance, which is what includes the
@@ -159,12 +153,11 @@ private void loadBundle() {
159
153
setJSBundle (instanceManager , latestJSBundleFile );
160
154
161
155
// #3) Get the context creation method and fire it on the UI thread (which RN enforces)
162
- final Method recreateMethod = instanceManager .getClass ().getMethod ("recreateReactContextInBackground" );
163
156
new Handler (Looper .getMainLooper ()).post (new Runnable () {
164
157
@ Override
165
158
public void run () {
166
159
try {
167
- recreateMethod . invoke ( instanceManager );
160
+ instanceManager . recreateReactContextInBackground ( );
168
161
mCodePush .initializeUpdateAfterRestart ();
169
162
} catch (Exception e ) {
170
163
// The recreation method threw an unknown exception
@@ -181,6 +174,14 @@ public void run() {
181
174
}
182
175
}
183
176
177
+ private void clearLifecycleEventListener () {
178
+ // Remove LifecycleEventListener to prevent infinite restart loop
179
+ if (mLifecycleEventListener != null ) {
180
+ getReactApplicationContext ().removeLifecycleEventListener (mLifecycleEventListener );
181
+ mLifecycleEventListener = null ;
182
+ }
183
+ }
184
+
184
185
// Use reflection to find the ReactInstanceManager. See #556 for a proposal for a less brittle way to approach this.
185
186
private ReactInstanceManager resolveInstanceManager () throws NoSuchFieldException , IllegalAccessException {
186
187
ReactInstanceManager instanceManager = CodePush .getReactInstanceManager ();
@@ -192,37 +193,11 @@ private ReactInstanceManager resolveInstanceManager() throws NoSuchFieldExceptio
192
193
if (currentActivity == null ) {
193
194
return null ;
194
195
}
195
- try {
196
- // In RN >=0.29, the "mReactInstanceManager" field yields a null value, so we try
197
- // to get the instance manager via the ReactNativeHost, which only exists in 0.29.
198
- Method getApplicationMethod = ReactActivity .class .getMethod ("getApplication" );
199
- Object reactApplication = getApplicationMethod .invoke (currentActivity );
200
- Class <?> reactApplicationClass = tryGetClass (REACT_APPLICATION_CLASS_NAME );
201
- Method getReactNativeHostMethod = reactApplicationClass .getMethod ("getReactNativeHost" );
202
- Object reactNativeHost = getReactNativeHostMethod .invoke (reactApplication );
203
- Class <?> reactNativeHostClass = tryGetClass (REACT_NATIVE_HOST_CLASS_NAME );
204
- Method getReactInstanceManagerMethod = reactNativeHostClass .getMethod ("getReactInstanceManager" );
205
- instanceManager = (ReactInstanceManager )getReactInstanceManagerMethod .invoke (reactNativeHost );
206
- } catch (Exception e ) {
207
- // The React Native version might be older than 0.29, or the activity does not
208
- // extend ReactActivity, so we try to get the instance manager via the
209
- // "mReactInstanceManager" field.
210
- Class instanceManagerHolderClass = currentActivity instanceof ReactActivity
211
- ? ReactActivity .class
212
- : currentActivity .getClass ();
213
- Field instanceManagerField = instanceManagerHolderClass .getDeclaredField ("mReactInstanceManager" );
214
- instanceManagerField .setAccessible (true );
215
- instanceManager = (ReactInstanceManager )instanceManagerField .get (currentActivity );
216
- }
217
- return instanceManager ;
218
- }
219
196
220
- private Class tryGetClass (String className ) {
221
- try {
222
- return Class .forName (className );
223
- } catch (ClassNotFoundException e ) {
224
- return null ;
225
- }
197
+ ReactApplication reactApplication = (ReactApplication ) currentActivity .getApplication ();
198
+ instanceManager = reactApplication .getReactNativeHost ().getReactInstanceManager ();
199
+
200
+ return instanceManager ;
226
201
}
227
202
228
203
@ ReactMethod
0 commit comments