1
1
package com .microsoft .codepush .react ;
2
2
3
3
import com .facebook .react .ReactPackage ;
4
- import com .facebook .react .bridge .ActivityEventListener ;
5
4
import com .facebook .react .bridge .JavaScriptModule ;
6
5
import com .facebook .react .bridge .LifecycleEventListener ;
7
6
import com .facebook .react .bridge .NativeModule ;
31
30
import java .io .File ;
32
31
import java .io .IOException ;
33
32
import java .util .ArrayList ;
34
- import java .util .Calendar ;
35
- import java .util .Date ;
36
33
import java .util .HashMap ;
37
34
import java .util .List ;
38
35
import java .util .Map ;
39
- import java .util .Timer ;
40
- import java .util .TimerTask ;
41
36
import java .util .zip .ZipEntry ;
42
37
import java .util .zip .ZipFile ;
43
38
44
39
public class CodePush {
45
40
46
41
private boolean didUpdate = false ;
47
- private Timer timer ;
48
42
private boolean usingTestFolder = false ;
49
43
50
44
private String assetsBundleFileName ;
51
45
52
46
private final String FAILED_UPDATES_KEY = "CODE_PUSH_FAILED_UPDATES" ;
53
47
private final String PENDING_UPDATE_KEY = "CODE_PUSH_PENDING_UPDATE" ;
54
48
private final String PENDING_UPDATE_HASH_KEY = "hash" ;
55
- private final String PENDING_UPDATE_ROLLBACK_TIMEOUT_KEY = "rollbackTimeout " ;
49
+ private final String PENDING_UPDATE_WAS_INITIALIZED_KEY = "wasInitialized " ;
56
50
private final String ASSETS_BUNDLE_PREFIX = "assets://" ;
57
51
private final String CODE_PUSH_PREFERENCES = "CodePush" ;
58
52
private final String DOWNLOAD_PROGRESS_EVENT_NAME = "CodePushDownloadProgress" ;
@@ -98,10 +92,6 @@ public ReactPackage getReactPackage() {
98
92
return codePushReactPackage ;
99
93
}
100
94
101
- public String getDocumentsDirectory () {
102
- return codePushPackage .getDocumentsDirectory ();
103
- }
104
-
105
95
public String getBundleUrl (String assetsBundleFileName ) {
106
96
this .assetsBundleFileName = assetsBundleFileName ;
107
97
String binaryJsBundleUrl = ASSETS_BUNDLE_PREFIX + assetsBundleFileName ;
@@ -138,13 +128,6 @@ public String getBundleUrl(String assetsBundleFileName) {
138
128
}
139
129
}
140
130
141
- private void cancelRollbackTimer () {
142
- if (timer != null ) {
143
- timer .cancel ();
144
- timer = null ;
145
- }
146
- }
147
-
148
131
private boolean isFailedHash (String packageHash ) {
149
132
SharedPreferences settings = applicationContext .getSharedPreferences (CODE_PUSH_PREFERENCES , 0 );
150
133
String failedUpdatesString = settings .getString (FAILED_UPDATES_KEY , null );
@@ -176,7 +159,10 @@ private void rollbackPackage() {
176
159
throw new CodePushUnknownException ("Error in rolling back package" , e );
177
160
}
178
161
179
- codePushNativeModule .loadBundle ();
162
+ removePendingUpdate ();
163
+ if (codePushNativeModule != null ) {
164
+ codePushNativeModule .loadBundle ();
165
+ }
180
166
}
181
167
182
168
private void saveFailedUpdate (String packageHash ) {
@@ -204,68 +190,85 @@ private void saveFailedUpdate(String packageHash) {
204
190
}
205
191
}
206
192
207
- private void savePendingUpdate (String packageHash , int rollbackTimeout ) {
193
+ private void removePendingUpdate () {
194
+ SharedPreferences settings = applicationContext .getSharedPreferences (CODE_PUSH_PREFERENCES , 0 );
195
+ settings .edit ().remove (PENDING_UPDATE_KEY ).commit ();
196
+ }
197
+
198
+ private void savePendingUpdate (String packageHash , boolean wasInitialized ) {
208
199
SharedPreferences settings = applicationContext .getSharedPreferences (CODE_PUSH_PREFERENCES , 0 );
209
200
JSONObject pendingUpdate = new JSONObject ();
210
201
try {
211
202
pendingUpdate .put (PENDING_UPDATE_HASH_KEY , packageHash );
212
- pendingUpdate .put (PENDING_UPDATE_ROLLBACK_TIMEOUT_KEY , rollbackTimeout );
203
+ pendingUpdate .put (PENDING_UPDATE_WAS_INITIALIZED_KEY , wasInitialized );
213
204
settings .edit ().putString (PENDING_UPDATE_KEY , pendingUpdate .toString ()).commit ();
214
205
} catch (JSONException e ) {
215
206
// Should not happen.
216
207
throw new CodePushUnknownException ("Unable to save pending update." , e );
217
208
}
218
-
219
- }
220
-
221
- private void startRollbackTimer (int rollbackTimeout ) {
222
- timer = new Timer ();
223
- Calendar c = Calendar .getInstance ();
224
- c .setTime (new Date ());
225
- c .add (Calendar .MILLISECOND , rollbackTimeout );
226
- Date timeout = c .getTime ();
227
- timer .schedule (new TimerTask () {
228
- @ Override
229
- public void run () {
230
- rollbackPackage ();
231
- }
232
- }, timeout );
233
209
}
234
210
235
- private void initializeUpdateAfterRestart () {
211
+ private JSONObject getPendingUpdate () {
236
212
SharedPreferences settings = applicationContext .getSharedPreferences (CODE_PUSH_PREFERENCES , 0 );
237
213
String pendingUpdateString = settings .getString (PENDING_UPDATE_KEY , null );
214
+ if (pendingUpdateString == null ) {
215
+ return null ;
216
+ }
238
217
239
- if (pendingUpdateString != null ) {
218
+ try {
219
+ JSONObject pendingUpdate = new JSONObject (pendingUpdateString );
220
+ return pendingUpdate ;
221
+ } catch (JSONException e ) {
222
+ // Should not happen.
223
+ throw new CodePushUnknownException ("Unable to parse pending update metadata " +
224
+ pendingUpdateString + " stored in SharedPreferences" , e );
225
+ }
226
+ }
227
+
228
+ private void initializeUpdateAfterRestart () {
229
+ JSONObject pendingUpdate = getPendingUpdate ();
230
+ if (pendingUpdate != null ) {
231
+ didUpdate = true ;
240
232
try {
241
- didUpdate = true ;
242
- JSONObject pendingUpdateJSON = new JSONObject (pendingUpdateString );
243
- int rollbackTimeout = pendingUpdateJSON .getInt (PENDING_UPDATE_ROLLBACK_TIMEOUT_KEY );
244
- if (0 != rollbackTimeout ) {
245
- startRollbackTimer (rollbackTimeout );
233
+ boolean wasInitialized = pendingUpdate .getBoolean (PENDING_UPDATE_WAS_INITIALIZED_KEY );
234
+ if (wasInitialized ) {
235
+ // Pending update was initialized, but notifiyApplicationReady was not called.
236
+ // Therefore, deduce that it is a broken update and rollback.
237
+ rollbackPackage ();
238
+ } else {
239
+ // Mark that we tried to initiazlie the new update, so that if it crashes,
240
+ // we will know that we need to rollback when the app next starts.
241
+ savePendingUpdate (pendingUpdate .getString (PENDING_UPDATE_HASH_KEY ),
242
+ /* wasInitialized */ true );
246
243
}
247
-
248
- settings .edit ().remove (PENDING_UPDATE_KEY ).commit ();
249
244
} catch (JSONException e ) {
250
245
// Should not happen.
251
- throw new CodePushUnknownException ("Unable to parse pending update metadata " +
252
- pendingUpdateString + " stored in SharedPreferences" , e );
246
+ throw new CodePushUnknownException ("Unable to read pending update metadata stored in SharedPreferences" , e );
253
247
}
254
248
}
255
249
}
256
250
257
251
private class CodePushNativeModule extends ReactContextBaseJavaModule {
258
252
259
253
private LifecycleEventListener lifecycleEventListener = null ;
254
+ private static final String JS_BUNDLE_FILE_NAME = "ReactNativeDevBundle.js" ;
255
+
256
+ private void clearReactDevBundleCache () {
257
+ File cachedDevBundle = new File (getReactApplicationContext ().getFilesDir (), JS_BUNDLE_FILE_NAME );
258
+ if (cachedDevBundle .exists ()) {
259
+ cachedDevBundle .delete ();
260
+ }
261
+ }
260
262
261
263
private void loadBundle () {
264
+ clearReactDevBundleCache ();
262
265
Intent intent = mainActivity .getIntent ();
263
266
mainActivity .finish ();
264
267
mainActivity .startActivity (intent );
265
268
}
266
269
267
270
@ ReactMethod
268
- public void installUpdate (final ReadableMap updatePackage , final int rollbackTimeout , final int installMode , final Promise promise ) {
271
+ public void installUpdate (final ReadableMap updatePackage , final int installMode , final Promise promise ) {
269
272
AsyncTask asyncTask = new AsyncTask () {
270
273
@ Override
271
274
protected Void doInBackground (Object [] params ) {
@@ -276,7 +279,7 @@ protected Void doInBackground(Object[] params) {
276
279
if (pendingHash == null ) {
277
280
throw new CodePushUnknownException ("Update package to be installed has no hash." );
278
281
} else {
279
- savePendingUpdate (pendingHash , rollbackTimeout );
282
+ savePendingUpdate (pendingHash , /* wasInitialized */ false );
280
283
}
281
284
282
285
if (installMode == CodePushInstallMode .IMMEDIATE .getValue ()) {
@@ -394,7 +397,7 @@ public void isFirstRun(String packageHash, Promise promise) {
394
397
395
398
@ ReactMethod
396
399
public void notifyApplicationReady (Promise promise ) {
397
- cancelRollbackTimer ();
400
+ removePendingUpdate ();
398
401
promise .resolve ("" );
399
402
}
400
403
0 commit comments