Skip to content
This repository was archived by the owner on May 20, 2025. It is now read-only.

Commit 97303b7

Browse files
authored
Merge pull request #387 from Microsoft/restart-app-2x
fix double restart issue
2 parents 7d18c26 + 9cfbac6 commit 97303b7

File tree

8 files changed

+117
-22
lines changed

8 files changed

+117
-22
lines changed

Examples/CodePushDemoApp/android/app/build.gradle

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,7 @@ apply from: "react.gradle"
6161
apply from: "../../node_modules/react-native-code-push/android/codepush.gradle"
6262

6363
/**
64-
* Set this to true to create three separate APKs instead of one:
65-
* - A universal APK that works on all devices
64+
* Set this to true to create two separate APKs instead of one:
6665
* - An APK that only works on ARM devices
6766
* - An APK that only works on x86 devices
6867
* The advantage is the size of the APK is reduced by about 4MB.
@@ -93,7 +92,7 @@ android {
9392
splits {
9493
abi {
9594
enable enableSeparateBuildPerCPUArchitecture
96-
universalApk true
95+
universalApk false // Also generate an universal APK
9796
reset()
9897
include "armeabi-v7a", "x86"
9998
}

Examples/CodePushDemoApp/crossplatformdemo.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ let CodePushDemoApp = React.createClass({
5757
break;
5858
case CodePush.SyncStatus.UPDATE_INSTALLED:
5959
self.setState({
60-
syncMessage: "Update installed and will be run when the app next resumes.",
60+
syncMessage: "Update installed.",
6161
progress: false
6262
});
6363
break;

RestartManager.js

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,44 +3,57 @@ const NativeCodePush = require("react-native").NativeModules.CodePush;
33

44
const RestartManager = (() => {
55
let _allowed = true;
6-
let _restartPending = false;
6+
let _restartInProgress = false;
7+
let _restartQueue = [];
78

89
function allow() {
910
log("Re-allowing restarts");
1011
_allowed = true;
11-
12-
if (_restartPending) {
12+
13+
if (_restartQueue.length) {
1314
log("Executing pending restart");
14-
restartApp(true);
15+
restartApp(_restartQueue.shift(1));
1516
}
1617
}
17-
18+
1819
function clearPendingRestart() {
19-
_restartPending = false;
20+
_restartQueue = [];
2021
}
21-
22+
2223
function disallow() {
2324
log("Disallowing restarts");
2425
_allowed = false;
2526
}
2627

27-
function restartApp(onlyIfUpdateIsPending = false) {
28-
if (_allowed) {
29-
NativeCodePush.restartApp(onlyIfUpdateIsPending);
30-
log("Restarting app");
31-
} else {
28+
async function restartApp(onlyIfUpdateIsPending = false) {
29+
if (_restartInProgress) {
30+
log("Restart request queued until the current restart is completed");
31+
_restartQueue.push(onlyIfUpdateIsPending);
32+
} else if (!_allowed) {
3233
log("Restart request queued until restarts are re-allowed");
33-
_restartPending = true;
34-
return true;
34+
_restartQueue.push(onlyIfUpdateIsPending);
35+
} else {
36+
_restartInProgress = true;
37+
if (await NativeCodePush.restartApp(onlyIfUpdateIsPending)) {
38+
// The app has already restarted, so there is no need to
39+
// process the remaining queued restarts.
40+
log("Restarting app");
41+
return;
42+
}
43+
44+
_restartInProgress = false;
45+
if (_restartQueue.length) {
46+
restartApp(_restartQueue.shift(1));
47+
}
3548
}
3649
}
3750

3851
return {
3952
allow,
40-
clearPendingRestart,
53+
clearPendingRestart,
4154
disallow,
4255
restartApp
4356
};
4457
})();
4558

46-
module.exports = RestartManager;
59+
module.exports = RestartManager;

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,12 +449,16 @@ public void recordStatusReported(ReadableMap statusReport) {
449449
}
450450

451451
@ReactMethod
452-
public void restartApp(boolean onlyIfUpdateIsPending) {
452+
public void restartApp(boolean onlyIfUpdateIsPending, Promise promise) {
453453
// If this is an unconditional restart request, or there
454454
// is current pending update, then reload the app.
455455
if (!onlyIfUpdateIsPending || mSettingsManager.isPendingUpdate(null)) {
456456
loadBundle();
457+
promise.resolve(true);
458+
return;
457459
}
460+
461+
promise.resolve(false);
458462
}
459463

460464
@ReactMethod

ios/CodePush/CodePush.m

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -740,13 +740,19 @@ - (void)applicationWillResignActive
740740
/*
741741
* This method is the native side of the CodePush.restartApp() method.
742742
*/
743-
RCT_EXPORT_METHOD(restartApp:(BOOL)onlyIfUpdateIsPending)
743+
RCT_EXPORT_METHOD(restartApp:(BOOL)onlyIfUpdateIsPending
744+
resolve:(RCTPromiseResolveBlock)resolve
745+
rejecter:(RCTPromiseRejectBlock)reject)
744746
{
745747
// If this is an unconditional restart request, or there
746748
// is current pending update, then reload the app.
747749
if (!onlyIfUpdateIsPending || [self isPendingUpdate:nil]) {
748750
[self loadBundle];
751+
resolve(@(YES));
752+
return;
749753
}
754+
755+
resolve(@(NO));
750756
}
751757

