发新帖

[uni-app] [Android] uniapp开发打包的app远程升级功能的实现

零下一度 1月前 103

一、首先是uniapp 生成apk

用Hbuilder 进行打包,可以选择云打包。也可以使用自有证书,目测比直接使用云证书要快一些。


二、实现过程

1.准备update.json文件

准备update.json文件,供前端访问升级信息用。

{
 
  "versionCode": 101,
  "versionName": "v1.0.1",
  "versionDesc": "功能优化",
  "appName": "android_myapp.apk",
  "downloadUrl": "http://111.xx.xx.xx:8000/static/release/"
}


2. 客户端检查更新

在客户端,你可以使用uni.request方法来请求这个JSON文件,并对比当前版本和最新版本。如果发现新版本,提示用户更新。

export default {
    methods: {
        checkForUpdates() {
            const currentVersion = '1.1.0'; // 当前应用版本
            uni.request({
                url: 'https://your-update-server.com/update.json',
                success(res) {
                    const latestVersion = res.data.version;
                    const updateUrl = res.data.url;
                    const updateDescription = res.data.description;
 
                    if (latestVersion > currentVersion) {
                        uni.showModal({
                            title: '发现新版本',
                            content: updateDescription,
                            confirmText: '立即更新',
                            success(res) {
                                if (res.confirm) {
                                    this.downloadAndInstallUpdate(updateUrl);
                                }
                            }
                        });
                    } else {
                        uni.showToast({
                            title: '已是最新版本',
                            icon: 'none'
                        });
                    }
                },
                fail(err) {
                    console.error('检查更新失败', err);
                }
            });
        },
        downloadAndInstallUpdate(updateUrl) {
            // 下载并安装更新包的逻辑
        }
    },
    onLoad() {
        this.checkForUpdates();
    }
}


3. 下载更新包

下载更新包可以使用uni.downloadFile方法。下载完成后,保存文件到本地。

downloadAndInstallUpdate(updateUrl) {
    const downloadTask = uni.downloadFile({
        url: updateUrl,
        success(res) {
            if (res.statusCode === 200) {
                const filePath = res.tempFilePath;
                this.installUpdate(filePath);
            } else {
                console.error('下载更新包失败');
            }
        },
        fail(err) {
            console.error('下载更新包失败', err);
        }
    });
 
    downloadTask.onProgressUpdate((res) => {
        console.log('下载进度', res.progress);
        console.log('已经下载的数据长度', res.totalBytesWritten);
        console.log('预期需要下载的数据总长度', res.totalBytesExpectedToWrite);
    });
}


4. 安装更新包

安装更新包的逻辑会根据不同的平台有所不同。以下是Android平台上的示例:

installUpdate(filePath) {
    plus.runtime.install(filePath, {
        force: false,
        success(res) {
            console.log('安装成功');
            plus.runtime.restart();
        },
        fail(err) {
            console.error('安装失败', err);
        }
    });
}


三、详细实现

 index.vue首页界面增加使用个popup弹出组件,用来展示升级进度。

<template>
<view class="content">
<!-- 升级弹出框 -->
   <uni-popup ref="popup" type="center" :mask-click="false" catchTouchMove>
<view class="popup-content">
        <text>{{percentText}}</text>
    <progress  :percent="progress" :stroke-width="10" />
    </view>
   </uni-popup>
</view>
</template>
 
<style scoped>
 
.popup-content {
  text-align: center;
  max-width: 500rpx; /* 限定最大高度 */
  overflow:hidden;
  background-color: #00aaff;
  border-radius: 10rpx; 
  padding: 50px;
}
</style>

在checkVersion()方法里,调用接口checkUpdate去请求后台的update.json文件,获取升级信息。

// 检查更新
export const checkUpdate = async (verCode,verName) => {
  try {
console.log('checkUpdate request');
    const response = await uni.$http.get('/update.json',{verCode:verCode,verName:verName});
console.log(response);
    if (response.statusCode !== 200) {
      uni.showToast({
        title: '数据请求失败! ',
        duration: 1500,
        icon: 'none',
      });
      return [];
    }
    return response.data;
  } catch (error) {
    console.error('Network request failed:', error);
    uni.showToast({
      title: '网络请求失败! ',
      duration: 1500,
      icon: 'none',
    });
    return [];
  }
};

比较版本号,判断是否应该升级。需要升级则使用uni.showModal弹出提示框。

if(verCode < result.versionCode){
// 更新提醒
uni.showModal({
title: '发现新版本,是否更新',
content: '待更新版本:' + result.versionName +'\n更新说明:'+result.versionDesc,
success: res => {
// 点击确定
if (res.confirm) {
// 打开手机自带浏览器下载
//plus.runtime.openURL(result.downloadUrl+result.appName)
this.downloadUrl = result.downloadUrl+result.appName
console.log('downloadUrl:'+this.downloadUrl)
//显示升级进度
this.showPopup();
if (this.downloadUrl) {
this.isStartDownload = true
//开始下载App
downloadApp(this.downloadUrl, current => {
//下载进度
this.progress = current
}).then(fileName => {
//下载完成
this.isDownloadFinish = true
this.fileName = fileName
if (fileName) {
//自动安装App
this.handleInstallApp()
}
}).catch(e => {
console.log(e, 'e')
})
} else {
uni.showToast({
title: '下载链接不存在',
icon: 'none'
})
}
//this.startProgress();
}
}
});
}

