当一个 APP 在运行的时候, 开发者想要将本人的代码更新到用户的手机上时, 个别都有两种计划, 一是热更新, 二就是 APP 更新.
热更新暂且不说, 这篇文章就讲讲 APP 如何更新
App 更新流程
- 在 App 关上时申请接口或文件, 获取 近程版本 / 版本更新阐明 / 地址 等等重用信息
- 通过库或者原生计划, 获取 App 的以后版本
- 比拟近程版本和以后版本的区别(能够应用库, 也能够本人写一个比拟计划)
- 通过获取到的链接进行操作(能够跳转到对应网站下载, 相似蒲公英这种, 能够是 apk 链接, 通过安卓原生办法下载, 也能够是 App Store 链接)
大抵的流程图
具体阐明:
- 这些近程信息, 能够是接口, 这样能够有一个中台来管制, 当然也能够是一个文件, 让运维来管制
对于信息, 不止于近程版本, 在我的项目里还能够增加其余属性, 如:versionCode
,versionCodeSwitch
,notUpdate
,deleteApp
1.1versionCode
通过 code 来降级版本, 个别是一个数字 (在 ios 里提交 App Store 的时候有须要用到的中央), 这样versionName
并不会减少, 然而如果增加了versionCode
, 如果要降级versionName
,versionCode
也须要减少
1.2versionCodeSwitch
用来管制是否要依据versionCode
来更新, 个别我都是在测试和其余环境开启, 生产环境敞开的
1.3notUpdate
是否要依据近程信息来更新, 个别都是开启状态
1.3deleteApp
安卓 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 软件的更新
除了原生版本的更新, 还有热更新, 也是十分重要的, 我将会在前面的博客中解析他
完