ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【Uniapp】Uniapp 实现 App 版本自动升级

2021-07-26 16:33:11  阅读:1483  来源: 互联网

标签:Uniapp App pro let 版本 plus android notifyId mNotificationBuild


文章目录

一、App 版本自动升级

Uniapp 的官方也提供了 App 升级的支持,升级中心 uni-upgrade-center,但是由于需要使用云端基于 uniCloud 云函数实现,对于项目来说,又多了一个服务端,管理起来较为麻烦,所以我们还是自定义版本自动升级,也方便实际开发中进行调整

此模块的代码可能还要调整一下,感觉比较繁琐

1、Uniapp 前端实现

定义消息通知

//js文件格式是GBK,在调用时手机上汉字会出现乱码,请大家在使用时复制粘贴一份,另存为UTF-8的文件格式,注意其中汉字显示是否正常,注释信息无所谓
//util.js原来是GRK格式(通知栏中会显示乱码),需重新复制一份,uniapp中js文件默认是UFT-8格式:将复制后的js文件中的中文乱码修改即可使用
//在通知栏显示下载进度条
export default function NotificationUtil() {
    let defaultTitle = '通知栏标题';
    let defaultContent = '通知内容';
    let defaultTicker = '通知提示';
    let defaultNotifyId = 1000;
    let defaultNumber = 1;

    /**
     * @description 比较两个版本大小
     * 比较版本大小,如果新版本nowVersion大于旧版本OldResourceVersion则返回true,否则返回false
     */
    function compareVersion(OldVersion, nowVersion) {
        if (!OldVersion || !nowVersion || OldVersion == '' || nowVersion == '') {
            return false;
        }
        //第二份参数 是 数组的最大长度
        let OldVersionA = OldVersion.split(".", 4);
        let nowVersionA = nowVersion.split(".", 4);
        for (let i = 0; i < OldVersionA.length && i < nowVersionA.length; i++) {
            let strOld = OldVersionA[i];
            let numOld = parseInt(strOld);
            let strNow = nowVersionA[i];
            let numNow = parseInt(strNow);
            //小版本到高版本
            if (numNow > numOld /*||strNow.length>strOld.length*/) {
                return true;
            } else if (numNow < numOld) {
                return false;
            }
        }
        //如果是版本  如 1.6 - 1.6.1
        if (nowVersionA.length > OldVersionA.length && 0 == nowVersion.indexOf(OldVersion)) {
            return true;
        }
    };

    /**
     * @constructor 创建通知栏进度条构造函数
     */
    function NotificationCustom() {
        if (plus.os.name != 'Android') {
            return;
        }
        //当前版本号
        let SystemVersion = plus.os.version;
        let Context = plus.android.importClass("android.content.Context");
        let main = plus.android.runtimeMainActivity();
        let NotificationManager = plus.android.importClass("android.app.NotificationManager");
        let nm = main.getSystemService(Context.NOTIFICATION_SERVICE)
        // Notification build 要android api16以上才能使用(4.1.2以上)
        let Notification = null;
        if (compareVersion('4.1.1', SystemVersion) == true) {
            Notification = plus.android.importClass("android.app.Notification");
        } else {
            Notification = plus.android.importClass("android.support.v4.app.NotificationCompat");
        }
        if (Notification) {
            // this.notifyManager = nm;
            // this.mNotificationBuild = new Notification.Builder(main);

            let Build = plus.android.importClass("android.os.Build");
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//android8.0及以上需设置通知渠道才能显示通知
                //创建通知渠道
                let name = "渠道名称1";
                let description = "渠道描述1";
                let channelId = "channelId1";//渠道id
                // let importance = NotificationManager.IMPORTANCE_DEFAULT;//重要性级别
                let importance = NotificationManager.IMPORTANCE_HIGH;//重要性级别
                let NotificationChannel = plus.android.importClass("android.app.NotificationChannel");
                let mChannel = new NotificationChannel(channelId, name, importance);
                // let mChannel = new NotificationChannel("channelId1", "渠道名称1", importance);
                mChannel.setDescription(description);//渠道描述
                mChannel.setDescription("渠道描述1");//渠道描述
                mChannel.enableLights(true);//是否显示通知指示灯
                mChannel.enableVibration(true);//是否振动
                nm.createNotificationChannel(mChannel);//创建通知渠道

                this.notifyManager = nm;
                this.mNotificationBuild = new Notification.Builder(main, channelId);
                // this.mNotificationBuild = new Notification.Builder(main,"channelId1");
            } else {
                this.notifyManager = nm;
                this.mNotificationBuild = new Notification.Builder(main);
            }

            /*
            mBuilder.setContentTitle("测试标题")//设置通知栏标题
                    .setContentText("测试内容") //设置通知栏显示内容
                    .setContentIntent(getDefalutIntent(Notification.FLAG_AUTO_CANCEL)) //设置通知栏点击意图
                    //  .setNumber(number) //设置通知集合的数量
                    .setTicker("测试通知来啦") //通知首次出现在通知栏,带上升动画效果的
                    .setWhen(System.currentTimeMillis())//通知产生的时间,会在通知信息里显示,一般是系统获取到的时间
                    .setPriority(Notification.PRIORITY_DEFAULT) //设置该通知优先级
                    //  .setAutoCancel(true)//设置这个标志当用户单击面板就可以让通知将自动取消
                    .setOngoing(false)//ture,ַ设置他为一个正在进行的通知。他们通常是用来表示一个后台任务,用户积极参与(如播放音乐)或以某种方式正在等待,因此占用设备(如一个文件下载,同步操作,主动网络连接)
                    .setDefaults(Notification.DEFAULT_VIBRATE)//向通知添加声音、闪灯和振动效果的最简单、最一致的方式是使用当前的用户默认设置,使用defaults属性,可以组合
                    //Notification.DEFAULT_ALL  Notification.DEFAULT_SOUND 添加声音 // requires VIBRATE permission
                    .setSmallIcon(R.drawable.ic_launcher);//设置通知小ICON
            */
            //设为true代表常驻状态栏
            this.mNotificationBuild.setOngoing(false);
            this.mNotificationBuild.setContentTitle(defaultTitle);
            this.mNotificationBuild.setContentText(defaultContent);
            this.mNotificationBuild.setTicker(defaultTicker);
            //默认的push图标
            // this.mNotificationBuild.setSmallIcon(17301620);//设置小图标
            //https://www.cnblogs.com/penghuster/p/4909930.html
            let R = plus.android.importClass("android.R");
            this.mNotificationBuild.setSmallIcon(R.drawable.stat_sys_download);
            //设置默认声音
            // console.log('默认:'+plus.android.importClass("android.app.Notification").DEFAULT_SOUND);
            this.mNotificationBuild.setDefaults(plus.android.importClass("android.app.Notification").DEFAULT_SOUND);
            //this.mNotificationBuild.setNumber(defaultNumber)
        }
    };
    /**
     * @description 给android通知栏发送通知
     * @param {String} title 标题
     * @param {String} content  内容
     * @param {String} tickerTips  提示
     * @param {Number} notifyId id,默认为1000
     */
    NotificationCustom.prototype.setNotification = function (title, content, tickerTips, notifyId) {
        if (this.mNotificationBuild == null ||
            this.notifyManager == null) {
            return;
        }
        notifyId = (typeof (notifyId) == 'number') ? notifyId : defaultNotifyId;
        title = title || defaultTitle;
        content = content || defaultContent;
        tickerTips = tickerTips || defaultTicker;
        this.mNotificationBuild.setContentTitle(title);
        this.mNotificationBuild.setContentText(content);
        this.mNotificationBuild.setTicker(tickerTips);
        //默认有声音
        this.mNotificationBuild.setDefaults(plus.android.importClass("android.app.Notification").DEFAULT_SOUND);
        this.notifyManager.notify(notifyId, this.mNotificationBuild.build());
    };
    /**
     * @description 设置进度条
     * @param {Number} progress
     * @param {String} title 标题
     * @param {String} content  内容
     * @param {String} tickerTips  提示
     * @param {Number} notifyId id,默认为1000
     */
    NotificationCustom.prototype.setProgress = function (progress, title, content, tickerTips, notifyId) {
        if (this.mNotificationBuild == null ||
            this.notifyManager == null) {
            return;
        }
        notifyId = (typeof (notifyId) == 'number') ? notifyId : defaultNotifyId;
        title = title || 'APP更新包';
        content = content || '正在下载...【' + progress + '%】';
        tickerTips = tickerTips || '进度提示';
        this.mNotificationBuild.setContentTitle(title);
        this.mNotificationBuild.setContentText(content);
        this.mNotificationBuild.setTicker(tickerTips);
        /*
        如果为确定的进度条:调用setProgress(max, progress, false)来设置通知,在更新进度的时候在此发起通知更新progress,并且在下载完成后要移除进度条,通过调用setProgress(0, 0, false)既可。
        如果为不确定(持续活动)的进度条,这是在处理进度无法准确获知时显示活动正在持续,所以调用setProgress(0, 0, true) ,操作结束时,调用setProgress(0, 0, false)并更新通知以移除指示条
        */
        //进度条显示时,默认无声音
        this.mNotificationBuild.setDefaults(0);
        this.mNotificationBuild.setProgress(100, progress, false);
        this.notifyManager.notify(notifyId, this.mNotificationBuild.build());
    };
    /**
     * @description 完成进度条
     * @param {String} title 标题
     * @param {String} content  内容
     * @param {String} tickerTips  提示
     * @param {Number} notifyId id,默认为1000
     */
    NotificationCustom.prototype.compProgressNotification = function (title, content, tickerTips, notifyId) {
        if (this.mNotificationBuild == null ||
            this.notifyManager == null) {
            return;
        }
        notifyId = (typeof (notifyId) == 'number') ? notifyId : defaultNotifyId;
        title = title || 'APP更新包';
        content = content || '下载完毕!';
        tickerTips = tickerTips || '进度提示';

        let R = plus.android.importClass("android.R");
        this.mNotificationBuild.setSmallIcon(R.drawable.stat_sys_download_done);

        this.mNotificationBuild.setContentTitle(title);
        this.mNotificationBuild.setContentText(content);
        this.mNotificationBuild.setTicker(tickerTips);
        this.mNotificationBuild.setProgress(0, 0, false);//移除进度条
        //默认有声音
        this.mNotificationBuild.setDefaults(plus.android.importClass("android.app.Notification").DEFAULT_SOUND);
        this.notifyManager.notify(notifyId, this.mNotificationBuild.build());
    };
    /**
     * @description 清除通知栏信息
     * @param {Number} notifyId id,默认为1000
     */
    NotificationCustom.prototype.clearNotification = function (notifyId) {
        notifyId = (typeof (notifyId) == 'number') ? notifyId : defaultNotifyId;
        if (this.notifyManager) {
            this.notifyManager.cancel(notifyId);
        }
    };
    /**
     * @description 清除所有通知栏信息
     */
    NotificationCustom.prototype.clearAllNotification = function () {
        if (this.notifyManager) {
            this.notifyManager.cancelAll();
        }
    };
    return new NotificationCustom();
}

