优化uni-app更新策略思路

口语描述

  • 全量更新: 全量更新是指发APP包更新,对应的iOS是指发IPA包到appstore,Android发apk包到商城。
  • 增量更新: 指uni-app提供的热更新生成的wgt文件,可以下载完自动安装,然后重启APP即可完成更新(iOS开发者泪奔ing)。
  • 基座: Hybrid框架的打包都是依赖于框架自身的编译环境的。uni-app则是HBuilderX这款IDE。由于HBuilderX的不定时更新(可以看到会修复很多bug),造成了我们发增量包时编译环境的不同。所以,我的原则是,如果发新的增量包,需要保持编译环境一致,不然就需要发全量更新包。

背景描述

原来的更新策略是这样的,用户登录成功后跳转到home主页,然后在onShow方法中做更新检测:

onShow() {
        if (!this.isNew) { // isNew 默认false
            this.isNewVersion()
        }
    },
methods: {
            // 获取最新版本
        isNewVersion() {
            this.$api.getNewVersion().then(res => {
                if (res.is_newest) { //后台告诉我们是新版本,那么将标识符isNew置为true
                    this.isNew = true
                } else { //有新版本,分为增量更新和全量更新
                    // #ifdef APP-PLUS
                    if (res.is_update_now) {//增量更新
                        if (res.down_link !== 'none') {
                            this.checkUpdate(res.down_link)
                        }
                    } else { //全量更新
                        if (uni.getSystemInfoSync().platform == 'ios') {//iOS,提示用户去APP store下载最新
                            tip.showModal('请前往App Store下载最新版本', false)
                        } else {
                            tip.showModal('发现新版本', false).then(res => {//Android,跳转到fir上用户手动点击下载
                                if (res) {
                                    plus.webview.open('https://fir.im/hgxgjzs')
                                }
                            })
                        }
                    }
                    // #endif
                }
            })
        },
        // 下载增量包
        checkUpdate(url) {
            plus.nativeUI.showWaiting('下载更新...')
            plus.downloader
                .createDownload(url, { filename: '_doc/update/' }, (d, status) => {
                    if (status === 200) {
                        this.installWgt(d.filename)
                    } else {
                        plus.nativeUI.alert('下载更新失败!')
                    }
                    plus.nativeUI.closeWaiting()
                })
                .start()
        },
        // 安装增量包
        installWgt(path) {
            plus.nativeUI.showWaiting('安装资源文件中...')
            plus.runtime.install(
                path,
                {},
                () => {
                    plus.nativeUI.closeWaiting()
                    plus.nativeUI.alert('应用资源更新完成!', () => {
                        plus.runtime.restart()
                    })
                },
                e => {
                    plus.nativeUI.closeWaiting()
                    plus.nativeUI.alert('资源更新失败,原因:' + e.message)
                }
            )
        },
}

上面的更新策略代码有不完善的地方,需要改进。为帮助理解,现在结合用户使用场景进行描述。

众所周知,Hybrid框架在Android设备上的体验一直不是很让人满意,表现最明显的就是卡顿现象。在产品交付后,Android用户会遇到一些机型造成的页面崩溃,比如选择日期造成的崩溃和打开网页造成的崩溃:

日期选择器崩溃

打开网页崩溃

一般出现这种问题,我们都需要更新基座来尝试解决,也就是需要发新的全量包(apk 和ipa),让用户从版本A升级到版本B。我们再发增量包时,希望用户从B升级到C。


用户使用版本A-->新更新基座版本B-->新增量包版本C

不过,这只是理想场景。如果用户使用A版本发生了崩溃,而未及时升级到B版本的新基座,而我们此时已经发了增量包版本C。按照现在的代码更新逻辑,用户则直接跳过B 版本,直接从A版本更新到C。这样,如果用户仍旧反馈出现页面崩溃,而告诉我们他当前的版本号已经是C版本,作为开发者,就无法得知用户究竟是否更新过基座B。

优化更新策略

为了避免用户跳过基座的情况出现,新的更新策略加入了基座版本的设定,如果用户想要从A版本更新到C版本,那么就判断用户当前使用的版本是否是指定的基座版本,是的话则直接为用户进行增量更新,不是则需要用户先更新基座版本(也就是全量更新),在为其做增量更新。