完整实现

<script>
import TodayBoxOffice from '@/components/TodayBoxOffice.vue';
import { downloadApp,installApp } from '@/utils/upgrade.js';
export default {
components: {
    TodayBoxOffice
  },
data() {
return {
//升级相关
downloadUrl: '', //APP下载链接
isDownloadFinish: false, //是否下载完成
progress: 0,  //升级进度条百分比
fileName: '', //下载后app本地路径名称
}
},
async onLoad() {
console.log("onLoad")
//console.log(this.swiperList)
},
computed: {
//百分比文字
percentText() {
let percent = this.progress;
if (typeof percent !== 'number' || isNaN(percent)) return '正在升级中...'
if (percent < 100) return `正在升级中... ${percent}%`
return '下载完成'
}
},
        methods: {
checkVersion(){
console.log("checkVersion:")
// 获取本地应用资源版本号
let version = "1.0.0"
let verCode = 0
const systemInfo = uni.getSystemInfoSync();
// 应用程序版本号
// #ifdef APP
version = plus.runtime.version;
verCode = plus.runtime.versionCode;
// #endif
// #ifdef H5
version = systemInfo.appVersion;
// #endif
                console.log(version)
checkUpdate(verCode,version).then(result => {
console.log("checkUpdate,result:");
console.log(result);
if(verCode < result.versionCode){
// 更新提醒
uni.showModal({
title: '发现新版本,是否更新',
content: '待更新版本:' + result.versionName +'\n更新说明:'+result.versionDesc,
success: res => {
// 点击确定
if (res.confirm) {
// 打开手机自带浏览器下载
//plus.runtime.openURL(result.downloadUrl+result.appName)
this.downloadUrl = result.downloadUrl+result.appName
console.log('downloadUrl:'+this.downloadUrl)
//显示升级进度
this.showPopup();
if (this.downloadUrl) {
this.isStartDownload = true
//开始下载App
downloadApp(this.downloadUrl, current => {
//下载进度
this.progress = current
}).then(fileName => {
//下载完成
this.isDownloadFinish = true
this.fileName = fileName
if (fileName) {
//自动安装App
this.handleInstallApp()
}
}).catch(e => {
console.log(e, 'e')
})
} else {
uni.showToast({
title: '下载链接不存在',
icon: 'none'
})
}
//this.startProgress();
}
}
});
}
})
},
showPopup() {
    this.$refs.popup.open();
},
//安装app
handleInstallApp() {
//下载完成才能安装,防止下载过程中点击
if (this.isDownloadFinish && this.fileName) {
installApp(this.fileName, () => {
//安装成功,关闭升级弹窗
 this.$refs.popup.close();
})
}
}
},
}
</script>


/**
 * @description H5+下载App
 * @param downloadUrl:App下载链接
 * @param progressCallBack:下载进度回调
 */
export const downloadApp = (downloadUrl, progressCallBack = () => {}, ) => {
return new Promise((resolve, reject) => {
//创建下载任务
const downloadTask = plus.downloader.createDownload(downloadUrl, {
method: "GET"
}, (task, status) => {
console.log(status,'status')
if (status == 200) { //下载成功
resolve(task.filename)
 
} else {
reject('fail')
uni.showToast({
title: '下载失败',
duration: 1500,
icon: "none"
});
}
})
//监听下载过程
downloadTask.addEventListener("statechanged", (task, status) => {
switch (task.state) {
case 1: // 开始  
break;
case 2: //已连接到服务器  
break;
case 3: // 已接收到数据  
let hasProgress = task.totalSize && task.totalSize > 0 //是否能获取到App大小
if (hasProgress) {
let current = parseInt(100 * task.downloadedSize / task.totalSize); //获取下载进度百分比
progressCallBack(current)
}
break;
case 4: // 下载完成       
break;
}
});
//开始执行下载
downloadTask.start();
})
 
 
}
/**
 * @description H5+安装APP
 * @param fileName:app文件名
 * @param callBack:安装成功回调
 */
export const installApp = (fileName, callBack = () => {}) => {
//注册广播监听app安装情况
onInstallListening(callBack);
//开始安装
plus.runtime.install(plus.io.convertLocalFileSystemURL(fileName), {}, () => {
//成功跳转到安装界面
}, function(error) {
uni.showToast({
title: '安装失败',
duration: 1500,
icon: "none"
});
})
 
}
/**
 * @description 注册广播监听APP是否安装成功
 * @param callBack:安装成功回调函数
 */
const onInstallListening = (callBack = () => {}) => {
 
let mainActivity = plus.android.runtimeMainActivity(); //获取activity
//生成广播接收器
let receiver = plus.android.implements('io.dcloud.android.content.BroadcastReceiver', {
onReceive: (context, intent) => { //接收广播回调  
plus.android.importClass(intent);
mainActivity.unregisterReceiver(receiver); //取消监听
callBack()
}
});
let IntentFilter = plus.android.importClass('android.content.IntentFilter');
let Intent = plus.android.importClass('android.content.Intent');
let filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_ADDED); //监听APP安装     
filter.addDataScheme("package");
mainActivity.registerReceiver(receiver, filter); //注册广播
 
}


@CSDN@特立独行的猫a










最新回复 (0)
返回
零下一度
主题数
931
帖子数
0
注册排名
1