Skip to content

Commit 0d3d2d7

Browse files
authored
Move disallowRestart to native side (#1894)
Issue: Now disallowRestart feature works for InstallMode.IMMEDIATE only. #788 Solution: Move _allowed flag to the native side and use it for ON_NEXT_RESUME, ON_NEXT_SUSPEND, and IMMEDIATE installation mode.
1 parent 69fe187 commit 0d3d2d7

16 files changed

+555
-167
lines changed

CodePush.js

+7-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { AcquisitionManager as Sdk } from "code-push/script/acquisition-sdk";
22
import { Alert } from "./AlertAdapter";
33
import requestFetchAdapter from "./request-fetch-adapter";
44
import { AppState, Platform } from "react-native";
5-
import RestartManager from "./RestartManager";
65
import log from "./logging";
76
import hoistStatics from 'hoist-non-react-statics';
87

@@ -300,6 +299,10 @@ function setUpTestDependencies(testSdk, providedTestConfig, testNativeBridge) {
300299
if (testNativeBridge) NativeCodePush = testNativeBridge;
301300
}
302301

302+
async function restartApp(onlyIfUpdateIsPending = false) {
303+
NativeCodePush.restartApp(onlyIfUpdateIsPending);
304+
}
305+
303306
// This function allows only one syncInternal operation to proceed at any given time.
304307
// Parallel calls to sync() while one is ongoing yields CodePush.SyncStatus.SYNC_IN_PROGRESS.
305308
const sync = (() => {
@@ -605,11 +608,11 @@ if (NativeCodePush) {
605608
log,
606609
notifyAppReady: notifyApplicationReady,
607610
notifyApplicationReady,
608-
restartApp: RestartManager.restartApp,
611+
restartApp,
609612
setUpTestDependencies,
610613
sync,
611-
disallowRestart: RestartManager.disallow,
612-
allowRestart: RestartManager.allow,
614+
disallowRestart: NativeCodePush.disallow,
615+
allowRestart: NativeCodePush.allow,
613616
clearUpdates: NativeCodePush.clearUpdates,
614617
InstallMode: {
615618
IMMEDIATE: NativeCodePush.codePushInstallModeImmediate, // Restart the app immediately

RestartManager.js

-59
This file was deleted.

android/app/src/main/java/com/microsoft/codepush/react/CodePushNativeModule.java

+76-21
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
import java.io.IOException;
3131
import java.lang.reflect.Field;
32+
import java.util.ArrayList;
3233
import java.util.Date;
3334
import java.util.HashMap;
3435
import java.util.List;
@@ -45,6 +46,10 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
4546
private CodePushTelemetryManager mTelemetryManager;
4647
private CodePushUpdateManager mUpdateManager;
4748

49+
private boolean _allowed = true;
50+
private boolean _restartInProgress = false;
51+
private ArrayList<Boolean> _restartQueue = new ArrayList<>();
52+
4853
public CodePushNativeModule(ReactApplicationContext reactContext, CodePush codePush, CodePushUpdateManager codePushUpdateManager, CodePushTelemetryManager codePushTelemetryManager, SettingsManager settingsManager) {
4954
super(reactContext);
5055

@@ -206,6 +211,74 @@ private ReactInstanceManager resolveInstanceManager() throws NoSuchFieldExceptio
206211
return instanceManager;
207212
}
208213

214+
private void restartAppInternal(boolean onlyIfUpdateIsPending) {
215+
if (this._restartInProgress) {
216+
CodePushUtils.log("Restart request queued until the current restart is completed");
217+
this._restartQueue.add(onlyIfUpdateIsPending);
218+
return;
219+
} else if (!this._allowed) {
220+
CodePushUtils.log("Restart request queued until restarts are re-allowed");
221+
this._restartQueue.add(onlyIfUpdateIsPending);
222+
return;
223+
}
224+
225+
this._restartInProgress = true;
226+
if (!onlyIfUpdateIsPending || mSettingsManager.isPendingUpdate(null)) {
227+
loadBundle();
228+
CodePushUtils.log("Restarting app");
229+
return;
230+
}
231+
232+
this._restartInProgress = false;
233+
if (this._restartQueue.size() > 0) {
234+
boolean buf = this._restartQueue.get(0);
235+
this._restartQueue.remove(0);
236+
this.restartAppInternal(buf);
237+
}
238+
}
239+
240+
@ReactMethod
241+
public void allow(Promise promise) {
242+
CodePushUtils.log("Re-allowing restarts");
243+
this._allowed = true;
244+
245+
if (_restartQueue.size() > 0) {
246+
CodePushUtils.log("Executing pending restart");
247+
boolean buf = this._restartQueue.get(0);
248+
this._restartQueue.remove(0);
249+
this.restartAppInternal(buf);
250+
}
251+
252+
promise.resolve(null);
253+
return;
254+
}
255+
256+
@ReactMethod
257+
public void clearPendingRestart(Promise promise) {
258+
this._restartQueue.clear();
259+
promise.resolve(null);
260+
return;
261+
}
262+
263+
@ReactMethod
264+
public void disallow(Promise promise) {
265+
CodePushUtils.log("Disallowing restarts");
266+
this._allowed = false;
267+
promise.resolve(null);
268+
return;
269+
}
270+
271+
@ReactMethod
272+
public void restartApp(boolean onlyIfUpdateIsPending, Promise promise) {
273+
try {
274+
restartAppInternal(onlyIfUpdateIsPending);
275+
promise.resolve(null);
276+
} catch(CodePushUnknownException e) {
277+
CodePushUtils.log(e);
278+
promise.reject(e);
279+
}
280+
}
281+
209282
@ReactMethod
210283
public void downloadUpdate(final ReadableMap updatePackage, final boolean notifyProgress, final Promise promise) {
211284
AsyncTask<Void, Void, Void> asyncTask = new AsyncTask<Void, Void, Void>() {
@@ -411,7 +484,7 @@ protected Void doInBackground(Void... params) {
411484
return null;
412485
}
413486
}
414-
487+
415488
promise.resolve("");
416489
} catch(CodePushUnknownException e) {
417490
CodePushUtils.log(e);
@@ -460,7 +533,7 @@ protected Void doInBackground(Void... params) {
460533
@Override
461534
public void run() {
462535
CodePushUtils.log("Loading bundle on suspend");
463-
loadBundle();
536+
restartAppInternal(false);
464537
}
465538
};
466539

@@ -474,7 +547,7 @@ public void onHostResume() {
474547
if (installMode == CodePushInstallMode.IMMEDIATE.getValue()
475548
|| durationInBackground >= CodePushNativeModule.this.mMinimumBackgroundDuration) {
476549
CodePushUtils.log("Loading bundle on resume");
477-
loadBundle();
550+
restartAppInternal(false);
478551
}
479552
}
480553
}
@@ -582,24 +655,6 @@ public void recordStatusReported(ReadableMap statusReport) {
582655
}
583656
}
584657

585-
@ReactMethod
586-
public void restartApp(boolean onlyIfUpdateIsPending, Promise promise) {
587-
try {
588-
// If this is an unconditional restart request, or there
589-
// is current pending update, then reload the app.
590-
if (!onlyIfUpdateIsPending || mSettingsManager.isPendingUpdate(null)) {
591-
loadBundle();
592-
promise.resolve(true);
593-
return;
594-
}
595-
596-
promise.resolve(false);
597-
} catch(CodePushUnknownException e) {
598-
CodePushUtils.log(e);
599-
promise.reject(e);
600-
}
601-
}
602-
603658
@ReactMethod
604659
public void saveStatusReportForRetry(ReadableMap statusReport) {
605660
try {

code-push-plugin-testing-framework/script/platform.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -240,8 +240,8 @@ var AndroidEmulatorManager = (function () {
240240
var _this = this;
241241
return this.endRunningApplication(appId)
242242
.then(function () {
243-
// Wait for a 10 seconds before restarting.
244-
return Q.delay(10000);
243+
// Wait for a 1 second before restarting.
244+
return Q.delay(1000);
245245
})
246246
.then(function () {
247247
return _this.launchInstalledApplication(appId);

code-push-plugin-testing-framework/script/testBuilder.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ function itInternal(func, expectation, isCoreTest, assertion) {
6464
if ((!TestConfig.onlyRunCoreTests || isCoreTest)) {
6565
// Create a wrapper around the assertion to set the timeout on the test to 10 minutes.
6666
var assertionWithTimeout = function (done) {
67-
this.timeout(10 * 60 * 1000);
67+
this.timeout(10 * 2 * 60 * 1000);
6868
assertion(done);
6969
};
7070
return it(expectation, assertionWithTimeout);

ios/CodePush/CodePush.m

+70-12
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ @implementation CodePush {
3131
long long _latestExpectedContentLength;
3232
long long _latestReceivedConentLength;
3333
BOOL _didUpdateProgress;
34+
35+
BOOL _allowed;
36+
BOOL _restartInProgress;
37+
NSMutableArray *_restartQueue;
3438
}
3539

3640
RCT_EXPORT_MODULE()
@@ -369,8 +373,11 @@ + (void)ensureBinaryBundleExists
369373

370374
- (instancetype)init
371375
{
376+
_allowed = YES;
377+
_restartInProgress = NO;
378+
_restartQueue = [NSMutableArray arrayWithCapacity:1];
379+
372380
self = [super init];
373-
374381
if (self) {
375382
[self initializeUpdateAfterRestart];
376383
}
@@ -659,7 +666,7 @@ - (void)applicationWillEnterForeground
659666
} else {
660667
// For resume install mode.
661668
if (durationInBackground >= _minimumBackgroundDuration) {
662-
[self loadBundle];
669+
[self restartAppInternal:NO];
663670
}
664671
}
665672
}
@@ -680,7 +687,7 @@ - (void)applicationWillResignActive
680687
}
681688

682689
-(void)loadBundleOnTick:(NSTimer *)timer {
683-
[self loadBundle];
690+
[self restartAppInternal:NO];
684691
}
685692

686693
#pragma mark - JavaScript-exported module methods (Public)
@@ -752,6 +759,33 @@ -(void)loadBundleOnTick:(NSTimer *)timer {
752759
}];
753760
}
754761

762+
- (void)restartAppInternal:(BOOL)onlyIfUpdateIsPending
763+
{
764+
if (_restartInProgress) {
765+
CPLog(@"Restart request queued until the current restart is completed.");
766+
[_restartQueue addObject:@(onlyIfUpdateIsPending)];
767+
return;
768+
} else if (!_allowed) {
769+
CPLog(@"Restart request queued until restarts are re-allowed.");
770+
[_restartQueue addObject:@(onlyIfUpdateIsPending)];
771+
return;
772+
}
773+
774+
_restartInProgress = YES;
775+
if (!onlyIfUpdateIsPending || [[self class] isPendingUpdate:nil]) {
776+
[self loadBundle];
777+
CPLog(@"Restarting app.");
778+
return;
779+
}
780+
781+
_restartInProgress = NO;
782+
if ([_restartQueue count] > 0) {
783+
BOOL buf = [_restartQueue valueForKey: @"@firstObject"];
784+
[_restartQueue removeObjectAtIndex:0];
785+
[self restartAppInternal:buf];
786+
}
787+
}
788+
755789
/*
756790
* This is the native side of the CodePush.getConfiguration method. It isn't
757791
* currently exposed via the "react-native-code-push" module, and is used
@@ -939,22 +973,46 @@ -(void)loadBundleOnTick:(NSTimer *)timer {
939973
resolve(nil);
940974
}
941975

976+
RCT_EXPORT_METHOD(allow:(RCTPromiseResolveBlock)resolve
977+
rejecter:(RCTPromiseRejectBlock)reject)
978+
{
979+
CPLog(@"Re-allowing restarts.");
980+
_allowed = YES;
981+
982+
if ([_restartQueue count] > 0) {
983+
CPLog(@"Executing pending restart.");
984+
BOOL buf = [_restartQueue valueForKey: @"@firstObject"];
985+
[_restartQueue removeObjectAtIndex:0];
986+
[self restartAppInternal:buf];
987+
}
988+
989+
resolve(nil);
990+
}
991+
992+
RCT_EXPORT_METHOD(clearPendingRestart:(RCTPromiseResolveBlock)resolve
993+
rejecter:(RCTPromiseRejectBlock)reject)
994+
{
995+
[_restartQueue removeAllObjects];
996+
resolve(nil);
997+
}
998+
999+
RCT_EXPORT_METHOD(disallow:(RCTPromiseResolveBlock)resolve
1000+
rejecter:(RCTPromiseRejectBlock)reject)
1001+
{
1002+
CPLog(@"Disallowing restarts.");
1003+
_allowed = NO;
1004+
resolve(nil);
1005+
}
1006+
9421007
/*
9431008
* This method is the native side of the CodePush.restartApp() method.
9441009
*/
9451010
RCT_EXPORT_METHOD(restartApp:(BOOL)onlyIfUpdateIsPending
9461011
resolve:(RCTPromiseResolveBlock)resolve
9471012
rejecter:(RCTPromiseRejectBlock)reject)
9481013
{
949-
// If this is an unconditional restart request, or there
950-
// is current pending update, then reload the app.
951-
if (!onlyIfUpdateIsPending || [[self class] isPendingUpdate:nil]) {
952-
[self loadBundle];
953-
resolve(@(YES));
954-
return;
955-
}
956-
957-
resolve(@(NO));
1014+
[self restartAppInternal:onlyIfUpdateIsPending];
1015+
resolve(nil);
9581016
}
9591017

9601018
/*

0 commit comments

Comments
 (0)