ionic3 熱更新 填坑過(guò)程

最近公司做了手機(jī)app需要使用到熱更新,先對(duì)熱更新進(jìn)行一個(gè)簡(jiǎn)單的介紹吧;`

熱更新

熱更新是一種app的常用更新方式。簡(jiǎn)單說(shuō)就是當(dāng)你的手機(jī)上已經(jīng)用了app之后,打開app的時(shí)候及時(shí)更新。

對(duì)于熱更新的重大打擊

2017年6月,AppStore審核團(tuán)隊(duì)針對(duì)AppStore中“熱更新”的App開發(fā)者發(fā)送郵件,要求移除所有相關(guān)的代碼、框架或SDK,并重新提交審核,否則就會(huì)在AppStore中下架該軟件。

該文章主要是分享基于 ionic3 的熱更新

第一步 安裝插件

安裝熱更新cli

npm install -g cordova-hot-code-push-cli

安裝熱更新插件

ionic cordova plugin add cordova-hot-code-push-plugin

第二步 打包編譯代代碼

一、在項(xiàng)目根目錄 創(chuàng)建一個(gè) cordova-hcp.json

創(chuàng)建cordova-hcp.json

解釋一下每個(gè)key的意思 :

1.autogenerated自動(dòng)生成的意思? 默認(rèn)是 true

2. update 熱更新的觸發(fā)方式 目前有三種?

? ? start(app啟動(dòng)的時(shí)候觸發(fā), 默認(rèn)是start);

? ? resume(app從后臺(tái)切換回來(lái)的時(shí)候觸發(fā));

? ? now (web內(nèi)容下載完畢)

3. min_native_interface 用于控制app的外殼版本;來(lái)判斷當(dāng)前app是直接內(nèi)殼更新還是 需要下載app進(jìn)行外殼更新(下面會(huì)有詳細(xì)的介紹)

4.content_url 來(lái)配置你服務(wù)器的地址 用于后續(xù) app觸發(fā)更新時(shí) 和服務(wù)器上的文件進(jìn)行比對(duì) 和下載更新用(下面也會(huì)有詳細(xì)的介紹)

二、配置config.xml

基本配置

? ? <chcp>

? ? <native-interface version="5" />? # 你的app 的當(dāng)前版本

? ? <config-file url="http://{你自己的服務(wù)器地址}/hotcode/chcp.json" /> #你服務(wù)器上面的地址

? ?</chcp>

自動(dòng)下載(默認(rèn)是true 只要觸發(fā)了就好自動(dòng)下載)

<chcp>

<auto-download enabled="false" />

?</chcp>

改成false 可以通過(guò)代碼去觸發(fā) 更新下載

自動(dòng)安裝(默認(rèn)是true 只要下載好了就會(huì)安裝)

<chcp>

<auto-install enabled="false" />

?</chcp>

改成false 可以通過(guò)代碼去觸發(fā) 安裝

可用事件

chcp_updateIsReadyToInstall - web內(nèi)容已經(jīng)下載并可以安裝時(shí)觸發(fā).

chcp_updateLoadFailed - 插件無(wú)法下載web更新時(shí)觸發(fā). 詳細(xì)錯(cuò)誤信息在事件參數(shù)里.

chcp_nothingToUpdate - 無(wú)可用更新下載時(shí)觸發(fā).

chcp_updateInstalled - web內(nèi)容安裝成功時(shí)觸發(fā).

chcp_updateInstallFailed - web內(nèi)容安裝失敗時(shí)觸發(fā). 詳細(xì)錯(cuò)誤信息在事件參數(shù)里.

chcp_nothingToInstall -無(wú)可用更新安裝時(shí)觸發(fā).

chcp_assetsInstalledOnExternalStorage - 插件成功把a(bǔ)pp內(nèi)置的web內(nèi)容拷貝到外置存儲(chǔ)中時(shí)觸發(fā). 你可能需要開發(fā)調(diào)試時(shí)用到這個(gè)事件,也許不會(huì).

chcp_assetsInstallationError -插件無(wú)法拷貝app內(nèi)置的web內(nèi)容到外置存儲(chǔ)中時(shí)觸發(fā). 如果此事件發(fā)生了 - 插件不再工作. 也許是設(shè)備沒有足夠的存儲(chǔ)空間導(dǎo)致.? 詳細(xì)錯(cuò)誤信息在事件參數(shù)里.

三、corodva-hcp build

根目錄下的www 會(huì)生成兩個(gè)文件 chcp.json文件 和chcp.manifest文件

chcp.json

每次運(yùn)行corodva-hcp build? ?chcp.json? 都會(huì)更根據(jù)?cordova-hcp.json 文件的配置進(jìn)行更新? release是當(dāng)時(shí)運(yùn)行的時(shí)間戳(下面會(huì)詳細(xì)說(shuō)release的用途)

四、配置 content_url?

這里就需要自己去配置 我就以我的情況來(lái)簡(jiǎn)單說(shuō)明一下,我有個(gè)遠(yuǎn)程服務(wù)器 在該服務(wù)器上安裝了nginx,我配置了一個(gè)http://{你的服務(wù)器地址}/hotcode 的路徑 然后我把根目錄下的www你們的文件拷貝到服務(wù)器上

我是nginx配置里面配置的文件為update,訪問(wèn)路徑配置的hotcode具體配置可以百度一下nginx配置

五、打包一個(gè)apk 安裝到手機(jī)

這樣基本配置就ok了?

第三步 測(cè)試熱更新

先介紹一下熱更新的更新機(jī)制我通常分為外殼更新和內(nèi)殼更新

外殼更新就是 當(dāng)app添加新的插件和配置的時(shí)候無(wú)法通過(guò)更新html、js、css來(lái)實(shí)現(xiàn)的需要使用外殼更新 下載新的app來(lái)覆蓋安裝

內(nèi)殼更新就是 app內(nèi)部做的樣式圖片代碼邏輯bug的修復(fù)更新可以直接推送到手機(jī)上進(jìn)行實(shí)時(shí)更新(現(xiàn)在只能android版本使用)

先說(shuō)內(nèi)殼更新

每次運(yùn)行corodva-hcp build 之后 chcp.json? 都會(huì)更根據(jù)?cordova-hcp.json 文件的配置進(jìn)行更新? release是當(dāng)時(shí)運(yùn)行的時(shí)間戳

然后你重新編譯你的代碼?ionic-app-scripts build 生成 www文件后 同步到你配置好的服務(wù)器上

這個(gè)時(shí)候 開app

建議可以使用Android Studio連接手機(jī)來(lái)調(diào)試 如下

chcp熱更新會(huì)跟服務(wù)器比對(duì)chcp.json 文件

如果chcp.json文件中min_native_interface 一樣 但是 release不一樣 時(shí)會(huì)判斷為內(nèi)殼更新會(huì)從服務(wù)器上開始更新代碼的你的手機(jī)上

提示:

如果沒有沒有像我圖片中的效果 有可能是你的服務(wù)器配置有問(wèn)題 先測(cè)試一下 你服務(wù)器是否能訪問(wèn),再就是可能你app中的chcp.json和服務(wù)器上min_native_interface 、release一樣 所以不用更新 log會(huì)提示

沒有更新

再來(lái)說(shuō)外殼更新

外殼更新主要用于你更新了新的插件和一些app配置的時(shí)候使用,需要注意的有 你app的版外殼版本就是min_native_interface,只有當(dāng)app中chcp.json的min_native_interface 比 服務(wù)器中的chcp.json中的min_native_interface小的時(shí)候 他會(huì)出發(fā)一個(gè)報(bào)錯(cuò)?chcp_updateLoadFailed ,?chcp.error.APPLICATION_BUILD_VERSION_TOO_LOW

熱更新的報(bào)錯(cuò)碼

特別注意如果 在config.xml中 要配置 android-versionCode的版本要和 native-interface保持一致 這樣才能你下載好app安裝后 不會(huì)去自動(dòng)更新以前老的不合適的代碼;

配置app版本

再來(lái)說(shuō)如何去再app中觸發(fā)提示框下載新版本的app讓用戶安裝,下面是源碼

import {Injectable }from '@angular/core';

import {File }from '@ionic-native/file';

import {FileOpener }from '@ionic-native/file-opener';

import {AlertController, LoadingController }from 'ionic-angular';

import {VERSION_NUMBER}from '../providers';

declare var FileTransfer:any;

declare var cordova:any;

declare var chcp:any;

@Injectable()

export class AppUpdate {

? public downloading;

? public firstFlag =true;

? public timer;

? public downloadProgress =0;

? constructor(private alertCtrl:AlertController,

? ? ? ? ? ? ? public loadingCtrl:LoadingController,

? ? ? ? ? ? ? public fileOpener:FileOpener,

? ? ? ? ? ? ? public file:File

? ) {

this.storage.set('isUpdate', false);

? ? this.bindEvents();

? }

bindEvents() {

console.log('----------進(jìn)入更新模塊---------');

? ? document.addEventListener('deviceready', () => {

? ? ?console.log('onDeviceReady');

? ? }, false);

? ? document.addEventListener('chcp_updateLoadFailed', (eventData:any) => {

? ? ? ?console.log('chcp_updateLoadFailed');

? ? ? const error =eventData.detail.error;

? ? ? console.log('123' +error.code +',' +chcp.error.APPLICATION_BUILD_VERSION_TOO_LOW);

? ? ? // 當(dāng)檢測(cè)出內(nèi)核版本過(guò)小

? ? ? if (error &&error.code ==chcp.error.APPLICATION_BUILD_VERSION_TOO_LOW) {

if (this.firstFlag) {

this.firstFlag =false;

? ? ? ? ? // 提示

? ? ? ? ? this.alertUpdate();

? ? ? ? }

}else {

console.log('是新版本');

? ? ? }

}, false);

? ? //沒有更新

? document.addEventListener('chcp_nothingToUpdate', function(eventData){

console.log('chcp_nothingToUpdate');

? ? ? alert('是新版本');

}, false);

document.addEventListener('chcp_nothingToUpdate', function(eventData){

console.log('chcp_nothingToUpdate');

alert('chcp_nothingToUpdate');

}, false);

? ? /!*插件開始在外部存儲(chǔ)上安裝應(yīng)用程序資產(chǎn)之前立即調(diào)度事件*!/

document.addEventListener('chcp_beforeAssetsInstalledOnExternalStorage', function(eventData){

console.log('chcp_beforeAssetsInstalledOnExternalStorage');

alert('chcp_beforeAssetsInstalledOnExternalStorage');

}, false);

? ? /!*插件無(wú)法拷貝app內(nèi)置的web內(nèi)容到外置存儲(chǔ)中時(shí)觸發(fā). *!/

document.addEventListener('chcp_assetsInstallationError', function(eventData){

console.log('chcp_assetsInstallationError');

alert('chcp_assetsInstallationError');

}, false);

document.addEventListener('chcp_assetsInstalledOnExternalStorage', function(eventData){

console.log('chcp_assetsInstalledOnExternalStorage');

alert('chcp_assetsInstalledOnExternalStorage');

}, false);

? ? /!*web內(nèi)容已經(jīng)下載并可以安裝時(shí)觸發(fā).*!/

document.addEventListener('chcp_updateIsReadyToInstall', function(eventData){

console.log('chcp_updateIsReadyToInstall');

alert('chcp_updateIsReadyToInstall');

}, false);

document.addEventListener('chcp_beforeInstall', function(eventData){

console.log('chcp_beforeInstall');

alert('chcp_beforeInstall');

}, false);

document.addEventListener('chcp_updateInstallFailed', function(eventData){

console.log('chcp_updateInstallFailed');

alert('chcp_updateInstallFailed');

}, false);

document.addEventListener('chcp_updateInstalled', function(eventData){

console.log('chcp_updateInstalled');

alert('chcp_updateInstalled');

}, false);

document.addEventListener('chcp_nothingToInstall', function(eventData){

console.log('chcp_nothingToInstall');

alert('chcp_nothingToInstall');

}, false);

? }

// 提示安裝

? alertUpdate() {

? ? let alert =this.alertCtrl.create({

? ? ?title:'有新的版本,請(qǐng)下載更新',

? ? ? message:'您當(dāng)前版本為' +VERSION_NUMBER +',發(fā)現(xiàn)新版本,是否下載新版本',

? ? ? buttons:[

? ? ? ? {

text:'下次再說(shuō)',

? ? ? ? ? role:'cancel',

? ? ? ? ? handler:() => {

this.firstFlag =true;

? ? ? ? ? ? this.storage.set('isUpdate', true);

? ? ? ? ? ? console.log('Cancel clicked');

? ? ? ? ? }

},

? ? ? ? {

text:'立即升級(jí)',

? ? ? ? ? handler:() => {

this.firstFlag =true;

? ? ? ? ? ? console.log('Update App');

? ? ? ? ? ? this.presentLoadingDefault();

? ? ? ? ? }

}

]

? ? });

? ? alert.present().then();

? }

userWentToStoreCallback() {

//user went to the store from the dialog

? }

userDeclinedRedirectCallback() {

// User didn't want to leave the app.

// Maybe he will update later.

? }

downloadfile(loading) {

console.log('downloadfile');

? ? //下載代碼

? ? var fileTransfer =new FileTransfer();

? ? const fs:string =cordova.file.externalRootDirectory ;

? ? this.file.createDir(fs,'fawo',true).then((dir:any) =>{

console.log('create dir success'+JSON.stringify(dir));

? ? ? fileTransfer.download("http://{自己服務(wù)器地址}/download/{app名字}.apk",dir.nativeURL+'{自己定義}.apk', (entry) => {

// 打開下載下來(lái)的APP

? ? ? ? this.fileOpener.open(

? ? ? ? ? dir.nativeURL+'自己定義.apk',//下載文件保存地址

? ? ? ? ? 'application/vnd.android.package-archive').then((data:any) => {

console.log('open file success');

? ? ? ? }).catch(err =>{

console.log('open file error' +err);

? ? ? ? ? alert('打開安裝包失??!');

? ? ? ? });

? ? ? }, function(err) {

console.log('下載失敗'+JSON.stringify(err));

? ? ? ? alert('下載失敗');

? ? ? ? loading.dismiss();

? ? ? },true);

? ? }).catch(err =>{

console.log('create dir err'+err);

? ? });

? ? fileTransfer.onprogress =(progressEvent) => {

this.downloadProgress =(progressEvent.loaded /progressEvent.total) *100;

? ? ? console.log('已經(jīng)下載:' +this.downloadProgress);

? ? };

? }

presentLoadingDefault() {

this.downloading =this.loadingCtrl.create({

content:'已經(jīng)下載:0%'

? ? });

? ? this.downloading.present();

? ? this.downloadfile(this.downloading);

? ? this.timer =setInterval(() => {

? ? ?this.downloading.setContent('已經(jīng)下載' +this.downloadProgress.toString().split('.')[0] +'%');

? ? ? this.downloading.present().then();

? ? ? if (this.downloadProgress >99) {

? ? ? ?clearInterval(this.timer);

? ? ? ? this.downloading.dismiss();

? ? ? }

}, 300)

? }

}

這個(gè)文件引入 app.module.ts 如果在調(diào)試中不會(huì)打印出console.log('----------進(jìn)入更新模塊---------'); 說(shuō)明沒有觸發(fā)這個(gè)模塊 可以試著在app.component.ts中當(dāng)app啟動(dòng)的時(shí)候主動(dòng)觸發(fā)一次

(特別注意)

1.Android8以后 app自己下載的apk是需要用戶信任;建議加上這個(gè)

<platform name="android">

<config-file parent="/manifest" target="AndroidManifest.xml" xmlns:android="http://schemas.android.com/apk/res/android">

? ? <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

</config-file>

</platform>

2.再就是apk自安裝的時(shí)候會(huì)報(bào)錯(cuò)打不開

需要修改platform/android/mainfest.xml中修改uses-sdk的值,其中android:targetSdkVersion最大 值不能超過(guò)23,否則會(huì)出錯(cuò).

<uses-sdk android:minSdkVersion=”16” android:targetSdkVersion=”23” />

---------------------

非常感謝這個(gè)作者 解決了的一個(gè)大麻煩

作者:cangahi09025566

來(lái)源:CSDN

原文:https://blog.csdn.net/cangahi09025566/article/details/80322830

到這里熱更新的坑算是填了差不多了,還有很多的細(xì)節(jié)我自己也還沒全部搞明白,也是記錄我自己這幾天搗鼓的心路歷程,希望能對(duì)你有些許幫助;

最后感謝幾個(gè)作者?

ionic3 項(xiàng)目熱更新使用 - 簡(jiǎn)書

CSDN 熱更新詳解

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容