定义版本工具

import NotifiUtil from 'notification.js'

const downloadAndHandleWgtFile = function (url) {
    uni.downloadFile({
        url: url,
        success: (downloadResult) => {
            console.log("downLoadFile:" + JSON.stringify(downloadResult));
            if (downloadResult.statusCode === 200) {
                plus.runtime.install(downloadResult.tempFilePath, {
                    force: false
                }, function () {
                    console.log('install success...');
                    plus.runtime.restart();
                }, function (e) {
                    console.error('install fail...');
                });
            }
        }
    });
}

const downloadAndHandleApkFile = function (url) {
    plus.nativeUI.showWaiting("下载中...");
    let dtask = plus.downloader.createDownload(url, {}, function (d, status) {
        if (status == 200) { // 下载成功
            let path = d.filename;
            console.log("path:" + d.filename);
            plus.runtime.install(path);  // 安装下载的apk文件
        } else {//下载失败
            alert("Download failed: " + status);
        }
        plus.nativeUI.closeWaiting();
    });
    dtask.start();
}

//版本升级:实现通知栏显示下载进度
const downloadApkAndShowProgressForUpdate = function (url) {
    // if(!hasNetwork()){
    // 	return;
    // }
    uni.showToast({
        title: '下载中...',
        icon: 'none'
    });

    //Uni.Push:标准基座下可直接运行;自定义基座或正式包,需在manifest.json添加Uni.Push模块并配置
    // plus.push.createMessage( "下载中...", "jack",{cover:true,sound:"none",title:"标题",subtitle:"副标题"});//仅在通知栏显示开始和结束提醒:不会引起操作界面卡顿

    let appname = '杰克物联';
    let NotificationUtil = NotifiUtil();//实例创建
    NotificationUtil.setNotification(appname, "开始下载! ");
    //dtask就是plus.createDownload
    // let url = "https://www.uchat.com.cn/app/iot/JackIot.apk";
    let dtask = plus.downloader.createDownload(url);// POST请求提交数据
    dtask.start();
    let arr = [{
        pro: 1,
        isFirst: true
    }, {
        pro: 10,
        isFirst: true
    }, {
        pro: 30,
        isFirst: true
    }, {
        pro: 50,
        isFirst: true
    }, {
        pro: 70,
        isFirst: true
    }, {
        pro: 90,
        isFirst: true
    }]
    dtask.addEventListener("statechanged", async function (task, status) {
        switch (task.state) {
            case undefined: //下载任务未开始
            case 0: //下载任务开始调度
            case 1: //下载任务开始请求
            case 2:
                break; //下载任务请求已经接收
            case 3: // 已接收到数据
                // NotificationUtil.setProgress(Math.round(task.downloadedSize/task.totalSize*100),appname);//通知栏中实时更新进度条会引起操作界面卡顿:一秒执行40多次(监听函数),导致UI操作阻塞
                let pro = parseInt(task.downloadedSize / task.totalSize * 100);
                // if(pro == 1 || pro == 10 || pro == 30 || pro == 50 || pro == 70 || pro == 90){//优化方案一:仅在指定整数进度时更新
                // 	console.log("进度:" + pro);
                // 	NotificationUtil.setProgress(pro,appname);
                // }
                switch (pro) {//优化方案二:仅进度第一次出现(1,10,30,50,70,90)时刷新
                    case arr[0].pro:
                        if (arr[0].isFirst) {
                            console.log("进度:" + pro);
                            NotificationUtil.setProgress(pro, appname);
                            arr[0].isFirst = false;
                        }
                        break;
                    case arr[1].pro:
                        if (arr[1].isFirst) {
                            console.log("进度:" + pro);
                            NotificationUtil.setProgress(pro, appname);
                            arr[1].isFirst = false;
                        }
                        break;
                    case arr[2].pro:
                        if (arr[2].isFirst) {
                            console.log("进度:" + pro);
                            NotificationUtil.setProgress(pro, appname);
                            arr[2].isFirst = false;
                        }
                        break;
                    case arr[3].pro:
                        if (arr[3].isFirst) {
                            console.log("进度:" + pro);
                            NotificationUtil.setProgress(pro, appname);
                            arr[3].isFirst = false;
                        }
                        break;
                    case arr[4].pro:
                        if (arr[4].isFirst) {
                            console.log("进度:" + pro);
                            NotificationUtil.setProgress(pro, appname);
                            arr[4].isFirst = false;
                        }
                        break;
                    case arr[5].pro:
                        if (arr[5].isFirst) {
                            // console.log("进度:" + pro);
                            NotificationUtil.setProgress(pro, appname);
                            arr[5].isFirst = false;
                        }
                        break;
                    default:
                        break;
                }
                break;
            case 4: // 下载完成
                console.log("Download success: " + task.filename);
                NotificationUtil.compProgressNotification(appname, "下载完成! ");
                // let ins = plus.runtime.install(plus.io.convertLocalFileSystemURL(task.filename), {force: force},()=>{
                // 	uni.showToast({icon:'none',title:'安装成功!'});
                // 	NotificationUtil.clearNotification();
                // },(e)=>{
                // 	uni.showToast({icon:'none',title:'安装失败!'});
                // 	NotificationUtil.clearNotification();
                // })
                plus.runtime.install(task.filename);  // 安装下载的apk文件
                break;
            default: //5: (Number 类型 )下载任务已暂停 -1: (Number 类型 )枚举任务状态
                console.log("Download failed: " + status);
                NotificationUtil.compProgressNotification(appname, "下载失败! ");
                break;
        }
    });
}


