乐趣区

关于前端:ReactNative-原生-APP-更新

当一个 APP 在运行的时候, 开发者想要将本人的代码更新到用户的手机上时, 个别都有两种计划, 一是热更新, 二就是 APP 更新.
热更新暂且不说, 这篇文章就讲讲 APP 如何更新

App 更新流程

  1. 在 App 关上时申请接口或文件, 获取 近程版本 / 版本更新阐明 / 地址 等等重用信息
  2. 通过库或者原生计划, 获取 App 的以后版本
  3. 比拟近程版本和以后版本的区别(能够应用库, 也能够本人写一个比拟计划)
  4. 通过获取到的链接进行操作(能够跳转到对应网站下载, 相似蒲公英这种, 能够是 apk 链接, 通过安卓原生办法下载, 也能够是 App Store 链接)

大抵的流程图

具体阐明:

  1. 这些近程信息, 能够是接口, 这样能够有一个中台来管制, 当然也能够是一个文件, 让运维来管制
    对于信息, 不止于近程版本, 在我的项目里还能够增加其余属性, 如: 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, 再从新下载
  2. 获取以后手机的信息, 计划较多, 我应用的是 react-native-device-info 这个库, 这个库外面提供的信息较全, 当然也能够应用原生办法, 来获取 APP 的信息
  3. 对于本地版本号和原生版本号之间的比照也是能够应用库, 也能够本人写, 这边举荐两个库, 下载量都是百万以上的: 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
    }
  4. 对于下载 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 脚本更新对应信息(有须要也能够应用其余语言脚本):

  1. 首先定义须要获取的文件地址

    androidVersionFilePath="$WORKSPACE/android/app/build.gradle"  // 通过此文件获取安卓的版本信息
    iosVersionFilePath="$WORKSPACE/ios/veronica/Info.plist" // 通过此文件获取 iOS 的版本信息
    changeLogPath="$WORKSPACE/change.log" // 将版本更新信息存储在此文件中
  2. 通过文件地址, 获取打完包后的版本信息

    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" "#")
  3. 替换现有文件中的信息:

    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 软件的更新
除了原生版本的更新, 还有热更新, 也是十分重要的, 我将会在前面的博客中解析他

退出移动版