概要 需求分析 技術(shù)實(shí)現(xiàn)梳理 1.是否更新判斷: 2.升級(jí)彈窗的展示 3.根據(jù)升級(jí)類型限制操作 4.下載APP監(jiān)聽下載進(jìn)度 5.下載完自動(dòng)安裝 核心API講解 1.plus.downloader.createDownload(url,options,completedCallback)(下載) 2.plus.runtime.install(安裝APP) 代碼實(shí)現(xiàn) 效果演示 注意事項(xiàng) 概要 本文主要講述uniapp APP在線升級(jí)功能實(shí)現(xiàn),并用代碼演示包括強(qiáng)制升級(jí)、可選升級(jí)、下載進(jìn)度顯示、下載自動(dòng)安裝等功能,示例代碼已經(jīng)過測(cè)試可結(jié)合實(shí)際開發(fā)場(chǎng)景做調(diào)整直接引入使用 需求分析 要求: 1.打開APP自動(dòng)檢測(cè)是否有最新版本,如有彈窗提示下載更新 2.升級(jí)類型分為可選更新,強(qiáng)制更新,可選更新用戶可以選擇關(guān)閉不更新情況下繼續(xù)使用APP,強(qiáng)制更新用戶無法關(guān)閉更新窗口,無法使用任何功能,必須在線升級(jí)后才能使用 3.下載過程進(jìn)度條顯示下載進(jìn)度 4.下載完成自動(dòng)跳轉(zhuǎn)安裝界面,用戶取消安裝還能繼續(xù)手動(dòng)點(diǎn)擊安裝 技術(shù)實(shí)現(xiàn)梳理 1.是否更新判斷: 通過接口獲取線上最新版本號(hào)(默認(rèn)規(guī)定版本號(hào)為正整數(shù))與本地APP版本號(hào)進(jìn)行比較大小,當(dāng)線上最新版本號(hào)大于本地版本號(hào)就需要更新。本地App版本可在每次發(fā)版時(shí)候在manifest.json-基礎(chǔ)配置-應(yīng)用版本號(hào)進(jìn)行設(shè)置 2.升級(jí)彈窗的展示 升級(jí)彈窗實(shí)現(xiàn)有2種方案,一種直接在首頁(yè)里嵌套彈窗組件,另一種是把彈窗放置在獨(dú)立的頁(yè)面,并把頁(yè)面窗口設(shè)置透明,當(dāng)需要升級(jí)的時(shí)候直接從首頁(yè)進(jìn)入,從視覺效果上看就相當(dāng)于在首頁(yè)上的懸浮窗口??紤]到后續(xù)有強(qiáng)制更新頁(yè)面不能返回等操作,便于維護(hù)本案例將采用第二種方案 3.根據(jù)升級(jí)類型限制操作 當(dāng)升級(jí)類型為強(qiáng)制升級(jí)意味著頁(yè)面不能做除了升級(jí)的任何操作,包括返回功能,關(guān)閉彈窗功能,禁止返回可通過onBackPress生命周期函數(shù)處理,彈窗關(guān)閉入口動(dòng)態(tài)控制,包括關(guān)閉按鈕,遮罩層點(diǎn)擊關(guān)閉功能等 4.下載APP監(jiān)聽下載進(jìn)度 通過H5+方式下載 :plus.downloader.createDownload,生成下載任務(wù)對(duì)象(downloadTask),通過downloadTask.addEventListener("statechanged",(task,status)=>{})監(jiān)聽下載進(jìn)度 5.下載完自動(dòng)安裝 通過H5+ plus.runtime.install實(shí)現(xiàn)自動(dòng)安裝,該api只能監(jiān)聽是否打開安裝頁(yè)面,無法監(jiān)測(cè)到apk是否安裝成功,還需要調(diào)用安卓原生注冊(cè)廣播事件,監(jiān)聽apk安裝成功回調(diào) 核心API講解 1.plus.downloader.createDownload(url,options,completedCallback)(下載) 說明: 請(qǐng)求下載管理創(chuàng)建新的下載任務(wù),創(chuàng)建成功則返回Download對(duì)象,用于管理下載任務(wù) 參數(shù): url: ( String ) 必選 要下載文件資源地址 要下載文件的url地址,僅支持網(wǎng)絡(luò)資源地址,支持http或https協(xié)議。 允許創(chuàng)建多個(gè)相同url地址的下載任務(wù)。 注意:如果url地址中包含中文或空格等,需要進(jìn)行urlencode轉(zhuǎn)換。 options: ( DownloadOptions ) 可選 下載任務(wù)的參數(shù) 可通過此參數(shù)設(shè)置下載任務(wù)屬性,如保存文件路徑、下載優(yōu)先級(jí)等。 completedCallback: ( DownloadCompletedCallback ) 可選 下載任務(wù)完成回調(diào)函數(shù) 當(dāng)下載任務(wù)下載完成時(shí)觸發(fā),成功或失敗都會(huì)觸發(fā)。 返回值: Download:新建的下載任務(wù)對(duì)象 2.plus.runtime.install(filePath,options,installSuccessCB,installErrorCB)(安裝APP) 說明: 支持以下類型安裝包: 1. 應(yīng)用資源安裝包(wgt),擴(kuò)展名為'.wgt'; 2. 應(yīng)用資源差量升級(jí)包(wgtu),擴(kuò)展名為'.wgtu'; 3. 系統(tǒng)程序安裝包(apk),要求使用當(dāng)前平臺(tái)支持的安裝包格式。 注意:僅支持本地地址,調(diào)用此方法前需把安裝包從網(wǎng)絡(luò)地址或其他位置放置到運(yùn)行時(shí)環(huán)境可以訪問的本地目錄。 參數(shù): filePath: ( String ) 必選 要安裝的文件路徑 支持應(yīng)用資源安裝包(wgt)、應(yīng)用資源差量升級(jí)包(wgtu)、系統(tǒng)程序包(apk)。 options: ( WidgetOptions ) 可選 應(yīng)用安裝設(shè)置的參數(shù) installSuccessCB: ( InstallSuccessCallback ) 可選 正確安裝后的回調(diào) installErrorCB: ( InstallErrorCallback ) 可選 安裝失敗的回調(diào) 返回值: void : 無 代碼實(shí)現(xiàn) 項(xiàng)目目錄: pages.json: 新增升級(jí)彈窗頁(yè)面路由,設(shè)置窗口透明 { "pages": [ //pages數(shù)組中第一項(xiàng)表示應(yīng)用啟動(dòng)頁(yè),參考:https://uniapp./collocation/pages { "path": "pages/index/index",//首頁(yè) "style": { "navigationBarTitleText": "" } }, { "path": "pages/index/upgrade",//升級(jí)窗口頁(yè)面 "style": { "navigationBarTitleText": "", "navigationStyle": "custom",//導(dǎo)航欄自定義 "app-plus": { "bounce": "none", "animationType":"none",//取消窗口動(dòng)畫 "background": "transparent" // 設(shè)置背景透明 } } } ], "globalStyle": { "navigationBarTextStyle": "black", "navigationBarTitleText": "uni-app", "navigationBarBackgroundColor": "#F8F8F8", "backgroundColor": "#F8F8F8" }, "uniIdRouter": {} } index.vue:(首頁(yè)) 從接口獲取最新版本號(hào),跟本地對(duì)比,判斷是否進(jìn)入升級(jí)彈窗頁(yè) <script> export default { data() { return {} }, onLoad() { this.init() }, methods: { //初始化 init() { // #ifdef APP-PLUS this.checkVersion() // #endif }, //檢查版本更新情況 checkVersion() { //模擬接口獲取最新版本號(hào),版本號(hào)固定為整數(shù) setTimeout(() => { const newVersionName = "V1.2.0" //線上最新版本名 const newVersionCode = 20; //線上最新版本號(hào) const selfVersionCode = Number(uni.getSystemInfoSync().appVersionCode) //當(dāng)前App版本號(hào) //線上版本號(hào)高于當(dāng)前,進(jìn)行在線升級(jí) if (selfVersionCode < newVersionCode) { let platform = uni.getSystemInfoSync().platform //手機(jī)平臺(tái) //安卓手機(jī)彈窗升級(jí) if (platform === 'android') { uni.navigateTo({ url: './upgrade' }) } //IOS無法在線升級(jí)提示到商店下載 else { uni.showModal({ title: '發(fā)現(xiàn)新版本 ' + newVersionName, content: '請(qǐng)到App store進(jìn)行升級(jí)', showCancel: false }) } } }, 200) } } } </script> upgrade.vue(升級(jí)彈窗頁(yè)): 布局樣式可根據(jù)實(shí)際調(diào)整,頂部背景圖upgrade_bg.png自行放入static <template> <view class="upgrade-popup"> <image class="header-bg" src="../../static/upgrade_bg.png" mode="widthFix"></image> <view class="main"> <view class="version">發(fā)現(xiàn)新版本{{versionName}}</view> <view class="content"> <text class="title">更新內(nèi)容</text> <view class="desc" v-html="versionDesc"></view> </view> <!--下載狀態(tài)-進(jìn)度條顯示 --> <view class="footer" v-if="isStartDownload"> <view class="progress-view" :class="{'active':!hasProgress}" @click="handleInstallApp"> <!-- 進(jìn)度條 --> <view v-if="hasProgress" style="height: 100%;"> <view class="txt">{{percentText}}</view> <view class="progress" :style="setProStyle"></view> </view> <view v-else> <view class="btn upgrade force">{{ isDownloadFinish ? '立即安裝' :'下載中...'}}</view> </view> </view> </view> <!-- 強(qiáng)制更新 --> <view class="footer" v-else-if="isForceUpdate"> <view class="btn upgrade force" @click="handleUpgrade">立即更新</view> </view> <!-- 可選擇更新 --> <view class="footer" v-else> <view class="btn close" @click="handleClose">以后再說</view> <view class="btn upgrade" @click="handleUpgrade">立即更新</view> </view> </view> </view> </template> <script> import { downloadApp, installApp } from './upgrade.js' export default { data() { return { isForceUpdate: false, //是否強(qiáng)制更新 versionName: '', //版本名稱 versionDesc: '', //更新說明 downloadUrl: '', //APP下載鏈接 isDownloadFinish: false, //是否下載完成 hasProgress: false, //是否能顯示進(jìn)度條 currentPercent: 0, //當(dāng)前下載百分比 isStartDownload: false, //是否開始下載 fileName: '', //下載后app本地路徑名稱 } }, computed: { //設(shè)置進(jìn)度條樣式,實(shí)時(shí)更新進(jìn)度位置 setProStyle() { return { width: (510 * this.currentPercent / 100) + 'rpx' //510:按鈕進(jìn)度條寬度 } }, //百分比文字 percentText() { let percent = this.currentPercent; if (typeof percent !== 'number' || isNaN(percent)) return '下載中...' if (percent < 100) return `下載中${percent}%` return '立即安裝' } }, onLoad() { this.init() }, onBackPress(options) { // 禁用返回 if (options.from == 'backbutton') { return true; } }, methods: { //初始化獲取最新APP版本信息 init() { //模擬接口獲取 setTimeout(() => { //演示數(shù)據(jù)請(qǐng)根據(jù)實(shí)際修改 this.versionName = 'V1.2.0'; //版本名稱 this.versionDesc = "修復(fù)若干bug"; //更新說明 this.downloadUrl = 'https://xxxxx'; //下載鏈接 this.isForceUpdate = false; //是否強(qiáng)制更新 }, 200) }, //更新 handleUpgrade() { if (this.downloadUrl) { this.isStartDownload = true //開始下載App downloadApp(this.downloadUrl, current => { //下載進(jìn)度監(jiān)聽 this.hasProgress = true this.currentPercent = current }).then(fileName => { //下載完成 this.isDownloadFinish = true this.fileName = fileName if (fileName) { //自動(dòng)安裝App this.handleInstallApp() } }).catch(e => { console.log(e, 'e') }) } else { uni.showToast({ title: '下載鏈接不存在', icon: 'none' }) } }, //安裝app handleInstallApp() { //下載完成才能安裝,防止下載過程中點(diǎn)擊 if (this.isDownloadFinish && this.fileName) { installApp(this.fileName, () => { //安裝成功,關(guān)閉升級(jí)彈窗 uni.navigateBack() }) } }, //關(guān)閉返回 handleClose() { uni.navigateBack() }, } } </script> <style> page { background: rgba(0, 0, 0, 0.5);/**設(shè)置窗口背景半透明*/ } </style> <style lang="scss" scoped> .upgrade-popup { width: 580rpx; height: auto; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: #fff; border-radius: 20rpx; box-sizing: border-box; border: 1px solid #eee; } .header-bg { width: 100%; margin-top: -112rpx; } .main { padding: 10rpx 30rpx 30rpx; box-sizing: border-box; .version { font-size: 36rpx; color: #026DF7; font-weight: 700; width: 100%; text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; letter-spacing: 1px; } .content { margin-top: 60rpx; .title { font-size: 28rpx; font-weight: 700; color: #000000; } .desc { box-sizing: border-box; margin-top: 20rpx; font-size: 28rpx; color: #6A6A6A; max-height: 80vh; overflow-y: auto; } } .footer { width: 100%; display: flex; justify-content: center; align-items: center; position: relative; flex-shrink: 0; margin-top: 100rpx; .btn { width: 246rpx; display: flex; justify-content: center; align-items: center; position: relative; z-index: 999; height: 96rpx; box-sizing: border-box; font-size: 32rpx; border-radius: 10rpx; letter-spacing: 2rpx; &.force { width: 500rpx; } &.close { border: 1px solid #E0E0E0; margin-right: 25rpx; color: #000; } &.upgrade { background-color: #026DF7; color: white; } } .progress-view { width: 510rpx; height: 90rpx; display: flex; position: relative; align-items: center; border-radius: 6rpx; background-color: #dcdcdc; display: flex; justify-content: flex-start; padding: 0px; box-sizing: border-box; border: none; overflow: hidden; &.active { background-color: #026DF7; } .progress { height: 100%; background-color: #026DF7; padding: 0px; box-sizing: border-box; border: none; border-top-left-radius: 10rpx; border-bottom-left-radius: 10rpx; } .txt { font-size: 28rpx; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #fff; } } } } </style> upgrade.js(下載、安裝工具類): /** * @description H5+下載App * @param downloadUrl:App下載鏈接 * @param progressCallBack:下載進(jìn)度回調(diào) */ export const downloadApp = (downloadUrl, progressCallBack = () => {}, ) => { return new Promise((resolve, reject) => { //創(chuàng)建下載任務(wù) 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" }); } }) //監(jiān)聽下載過程 downloadTask.addEventListener("statechanged", (task, status) => { switch (task.state) { case 1: // 開始 break; case 2: //已連接到服務(wù)器 break; case 3: // 已接收到數(shù)據(jù) let hasProgress = task.totalSize && task.totalSize > 0 //是否能獲取到App大小 if (hasProgress) { let current = parseInt(100 * task.downloadedSize / task.totalSize); //獲取下載進(jìn)度百分比 progressCallBack(current) } break; case 4: // 下載完成 break; } }); //開始執(zhí)行下載 downloadTask.start(); }) } /** * @description H5+安裝APP * @param fileName:app文件名 * @param callBack:安裝成功回調(diào) */ export const installApp = (fileName, callBack = () => {}) => { //注冊(cè)廣播監(jiān)聽app安裝情況 onInstallListening(callBack); //開始安裝 plus.runtime.install(plus.io.convertLocalFileSystemURL(fileName), {}, () => { //成功跳轉(zhuǎn)到安裝界面 }, function(error) { uni.showToast({ title: '安裝失敗', duration: 1500, icon: "none" }); }) } /** * @description 注冊(cè)廣播監(jiān)聽APP是否安裝成功 * @param callBack:安裝成功回調(diào)函數(shù) */ const onInstallListening = (callBack = () => {}) => { let mainActivity = plus.android.runtimeMainActivity(); //獲取activity //生成廣播接收器 let receiver = plus.android.implements('io.dcloud.android.content.BroadcastReceiver', { onReceive: (context, intent) => { //接收廣播回調(diào) plus.android.importClass(intent); mainActivity.unregisterReceiver(receiver); //取消監(jiān)聽 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); //監(jiān)聽APP安裝 filter.addDataScheme("package"); mainActivity.registerReceiver(receiver, filter); //注冊(cè)廣播 } 效果演示 注意事項(xiàng) 1.無法彈出安裝APP界面 manifest.json-APP常用其他設(shè)置-targetSdkVersion必須設(shè)置26以上 2.無法安裝APP manifest.json-APP權(quán)限設(shè)置需勾選: "<uses-permission android:name=\"android.permission.INSTALL_PACKAGES\"/>", "<uses-permission android:name=\"android.permission.REQUEST_INSTALL_PACKAGES\"/>" 3.獲取的版本號(hào)和設(shè)置的不一致 通過uni.getSystemInfoSync().appVersionCode獲取的本地應(yīng)用版本號(hào)和manifest.json-應(yīng)用版本號(hào)設(shè)置不一致,需要云打包或者自定義基座里面才能生效 4.無法獲取下載進(jìn)度 app下載請(qǐng)求回復(fù)體頭部需要返回content-length字段,才能正常獲取到app總大小,需要下載接口開啟支持,本演示例子已做顯示的兼容處理。 插件傳送門 APP在線升級(jí)彈窗頁(yè)面組件 文章知識(shí)點(diǎn)與官方知識(shí)檔案 ———————————————— 版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接和本聲明。 原文鏈接:https://blog.csdn.net/sd1sd2/article/details/131241609 |
|