export default {
    downloadAndHandleWgtFile,
    downloadAndHandleApkFile,
    downloadApkAndShowProgressForUpdate
}

Page/launch.vue文件中实现更新

<template>
    <view>
    </view>
</template>

<script>
    import request from "@/utils/common.js";

    export default {
        data() {
            return {
                
            }
        },
        onl oad() {
            let that = this;

            uni.getSystemInfo({
                success: (res) => {
                    if (res.platform == "android") {
                        let version = plus.runtime.version;

                        // 检查版本号是不是当前版本
                        request.request('/app/versionConfig/getCurrent', {}, 'get', function (res) {
                            if (res.code == 200) {
                                if (res.data.versionCode != version) {
                                    if (plus.networkinfo.getCurrentType() != 3) {
                                        uni.showToast({
                                            title: '有新的版本发布,检测到您目前非Wifi连接,为节约您的流量,程序已停止自动更新,将在您连接WIFI之后重新检测更新。',
                                            mask: false,
                                            duration: 5000,
                                            icon: "none"
                                        });
                                        return;
                                    }
                                    uni.showToast({
                                        title: '有新的版本发布,检测到您目前为Wifi连接,程序已启动自动更新。新版本下载完成后将自动弹出安装程序。',
                                        mask: false,
                                        duration: 5000,
                                        icon: "none"
                                    });
                                    let dtask = plus.downloader.createDownload(that.$config.baseUrl + res.data.versionUrl, {}, function (d, status) {
                                        // 下载完成
                                        if (status == 200) {
                                            plus.runtime.install(plus.io.convertLocalFileSystemURL(d.filename), {}, {}, function (error) {
                                                uni.showToast({
                                                    title: '安装失败',
                                                    mask: false,
                                                    duration: 2000
                                                });
                                            })
                                        } else {
                                            uni.showToast({
                                                title: 'App升级包下载失败',
                                                mask: false,
                                                duration: 2000
                                            });
                                        }
                                    });
                                    dtask.start();
                                }
                            } else {
                                that.$msg(res.msg);
                            }
                        });
                    }
                }
            });
        }
    }