752758
#pragma mark - JavaScript-exported module methods (Private)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
var CodePushWrapper = require("../codePushWrapper.js");
2+
import CodePush from "react-native-code-push";
3+
4+
module.exports = {
5+
startTest: function(testApp) {
6+
CodePushWrapper.checkAndInstall(testApp,
7+
() => {
8+
CodePush.restartApp();
9+
CodePush.restartApp();
10+
}
11+
);
12+
},
13+
14+
getScenarioName: function() {
15+
return "Install and Restart 2x";
16+
}
17+
};
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
var CodePushWrapper = require("../codePushWrapper.js");
2+
import CodePush from "react-native-code-push";
3+
4+
module.exports = {
5+
startTest: function(testApp) {
6+
CodePush.restartApp(true);
7+
CodePushWrapper.checkAndInstall(testApp,
8+
() => {
9+
CodePush.restartApp(true);
10+
}
11+
);
12+
},
13+
14+
getScenarioName: function() {
15+
return "Restart2x";
16+
}
17+
};

test/test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,12 +465,14 @@ const ScenarioInstall = "scenarioInstall.js";
465465
const ScenarioInstallOnResumeWithRevert = "scenarioInstallOnResumeWithRevert.js";
466466
const ScenarioInstallOnRestartWithRevert = "scenarioInstallOnRestartWithRevert.js";
467467
const ScenarioInstallWithRevert = "scenarioInstallWithRevert.js";
468+
const ScenarioInstallRestart2x = "scenarioInstallRestart2x.js";
468469
const ScenarioSync1x = "scenarioSync.js";
469470
const ScenarioSyncResume = "scenarioSyncResume.js";
470471
const ScenarioSyncResumeDelay = "scenarioSyncResumeDelay.js";
471472
const ScenarioSyncRestartDelay = "scenarioSyncResumeDelay.js";
472473
const ScenarioSync2x = "scenarioSync2x.js";
473474
const ScenarioRestart = "scenarioRestart.js";
475+
const ScenarioRestart2x = "scenarioRestart2x.js";
474476
const ScenarioSyncMandatoryDefault = "scenarioSyncMandatoryDefault.js";
475477
const ScenarioSyncMandatoryResume = "scenarioSyncMandatoryResume.js";
476478
const ScenarioSyncMandatoryRestart = "scenarioSyncMandatoryRestart.js";
@@ -986,6 +988,43 @@ PluginTestingFramework.initializeTests(new RNProjectManager(), supportedTargetPl
986988
});
987989
}, ScenarioRestart);
988990

991+
TestBuilder.describe("#codePush.restartApplication.2x",
992+
() => {
993+
TestBuilder.it("blocks when a restart is in progress and doesn't crash if there is a pending package", false,
994+
(done: MochaDone) => {
995+
ServerUtil.updateResponse = { updateInfo: ServerUtil.createUpdateResponse(false, targetPlatform) };
996+
setupTestRunScenario(projectManager, targetPlatform, ScenarioInstallRestart2x)
997+
.then(setupUpdateScenario.bind(this, projectManager, targetPlatform, UpdateDeviceReady, "Update 1"))
998+
.then<void>((updatePath: string) => {
999+
ServerUtil.updatePackagePath = updatePath;
1000+
projectManager.runApplication(TestConfig.testRunDirectory, targetPlatform);
1001+
return ServerUtil.expectTestMessages([
1002+
ServerUtil.TestMessage.CHECK_UPDATE_AVAILABLE,
1003+
ServerUtil.TestMessage.DOWNLOAD_SUCCEEDED,
1004+
ServerUtil.TestMessage.UPDATE_INSTALLED,
1005+
ServerUtil.TestMessage.DEVICE_READY_AFTER_UPDATE]);
1006+
})
1007+
.done(() => { done(); }, (e) => { done(e); });
1008+
});
1009+
1010+
TestBuilder.it("doesn't block when the restart is ignored", false,
1011+
(done: MochaDone) => {
1012+
ServerUtil.updateResponse = { updateInfo: ServerUtil.createUpdateResponse(false, targetPlatform) };
1013+
setupTestRunScenario(projectManager, targetPlatform, ScenarioRestart2x)
1014+
.then(setupUpdateScenario.bind(this, projectManager, targetPlatform, UpdateDeviceReady, "Update 1"))
1015+
.then<void>((updatePath: string) => {
1016+
ServerUtil.updatePackagePath = updatePath;
1017+
projectManager.runApplication(TestConfig.testRunDirectory, targetPlatform);
1018+
return ServerUtil.expectTestMessages([
1019+
ServerUtil.TestMessage.CHECK_UPDATE_AVAILABLE,
1020+
ServerUtil.TestMessage.DOWNLOAD_SUCCEEDED,
1021+
ServerUtil.TestMessage.UPDATE_INSTALLED,
1022+
ServerUtil.TestMessage.DEVICE_READY_AFTER_UPDATE]);
1023+
})
1024+
.done(() => { done(); }, (e) => { done(e); });
1025+
});
1026+
});
1027+
9891028
TestBuilder.describe("#window.codePush.sync",
9901029
() => {
9911030
// We test the functionality with sync twice--first, with sync only called once,

0 commit comments

Comments
 (0)