Merge pull request #387 from Microsoft/restart-app-2x

fix double restart issue
This commit is contained in:
Geoffrey Goh
2016-06-28 17:04:04 -07:00
committed by GitHub
8 changed files with 117 additions and 22 deletions

View File

@@ -61,8 +61,7 @@ apply from: "react.gradle"
apply from: "../../node_modules/react-native-code-push/android/codepush.gradle"
/**
* Set this to true to create three separate APKs instead of one:
* - A universal APK that works on all devices
* Set this to true to create two separate APKs instead of one:
* - An APK that only works on ARM devices
* - An APK that only works on x86 devices
* The advantage is the size of the APK is reduced by about 4MB.
@@ -93,7 +92,7 @@ android {
splits {
abi {
enable enableSeparateBuildPerCPUArchitecture
universalApk true
universalApk false // Also generate an universal APK
reset()
include "armeabi-v7a", "x86"
}

View File

@@ -57,7 +57,7 @@ let CodePushDemoApp = React.createClass({
break;
case CodePush.SyncStatus.UPDATE_INSTALLED:
self.setState({
syncMessage: "Update installed and will be run when the app next resumes.",
syncMessage: "Update installed.",
progress: false
});
break;

View File

@@ -3,44 +3,57 @@ const NativeCodePush = require("react-native").NativeModules.CodePush;
const RestartManager = (() => {
let _allowed = true;
let _restartPending = false;
let _restartInProgress = false;
let _restartQueue = [];
function allow() {
log("Re-allowing restarts");
_allowed = true;
if (_restartPending) {
if (_restartQueue.length) {
log("Executing pending restart");
restartApp(true);
restartApp(_restartQueue.shift(1));
}
}
function clearPendingRestart() {
_restartPending = false;
_restartQueue = [];
}
function disallow() {
log("Disallowing restarts");
_allowed = false;
}
function restartApp(onlyIfUpdateIsPending = false) {
if (_allowed) {
NativeCodePush.restartApp(onlyIfUpdateIsPending);
log("Restarting app");
} else {
async function restartApp(onlyIfUpdateIsPending = false) {
if (_restartInProgress) {
log("Restart request queued until the current restart is completed");
_restartQueue.push(onlyIfUpdateIsPending);
} else if (!_allowed) {
log("Restart request queued until restarts are re-allowed");
_restartPending = true;
return true;
_restartQueue.push(onlyIfUpdateIsPending);
} else {
_restartInProgress = true;
if (await NativeCodePush.restartApp(onlyIfUpdateIsPending)) {
// The app has already restarted, so there is no need to
// process the remaining queued restarts.
log("Restarting app");
return;
}
_restartInProgress = false;
if (_restartQueue.length) {
restartApp(_restartQueue.shift(1));
}
}
}
return {
allow,
clearPendingRestart,
clearPendingRestart,
disallow,
restartApp
};
})();
module.exports = RestartManager;
module.exports = RestartManager;

View File

@@ -449,12 +449,16 @@ public class CodePushNativeModule extends ReactContextBaseJavaModule {
}
@ReactMethod
public void restartApp(boolean onlyIfUpdateIsPending) {
public void restartApp(boolean onlyIfUpdateIsPending, Promise promise) {
// If this is an unconditional restart request, or there
// is current pending update, then reload the app.
if (!onlyIfUpdateIsPending || mSettingsManager.isPendingUpdate(null)) {
loadBundle();
promise.resolve(true);
return;
}
promise.resolve(false);
}
@ReactMethod

View File

@@ -740,13 +740,19 @@ RCT_EXPORT_METHOD(notifyApplicationReady:(RCTPromiseResolveBlock)resolve
/*
* This method is the native side of the CodePush.restartApp() method.
*/
RCT_EXPORT_METHOD(restartApp:(BOOL)onlyIfUpdateIsPending)
RCT_EXPORT_METHOD(restartApp:(BOOL)onlyIfUpdateIsPending
resolve:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
// If this is an unconditional restart request, or there
// is current pending update, then reload the app.
if (!onlyIfUpdateIsPending || [self isPendingUpdate:nil]) {
[self loadBundle];
resolve(@(YES));
return;
}
resolve(@(NO));
}
#pragma mark - JavaScript-exported module methods (Private)

View File

