11package com .microsoft .codepush .react ;
22
33import android .app .Activity ;
4- import android .content .Context ;
54import android .os .AsyncTask ;
65import android .os .Handler ;
76import android .os .Looper ;
87import android .provider .Settings ;
9- import android .view .Choreographer ;
108
11- import com .facebook .react .ReactActivity ;
9+ import com .facebook .react .ReactApplication ;
1210import com .facebook .react .ReactInstanceManager ;
1311import com .facebook .react .bridge .Arguments ;
1412import com .facebook .react .bridge .LifecycleEventListener ;
1816import com .facebook .react .bridge .ReactMethod ;
1917import com .facebook .react .bridge .ReadableMap ;
2018import com .facebook .react .bridge .WritableMap ;
21- import com .facebook .react .modules .core .DeviceEventManagerModule ;
2219import com .facebook .react .modules .core .ChoreographerCompat ;
20+ import com .facebook .react .modules .core .DeviceEventManagerModule ;
2321import com .facebook .react .modules .core .ReactChoreographer ;
2422
2523import org .json .JSONArray ;
@@ -44,9 +42,6 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
4442 private CodePushTelemetryManager mTelemetryManager ;
4543 private CodePushUpdateManager mUpdateManager ;
4644
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-
5045 public CodePushNativeModule (ReactApplicationContext reactContext , CodePush codePush , CodePushUpdateManager codePushUpdateManager , CodePushTelemetryManager codePushTelemetryManager , SettingsManager settingsManager ) {
5146 super (reactContext );
5247
@@ -100,7 +95,7 @@ public void run() {
10095
10196 // Use reflection to find and set the appropriate fields on ReactInstanceManager. See #556 for a proposal for a less brittle way
10297 // to approach this.
103- private void setJSBundle (ReactInstanceManager instanceManager , String latestJSBundleFile ) throws NoSuchFieldException , IllegalAccessException {
98+ private void setJSBundle (ReactInstanceManager instanceManager , String latestJSBundleFile ) throws IllegalAccessException {
10499 try {
105100 Field bundleLoaderField = instanceManager .getClass ().getDeclaredField ("mBundleLoader" );
106101 Class <?> jsBundleLoaderClass = Class .forName ("com.facebook.react.cxxbridge.JSBundleLoader" );
@@ -109,7 +104,7 @@ private void setJSBundle(ReactInstanceManager instanceManager, String latestJSBu
109104 ? "createAssetLoader" : "createFileLoader" ;
110105
111106 Method [] methods = jsBundleLoaderClass .getDeclaredMethods ();
112- for (Method method : methods ) {
107+ for (Method method : methods ) {
113108 if (method .getName ().equals (createFileLoaderMethodName )) {
114109 createFileLoaderMethod = method ;
115110 break ;
@@ -127,7 +122,7 @@ private void setJSBundle(ReactInstanceManager instanceManager, String latestJSBu
127122 // RN >= v0.34
128123 latestJSBundleLoader = createFileLoaderMethod .invoke (jsBundleLoaderClass , latestJSBundleFile );
129124 } else if (numParameters == 2 ) {
130- // RN >= v0.31 && RN < v0.34 or AssetLoader instance
125+ // AssetLoader instance
131126 latestJSBundleLoader = createFileLoaderMethod .invoke (jsBundleLoaderClass , getReactApplicationContext (), latestJSBundleFile );
132127 } else {
133128 throw new NoSuchMethodException ("Could not find a recognized 'createFileLoader' method" );
@@ -136,14 +131,13 @@ private void setJSBundle(ReactInstanceManager instanceManager, String latestJSBu
136131 bundleLoaderField .setAccessible (true );
137132 bundleLoaderField .set (instanceManager , latestJSBundleLoader );
138133 } 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" );
143136 }
144137 }
145138
146139 private void loadBundle () {
140+ clearLifecycleEventListener ();
147141 mCodePush .clearDebugCacheIfNeeded ();
148142 try {
149143 // #1) Get the ReactInstanceManager instance, which is what includes the
@@ -159,12 +153,11 @@ private void loadBundle() {
159153 setJSBundle (instanceManager , latestJSBundleFile );
160154
161155 // #3) Get the context creation method and fire it on the UI thread (which RN enforces)
162- final Method recreateMethod = instanceManager .getClass ().getMethod ("recreateReactContextInBackground" );
163156 new Handler (Looper .getMainLooper ()).post (new Runnable () {
164157 @ Override
165158 public void run () {
166159 try {
167- recreateMethod . invoke ( instanceManager );
160+ instanceManager . recreateReactContextInBackground ( );
168161 mCodePush .initializeUpdateAfterRestart ();
169162 } catch (Exception e ) {
170163 // The recreation method threw an unknown exception
@@ -181,6 +174,14 @@ public void run() {
181174 }
182175 }
183176
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+
184185 // Use reflection to find the ReactInstanceManager. See #556 for a proposal for a less brittle way to approach this.
185186 private ReactInstanceManager resolveInstanceManager () throws NoSuchFieldException , IllegalAccessException {
186187 ReactInstanceManager instanceManager = CodePush .getReactInstanceManager ();
@@ -192,37 +193,11 @@ private ReactInstanceManager resolveInstanceManager() throws NoSuchFieldExceptio
192193 if (currentActivity == null ) {
193194 return null ;
194195 }
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- }
219196
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 ;
226201 }
227202
228203 @ ReactMethod
0 commit comments