代码实现如下:

    onShow() {
        if (!this.hasCheckedVersion) { //默认hasCheckedVersion为false
            //没有检测过版本,则需要检测版本
            this.checkVersion()
        }
    },
    //针对Android用户的返回键,防止更新过程中点击返回按钮,造成下载APK失败
    onBackPress(options) {
        //值为 true 时,才表示不执行默认的返回,自行处理此时的业务逻辑
        return !this.allowBack
    },
    methods: {
            checkVersion() {
            //检测版本号
            // #ifdef APP-PLUS
            this.$api.getNewVersion().then(res => {
                if (res.status === 1) {
                    //是最新版本,标记为已检测,下次onshow不需要重复请求后台进行检测
                    this.hasCheckedVersion = true
                } else if (res.status === 2) {
                    // 非最新版本,且此时用户的基座满足要求, 直接进行在线更新
                    this.readyForUpdate(res.url)
                } else if (res.status === 3) {
                    // 非最新版本,但是此时用户的基座不满足要求,需要先下载APK/IPA
                    if (uni.getSystemInfoSync().platform == 'ios') {
                        tip.showModal('发现了新版本\n即将前往App Store更新', false).then(() => {
                            let url = 'itms-apps://itunes.apple.com/cn/app/id1478581650?mt=8'
                            plus.runtime.openURL(url)
                        })
                    } else {
                        tip.showModal('发现新版本 建议您立即更新', false).then(callback => {
                            tip.showLoading('正在更新 预估耗时1分钟...')
                            this.updateForAndroidAPK(res.url)
                        })
                    }
                }
            })
            // #endif
        },


        // 准备下载最新的增量更新包
        readyForUpdate(url) {
            this.allowBack = false
            plus.nativeUI.showWaiting('正在为您更新资源...')
            plus.downloader
                .createDownload(url, { filename: '_doc/update/' }, (d, status) => {
                    if (status === 200) {
                        this.allowBack = true
                        this.installWgt(d.filename)
                    } else {
                        plus.nativeUI.alert('下载更新失败!')
                    }
                    plus.nativeUI.closeWaiting()
                })
                .start()
        },

        // 安装增量包
        installWgt(path) {
            plus.nativeUI.showWaiting('正在为您安装新的资源...')
            plus.runtime.install(
                path,
                {},
                () => {
                    plus.nativeUI.closeWaiting()
                    plus.nativeUI.alert('应用资源更新完成!', () => {
                        plus.runtime.restart()
                    })
                },
                e => {
                    plus.nativeUI.closeWaiting()
                    plus.nativeUI.alert('资源更新失败,原因:' + e.message)
                }
            )
        },
        //为Android用户自动下载APK
        updateForAndroidAPK(url) {
            this.allowBack = false
            plus.downloader
                .createDownload(url, {}, (d, status) => {
                    tip.hideLoading()
                    if (status === 200) {
                        this.allowBack = true
                        plus.runtime.install(d.filename)
                    } else {
                        plus.nativeUI.alert('下载更新失败!')
                        this.allowBack = true
                    }
                })
                .start()
        },
    }

除了优化了更新策略,还为Android的“呆萌用户”解决了不知道怎么下载apk包这个世纪难题,为iOS用户解决了在APP store查找不到APP这个“奇异bug”。


   转载规则


《优化uni-app更新策略思路》 刘星星 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
在iOS中使用密码填充功能 在iOS中使用密码填充功能
在iOS中使用密码填充功能密码自动填充支持第三方 App 是 iOS 12 中包含的众多新功能之一。为了提高用户体验,越来越多的APP支持了这个功能,现在将这个特性总结一下(虽然貌似有点晚。。。)。 手机设置首先,你需要启用密码自动
2020-01-05
下一篇 
uni-app中wach和computed的使用总结 uni-app中wach和computed的使用总结
最近做uni-app的项目,适配微信小程序.由于现在微信小程序出台了新的登录授权规范,根据规范和业务需求,首页共有3种展示状态:①未登录,②登录并且有全部权限,③登录有部分权限.该小程序的首页为购物车页面,业务相对复杂,考虑到状态的管理,
2019-12-13
  目录