@@ -0,0 +1,17 @@
var CodePushWrapper = require("../codePushWrapper.js");
import CodePush from "react-native-code-push";
module.exports = {
startTest: function(testApp) {
CodePushWrapper.checkAndInstall(testApp,
() => {
CodePush.restartApp();
CodePush.restartApp();
}
);
},
getScenarioName: function() {
return "Install and Restart 2x";
}
};

View File

@@ -0,0 +1,17 @@
var CodePushWrapper = require("../codePushWrapper.js");
import CodePush from "react-native-code-push";
module.exports = {
startTest: function(testApp) {
CodePush.restartApp(true);
CodePushWrapper.checkAndInstall(testApp,
() => {
CodePush.restartApp(true);
}
);
},
getScenarioName: function() {
return "Restart2x";
}
};

View File

@@ -465,12 +465,14 @@ const ScenarioInstall = "scenarioInstall.js";
const ScenarioInstallOnResumeWithRevert = "scenarioInstallOnResumeWithRevert.js";
const ScenarioInstallOnRestartWithRevert = "scenarioInstallOnRestartWithRevert.js";
const ScenarioInstallWithRevert = "scenarioInstallWithRevert.js";
const ScenarioInstallRestart2x = "scenarioInstallRestart2x.js";
const ScenarioSync1x = "scenarioSync.js";
const ScenarioSyncResume = "scenarioSyncResume.js";
const ScenarioSyncResumeDelay = "scenarioSyncResumeDelay.js";
const ScenarioSyncRestartDelay = "scenarioSyncResumeDelay.js";
const ScenarioSync2x = "scenarioSync2x.js";
const ScenarioRestart = "scenarioRestart.js";
const ScenarioRestart2x = "scenarioRestart2x.js";
const ScenarioSyncMandatoryDefault = "scenarioSyncMandatoryDefault.js";
const ScenarioSyncMandatoryResume = "scenarioSyncMandatoryResume.js";
const ScenarioSyncMandatoryRestart = "scenarioSyncMandatoryRestart.js";
@@ -986,6 +988,43 @@ PluginTestingFramework.initializeTests(new RNProjectManager(), supportedTargetPl
});
}, ScenarioRestart);
TestBuilder.describe("#codePush.restartApplication.2x",
() => {
TestBuilder.it("blocks when a restart is in progress and doesn't crash if there is a pending package", false,
(done: MochaDone) => {
ServerUtil.updateResponse = { updateInfo: ServerUtil.createUpdateResponse(false, targetPlatform) };
setupTestRunScenario(projectManager, targetPlatform, ScenarioInstallRestart2x)
.then(setupUpdateScenario.bind(this, projectManager, targetPlatform, UpdateDeviceReady, "Update 1"))
.then<void>((updatePath: string) => {
ServerUtil.updatePackagePath = updatePath;
projectManager.runApplication(TestConfig.testRunDirectory, targetPlatform);
return ServerUtil.expectTestMessages([
ServerUtil.TestMessage.CHECK_UPDATE_AVAILABLE,
ServerUtil.TestMessage.DOWNLOAD_SUCCEEDED,
ServerUtil.TestMessage.UPDATE_INSTALLED,
ServerUtil.TestMessage.DEVICE_READY_AFTER_UPDATE]);
})
.done(() => { done(); }, (e) => { done(e); });
});
TestBuilder.it("doesn't block when the restart is ignored", false,
(done: MochaDone) => {
ServerUtil.updateResponse = { updateInfo: ServerUtil.createUpdateResponse(false, targetPlatform) };
setupTestRunScenario(projectManager, targetPlatform, ScenarioRestart2x)
.then(setupUpdateScenario.bind(this, projectManager, targetPlatform, UpdateDeviceReady, "Update 1"))
.then<void>((updatePath: string) => {
ServerUtil.updatePackagePath = updatePath;
projectManager.runApplication(TestConfig.testRunDirectory, targetPlatform);
return ServerUtil.expectTestMessages([
ServerUtil.TestMessage.CHECK_UPDATE_AVAILABLE,
ServerUtil.TestMessage.DOWNLOAD_SUCCEEDED,
ServerUtil.TestMessage.UPDATE_INSTALLED,
ServerUtil.TestMessage.DEVICE_READY_AFTER_UPDATE]);
})
.done(() => { done(); }, (e) => { done(e); });
});
});
TestBuilder.describe("#window.codePush.sync",
() => {
// We test the functionality with sync twice--first, with sync only called once,