使用App Center 的CodePush(二)
Android和ios配置codepush
一、平台规则
(1).苹果App允许使用热更新Apple's developer agreement, 为了不影响用户体验,规定必须使用静默更新。
(2).Google Play不能使用静默更新,必须弹框告知用户App有更新。
(3).中国的android市场必须采用静默更新(如果弹框提示,App会被“请上传最新版本的二进制应用包”原因驳回)。
二、安装codepush
使用RN版本codepush
$ yarn add react-native-code-push
如果RN版本小于0.60 需要执行
react-native link react-native-code-push
三、Android集成
1.link之后,需要确保文件已配置
android/settings.gradle 文件下:
include ':react-native-code-push'
project(':react-native-code-push').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-code-push/android/app')
android/app/build.gradle
dependencies {
...
implementation project(':react-native-code-push')
...
}
MainApplication.java
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
...
new CodePush(getResources().getString(R.string.CodePushDeploymentKey), getApplicationContext(), BuildConfig.DEBUG));
}
2.设置部署的key
android/app/build.gradle
buildTypes {
debug {
resValue "string", "CodePushDeploymentKey", '""'
resValue 'string', "CODE_PUSH_APK_BUILD_TIME", String.format("\"%d\"", System.currentTimeMillis())
}
release {
signingConfig signingConfigs.release
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
resValue "string", "CodePushDeploymentKey", '"xxx"'
resValue 'string', "CODE_PUSH_APK_BUILD_TIME", String.format("\"%d\"", System.currentTimeMillis())
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a'
}
}
releaseDevelop {
signingConfig signingConfigs.release
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
resValue "string", "CodePushDeploymentKey", '"xxx"'
resValue 'string', "CODE_PUSH_APK_BUILD_TIME", String.format("\"%d\"", System.currentTimeMillis())
matchingFallbacks = ['release']
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a'
// architectures: 'armeabi-v7a', 'arm64-v8a', 'armeabi', 'x86', 'x86_64'
}
}
releaseStaging {
signingConfig signingConfigs.release
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
resValue "string", "CodePushDeploymentKey", '"xxx"'
resValue 'string', "CODE_PUSH_APK_BUILD_TIME", String.format("\"%d\"", System.currentTimeMillis())
matchingFallbacks = ['release']
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a'
// architectures: 'armeabi-v7a', 'arm64-v8a', 'armeabi', 'x86', 'x86_64'
}
}
}
可以看到,在这里有三个环境, develop,staging release=production
其中release,releaseDevelop,releaseStaging 是安卓规定的,不可修改。
四、ios配置
(1).打开Xcode项目,我在这里创建了三个Scheme,
分别是qingyulan,qingyulanStag,qingyulanPro
(2).选择 Build Settings 选项卡,点击工具栏的 + 并选择 Add User-Defined Setting
创建 CodePushDeploymentKey, 只更改release,同样的qingyulan和qingyulanStag都要更改
(3).打开项目的 Info.plist 文件,然后将CodePushDeploymentKey条目的值更改为$(CODEPUSH_KEY)
五、js代码
installMode参数
IMMEDIATE 立即更新
ON_NEXT_RESTART 下次App启动
ON_NEXT_RESUME 下次回到前台
ON_NEXT_SUSPEND 回到后台时间性,配合 minimumBackgroundDuration: 2 * 60 使用
(1).直接简易更新
App.js
import codePush from 'react-native-code-push'
const codePushOptions = {
installMode: codePush.InstallMode.ON_NEXT_RESUME,
};
const App = () => {
return <></>
}
export default codePush(codePushOptions)(App);
(2).手动更新
想对检查更新发生的时间进行细粒度控制,可以使用CodePush.sync() 随时调用
可以先关掉自动检查功能
import React from 'react';
import {View, StyleSheet,Button} from 'react-native';
import codePush from 'react-native-code-push';
const codePushOptions = { checkFrequency: codePush.CheckFrequency.MANUAL };
const App = () => {
const checkForUpdate = () => {
codePush.sync({
updateDialog: true,
installMode: codePush.InstallMode.IMMEDIATE,
});
};
const clear = () => {
codePush.clearUpdates();
};
return (
<View style={styles.container}>
<Button onPress={checkForUpdate}>
检查更新
</AwesomeButton>
<Button onPress={clear}>
清除更新
</AwesomeButton>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default codePush(codePushOptions)(App);
(3).动态分配key
如果是服务器下发,动态分配key
创建一个文件
CodePushUtils.js
import { AppState, Platform, Alert } from 'react-native';
import codePush from 'react-native-code-push';
import RNConfigReader from 'react-native-config-reader';
const isDebugEnvironment = () => {
const { scriptURL } = NativeModules.SourceCode;
const devEvn = scriptURL.split('&')[1];
return devEvn === 'dev=true';
};
const CodePushDeploymentKey = {
ios: {
development: '',
staging: '',
production: '',
},
android: {
debug: '',
release: '',
releaseDevelop: '',
releaseStaging: '',
},
};
const getDeploymentKey = () => {
const buildType = RNConfigReader.BUILD_TYPE;
const deploymentKey =
Platform.OS === 'android'
? CodePushDeploymentKey[Platform.OS][buildType]
: isDebugEnvironment
? ''
: CodePushDeploymentKey[Platform.OS][Config.APP_ENV];
return deploymentKey;
};
const codePushStatusDidChange = async (syncStatus) => {
switch (syncStatus) {
case codePush.SyncStatus.CHECKING_FOR_UPDATE:
// 0 - 正在查询CodePush服务器以进行更新。
console.info('[CodePush] Checking for update.');
break;
case codePush.SyncStatus.AWAITING_USER_ACTION:
// 1 - 有可用的更新,并且向最终用户显示了一个确认对话框。(仅在updateDialog使用时适用)
console.info('[CodePush] Awaiting user action.');
break;
case codePush.SyncStatus.DOWNLOADING_PACKAGE:
// 2 - 正在从CodePush服务器下载可用更新。
console.info('[CodePush] Downloading package.');
break;
case codePush.SyncStatus.INSTALLING_UPDATE:
// 3 - 已下载一个可用的更新,并将其安装。
console.info('[CodePush] Installing update.');
break;
case codePush.SyncStatus.UP_TO_DATE:
// 4 - 应用程序已配置的部署完全最新。
console.info('[CodePush] App is up to date.');
break;
case codePush.SyncStatus.UPDATE_IGNORED:
// 5 该应用程序具有可选更新,最终用户选择忽略该更新。(仅在updateDialog使用时适用)
console.info('[CodePush] User cancelled the update.');
break;
case codePush.SyncStatus.UPDATE_INSTALLED:
// 6 - 安装了一个可用的更新,它将根据 SyncOptions 中的 InstallMode指定在 syncStatusChangedCallback 函数返回后立即或在下次应用恢复/重新启动时立即运行。
console.info('[CodePush] Installed update.');
break;
case codePush.SyncStatus.SYNC_IN_PROGRESS:
// 7 - 正在执行的 sync 操作
console.info('[CodePush] Sync already in progress.');
break;
case codePush.SyncStatus.UNKNOWN_ERROR:
// -1 - 同步操作遇到未知错误。
console.info('[CodePush] An unknown error occurred.');
break;
}
};
const codePushDownloadDidProgress = (progress) => {
const curPercent = ((progress.receivedBytes / progress.totalBytes) * 100).toFixed(0);
console.log('[CodePushUtils] Downloading Progress', `${curPercent}%`);
// console.log(`${progress.receivedBytes} of ${progress.totalBytes} received.`);
};
const syncImmediate = async () => {
const deploymentKey = getDeploymentKey();
codePush.sync(
{
updateDialog: {
// 是否显示更新描述
appendReleaseDescription: true,
// 更新描述的前缀。 默认为"Description"
descriptionPrefix: '\n\n更新内容:\n',
// 强制更新按钮文字,默认为continue
mandatoryContinueButtonLabel: '立即更新',
// 强制更新时的信息. 默认为"An update is available that must be installed."
mandatoryUpdateMessage: '必须更新后才能使用',
// 非强制更新时,按钮文字,默认为"ignore"
optionalIgnoreButtonLabel: '稍后',
// 非强制更新时,确认按钮文字. 默认为"Install"
optionalInstallButtonLabel: '后台更新',
// 非强制更新时,检查到更新的消息文本
optionalUpdateMessage: '有新版本了,是否更新?',
// Alert窗口的标题
title: '更新',
},
deploymentKey,
installMode: codePush.InstallMode.IMMEDIATE,
},
codePushStatusDidChange,
codePushDownloadDidProgress,
);
};
export const checkForUpdate = async () => {
const deploymentKey = getDeploymentKey();
const update = await codePush.checkForUpdate(deploymentKey);
if (!update) {
Alert.alert('提示', '已是最新版本');
} else {
syncImmediate();
}
};
export const codePushSync = () => {
checkForUpdate();
// AppState.addEventListener('change', (newState) => {
// newState === 'active' && syncImmediate();
// });
};
评论区