</script>

2、SpringBoot 后端实现

我的后端表格设计比较简单,是否当前版本设计的比较简单,方便回滚版本
在这里插入图片描述
相应的SQL语句

drop table if exists biz_version_config;

/*==============================================================*/
/* Table: biz_version_config                                     */
/*==============================================================*/
create table biz_version_config
(
   id                   int(11) not null auto_increment comment '主键',
   version_code         varchar(255) comment '版本号',
   version_url          varchar(255) comment '下载地址',
   version_remark       varchar(255) comment '版本备注',
   is_current           int(11) comment '当前版本 1-是 2-否',
   create_by            varchar(64) default '' comment '创建者',
   create_time          datetime comment '创建时间',
   update_by            varchar(64) default '' comment '更新者',
   update_time          datetime comment '更新时间',
   status               tinyint(1) comment '状态 1-正常2-删除',
   primary key (id)
)
ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COMMENT = 'app版本配置' ROW_FORMAT = Dynamic;

alter table wm_version_config comment 'app版本配置';

然后就是后端的版本管理CRUD,这里就不在一一阐述了,还要增加有个接口,查询当前版本,条件直接使用

where is_current = 1

微信公众号

每天Get一个小技巧

标签:Uniapp,App,pro,let,版本,plus,android,notifyId,mNotificationBuild
来源: https://blog.csdn.net/qq_38762237/article/details/119111330

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有