React-Native 原生 APP 更新
当一个 APP在运行的时候, 开发者想要将自己的代码更新到用户的手机上时, 一般都有两种方案, 一是热更新, 二就是 APP 更新.
热更新暂且不说,这篇文章就讲讲 APP 如何更新
App更新流程
- 在 App 打开时请求接口或文件, 获取远程版本/版本更新说明/地址等等重用信息
- 通过库或者原生方案, 获取 App 的当前版本
- 比较远程版本和当前版本的区别(可以使用库,也可以自己写一个比较方案)
- 通过获取到的链接进行操作(可以跳转到对应网站下载,类似蒲公英这种,可以是 apk 链接, 通过安卓原生方法下载, 也可以是 App Store 链接)
大致的流程图
详细说明:
- 这些远程信息,可以是接口, 这样可以有一个中台来控制, 当然也可以是一个文件, 让运维来控制
关于信息,不止于远程版本, 在项目里还可以添加其他属性,如:
versionCode
,versionCodeSwitch
,notUpdate
,deleteApp
1.1
versionCode
通过 code 来升级版本,一般是一个数字(在 ios 里提交 App Store 的时候有需要用到的地方), 这样versionName
并不会增加, 但是如果添加了versionCode
, 如果要升级versionName
,versionCode
也需要增加1.2
versionCodeSwitch
用来控制是否要根据versionCode
来更新, 一般我都是在测试和其他环境开启,生产环境关闭的1.3
notUpdate
是否要根据远程信息来更新, 一般都是开启状态1.3
deleteApp
安卓 app 需要卸载重新安装, 因为直接安装可能存在某些问题, 将会使用此信息,先删除 APP, 再重新下载 - 获取当前手机的信息,方案较多, 我使用的是
react-native-device-info
这个库, 这个库里面提供的信息较全, 当然也可以使用原生方法, 来获取APP的信息 - 关于本地版本号和原生版本号之间的对比也是可以使用库,也可以自己写, 这边推荐两个库,下载量都是百万以上的: semver-compare 和 compare-versions, 这边附上我的
versionName
比较方案, 较为简陋:/**
* 比较两版本号
* @param currentVersion
* @return boolean
* true=需要更新 false=不需要
*/
compareVersion = (currentVersion: string): boolean => {
const {versionName: remoteVersion} = this.remoteInfo || {}
if (!remoteVersion) {
return false
}
if (currentVersion === remoteVersion) {
return false
}
const currentVersionArr = currentVersion.split('.')
const remoteVersionArr = remoteVersion.split('.')
for (let i = 0; i < 3; i++) {
if (Number(currentVersionArr[i]) < Number(remoteVersionArr[i])) {
return true
}
}
return false
}
- 关于下载 app 有很多方案, 最简单的就是跳转链接到第三方平台, 像蒲公英这样的, 使用 RN 提供的 Linking 方法来直接跳转
当然安卓是可以直接通过自己提供的地址下载的, 这里提供一个方法(此方法来源于网络):
@ReactMethod
public void installApk(String filePath, String fileProviderAuthority) {
File file = new File(filePath);
if (!file.exists()) {
Log.e("RNUpdater", "installApk: file doe snot exist '" + filePath + "'");
// FIXME this should take a promise and fail it
return;
}
if (Build.VERSION.SDK_INT >= 24) {
// API24 and up has a package installer that can handle FileProvider content:// URIs
Uri contentUri;
try {
contentUri = FileProvider.getUriForFile(getReactApplicationContext(), fileProviderAuthority, file);
} catch (Exception e) {
// FIXME should be a Promise.reject really
Log.e("RNUpdater", "installApk exception with authority name '" + fileProviderAuthority + "'", e);
throw e;
}
Intent installApp = new Intent(Intent.ACTION_INSTALL_PACKAGE);
installApp.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
installApp.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
installApp.setData(contentUri);
installApp.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, reactContext.getApplicationInfo().packageName);
reactContext.startActivity(installApp);
} else {
// Old APIs do not handle content:// URIs, so use an old file:// style
String cmd = "chmod 777 " + file;
try {
Runtime.getRuntime().exec(cmd);
} catch (Exception e) {
e.printStackTrace();
}
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(Uri.parse("file://" + file), "application/vnd.android.package-archive");
reactContext.startActivity(intent);
}
}
如果是我们自己提供下载服务,需要注意的是带宽, 如果网速过慢则用户体验过差, 还有就会带来更多的流量消耗, 其中的取舍,需要开发者决定
更新APP信息
在打包时, 通过脚本更新接口或者文件信息, 当然这个得看具体的打包方案
比如我现在的方案是使用 Jenkins 打包, 在打包时使用 shell 脚本更新对应信息(有需要也可以使用其他语言脚本):
- 首先定义需要获取的文件地址
androidVersionFilePath="$WORKSPACE/android/app/build.gradle" // 通过此文件获取安卓的版本信息
iosVersionFilePath="$WORKSPACE/ios/veronica/Info.plist" // 通过此文件获取iOS的版本信息
changeLogPath="$WORKSPACE/change.log" // 将版本更新信息存储在此文件中
- 通过文件地址, 获取打完包后的版本信息
getAndroidVersion(){
androidVersion=$(cat $androidVersionFilePath | grep "versionName" | awk '{print $2}' | sed 's/\"//g')
androidCode=$(cat $androidVersionFilePath | grep "versionCode " | awk '{print $2}' | sed 's/\"//g')
androidDelete=$(cat $androidVersionFilePath | grep "deleteApp" | awk '{print $4}' | sed 's/\"//g')
return 0
}
getIOSVersion(){
rows=$(awk '/CFBundleShortVersionString/ {getline; print}' $iosVersionFilePath)
iosVersion=$(echo "$rows" | sed -ne 's/<string>\(.*\)<\/string>/\1/p')
iosVersion=$(echo "$iosVersion" | sed 's/^[[:space:]]*//')
rows2=$(awk '/VersionCode/ {getline; print}' $iosVersionFilePath)
iosCode=$(echo "$rows2" | sed -ne 's/<string>\(.*\)<\/string>/\1/p')
iosCode=$(echo "$iosCode" | sed 's/^[[:space:]]*//')
return 0
}
desc=$(cat $changeLogPath | tr "\n" "#")
- 替换现有文件中的信息:
sed -i '' "s/\"releaseInfo\":.*$/\"releaseInfo\": \"$desc\"/" $JsonPath/$fileName
sed -i '' "s/\"versionName\":.*$/\"versionName\": \"$versionName\",/" $JsonPath/$fileName
sed -i '' "s/\"versionCode\":.*$/\"versionCode\": \"$versionCode\",/" $JsonPath/$fileName
sed -i '' "s/\"deleteApp\":.*$/\"deleteApp\": \"$deleteApp\",/" $JsonPath/$fileName
我的文件是以 json 作为格式的,说明文字是可以任意填写的,会触发一些解析问题:
- 不允许出现会造成
JSON.parse
解析失败的符号, 如 \ , ````,\n
,\r
,\"
等等 - 因为说明文字的换行我是通过
#
切割的, 所以也不允许出现这个符号
- 不允许出现会造成
大致流程图
总结
关于 APP 原生版本的更新流程基本就是这样,当然这个流程不光适用 APP, 也可以用于 PC 软件的更新
除了原生版本的更新,还有热更新, 也是非常重要的, 我将会在后面的博客中解析他
完
以上是 React-Native 原生 APP 更新 的全部内容, 来源链接: utcz.com/z/381332.html