ionic4.0 詳細熱更新 自動模式以及手動模式--秒殺網上一切熱更新因為網上沒有手動更新的詳細程式碼 測試無誤上線釋出了的東西.
ionic4.0 詳細熱更新 自動模式以及手動模式(全)–純手打,獨一無二,絕對好用.
網上一大堆ionic 熱更新,但是都是說的最基本的自動更新操作,安裝幾個外掛,改幾個配置檔案,就草草了事,讓我真的很煩躁,通過自己不斷的查詢,終於守得雲開見月明,找到了手動熱更新操作,以及通過點選事件觸發,或者監聽事件觸發更新的方法…關鍵時刻還是要看官方文件,以及github網站.話不多說,接下來就上熱更新操作 **
自動以及手動模式步驟
**
**
熱更新外掛
熱更新前準備工作:當然是按照外掛了喲. ** 1.熱更新外掛安裝
ionic2.0安裝方式:(已安裝),我的是ionic
cordova plugin add cordova-hot -code-push-plugin
ionic 4.0安裝方式,看自己版本
ionic cordova plugin add cordova-hot-code-push
ionic info 檢視自己的環境配置
熱更新步驟
1.cordova plugin add cordova-hot-code-push-plugin
2.cordova plugin add cordova-hot-code-push-local-dev-addon
3.npm install -g cordova-hot-code-push-cli
上面三個,網上有的人說第二個不用安裝,我還是安了,因為不知道,所以大不了安裝了再解除安裝就是,
import { HotCodePush } from '@ionic-native/hot-code-push';
constructor(private hotCodePush: HotCodePush) { }
...
hotCodePush.fetchUpdate (options).then(data => { console.log('Update available'); })
在專案根目錄下執行 $cordova-hcp server , 然後ctrl / command + z 結束伺服器,我們不使用它的伺服器來測試。只為了使用它生成的一個配置檔案,名為:.chcpenv
.chcpenv{ “content_url”: “http://192.168.131.2:1111/hot“, “config_url”: “http://192.168.131.2:1111/hot/chcp.json } 我們在專案APP根目錄下新建一個cordova-hcp.json檔案,這個檔案是生成配置檔案的模板,編輯如下 { “autogenerated”: true, “name”: “com.cqzuxia.zuxiaoa”, “min_native_interface”: 1, “content_url”: “http://192.168.131.2:1111/hot“, “update”: “start” } 在專案APP根目錄執行cordova-hcp build, 這樣就會按照上一步的cordova-hcp.json檔案為模板在www目錄中生成兩個檔案,chcp.json 和 chcp.manifest 兩個檔案。chcp.json 檔案內容 ——————————–chcp.json配置————————————-
{
"autogenerated": true,
"name": "com.cqzuxia.zuxiaoa",
"min_native_interface": 1,
"content_url": "http://192.168.131.2:31284",
"update": "now",
"release": "2018.09.04-16.27.07"
}
.chcpenv{
"content_url": "http://192.168.131.2:1111/hot",
"config_url": "http://192.168.131.2:1111/hot/chcp.json
}
——————————–.chcpenv配置————————————-
.chcpenv{
"content_url": "http://192.168.131.2:1111/hot",
"config_url": "http://192.168.131.2:1111/hot/chcp.json
}
chcp.manifest 檔案內容是存的www檔案的hash值,以後就會根據這個值比較是否改變來確定是否更新.當然還有看chcp.json中的時間戳 release.
自建的本地伺服器
我們現在可以開啟一個本地伺服器,隨便找一個地方新建一個資料夾,然後在資料夾根目錄下執行 hs -o -p 1111 (備註,hs命令是全域性安裝了http-server之後才能使用,npm i -g http-server), http://192.168.131.2:31284/ 專案執行cordova-hcp server 後的伺服器地址,一般不用,用自己自建的本地伺服器,儘量不要和自己的專案衝突,後面會感覺很繞,不在專案中建伺服器
d:
cmd -> d: –> cd SVNfilenew -> cd hotcodedevelop -> d:\SVNfilenew\hotcodedevelop 對,就是這個目錄下執行:hs -o -p 1111 D:\SVNfilenew\hotcodedevelop 如何啟動本地伺服器?? 關鍵命令列: hs -o -p 1111
啟動本地伺服器地址
cordova-hcp server 這樣我們可以通過訪問 http://127.0.0.1:1111 來訪問我們這個本地伺服器了。 ipconfig檢視本機IP:訪問本機本地伺服器
http://192.168.131.2:1111 這個本地伺服器就是儲存熱更新的程式碼的地方,也就是需要編譯ionic cordova run Android 生成後的www檔案存放地址.當然上線後就放在雲伺服器上相應的地址了. ——————————-config.xml—————————————–
<chcp>
<!-- 版本號 -->
<native-interface version="1" />
<!-- 伺服器地址 -->
<config-file url="http://192.168.131.2:1111/hot/chcp.json" />
<!-- 自動下載 -->
<auto-download enabled="true" />
<!-- 自動安裝 -->
<auto-install enabled="true" />
</chcp>
5.cordova-hcp build 在專案APP根目錄執行命令使用cordova-hcp build重新生成檔案的hash.這一步很容易被忽略 —->在足下校園APP目錄下執行
再把www考到伺服器對應的www. 到此為止,熱更新的自動操作完成,每次開啟APP都會更新資源了,再也不需要手動去下載版本了,是不是挺爽.但是還遠遠不夠,達不到要求啊. 上面這些操作就是自動熱更新的步驟,只不過手動熱更新同樣要使用這些步驟.
手動更新開始:
手動更新,就意味著要控制更新的程式碼所執行方法 下面哈哈,請見證奇蹟的時刻 安裝步驟同上
—————–自動模式————–config.xml—————————————–
<chcp>
<!-- 版本號 -->
<native-interface version="1" />手動配置這些都不需要了,會在程式碼中新增.
<!-- 伺服器地址 -->
<config-file url="http://192.168.131.2:1111/hot/chcp.json" />手動配置這些都不需要了,會在程式碼中新增.
<!-- 自動下載 -->
<auto-download enabled="false" />
<!-- 自動安裝 -->
<auto-install enabled="false" />
</chcp>
—————-手動模式—————config.xml—————————————–
<chcp>
<!-- 自動下載 -->
<auto-download enabled="false" />
<!-- 自動安裝 -->
<auto-install enabled="false" />
</chcp>
主要講解ts模組內容 app.component.ts中 新增宣告declare var chcp :any;
1.通過監聽更新,
//程式在前臺切換到後臺
document.addEventListener("pause", () => {
console.log("後臺隱藏:");
this.pause();
}, false);
//程式在後臺切換到前臺操作
document.addEventListener("resume", () => {
console.log("前臺顯示:");
// this.CheckResource();
this.resume();
}, false);
2.通過APP登入後進入介面時更新控制
//資源包熱更新資訊檢測
this.CheckResource();
// platform.resume.subscribe(() => this.fetchUpdate());
// 是否更新-----------------------------資源包熱更新
//資源包熱更新資訊檢測
CheckResource() {
this.sys.CheckResourceVersion((isUpdate, type) => {
console.log("是否有更新===="+isUpdate);
console.log("更新型別為===="+type);
if (isUpdate) {
if (type == 0) {
this.silentUpdate();
} else if (type == 1) {
this.hintUpdate(type);
} else if (type == 2) {
this.forceUpdate(type);
}
}
});
}
// 靜默更新 -- 使用者不曉得更新了.
silentUpdate() {
console.log("靜默更新");
this.fetchUpdate();
}
// 提示更新
hintUpdate(type) {
this.popConfirm(type);
}
// 強制更新 -- 使用者曉得更新了.
forceUpdate(type) {
this.popConfirm(type);
}
// 提示更新彈窗
popConfirm(type) {
let buttons = [{
text: '現在更新',
handler: () => {
this.utils.showLoading('正在下載更新資源包...,下載完成後將自動重啟APP,請稍候...',20000);
this.fetchUpdate();
}
}];
let msg = "";
if (type == 1) {
msg = "是否立即更新?";
buttons.unshift({
text: '以後再說',
handler: () => {
AppConfig.updateModal.dismiss();
}
});
}
else if (type == 2) {
msg = "(請現在下載更新後使用,必須更新)";
}
AppConfig.updateModal = this.alertCtrl.create({
title: '升級提示',
message: '【足下校園APP】發現有新資源包.' + msg,
buttons: buttons
});
AppConfig.updateModal.onDidDismiss(() => {
AppConfig.updateModal = null;
});
AppConfig.updateModal.present();
}
// 匹配更新
fetchUpdate() {
const options = {
'config-file': 'http://192.168.131.2:1111/hot/chcp.json'
};
chcp.fetchUpdate(this.updateCallback, options);
}
updateCallback(error, data) {
if (error) {
console.log('Failed to load the update with error code: ' + error.code);
console.log(error.description);
} else {
console.log('Update is loaded');
chcp.installUpdate(error => {
if (error) {
console.log('Failed to install the update with error code: ' + error.code);
console.log(error.description);
} else {
console.log('Update installed!');
}
});
}
}
一下是app.component.ts完整程式碼,以及韌體外殼更新程式碼.方法是CheckNewVersion()這個方法.這個比較簡單,直接跳轉下載
import { Component, ViewChild } from '@angular/core';
import { Platform, Nav, AlertController } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { StartPage } from '../pages/start/start';
import { SystemService } from '../providers/system-service';
import { HttpService } from '../providers/HttpService';
import { UserInfoData } from '../model/UserInfoData';
import { StorageService } from '../providers/StorageService';
// import { BrowserService } from '../providers/BrowserService';
import { JPush } from '@jiguang-ionic/jpush';
import { Device } from "@ionic-native/device";
import { Autostart } from '@ionic-native/autostart';
import { TeacherEduHttpService } from '../providers/TeacherEduHttpService';
import { File } from '@ionic-native/file';
import { ImageData } from '../model/ImageModel';
import { BrowserService } from '../providers/BrowserService';
import { Keyboard } from "@ionic-native/keyboard";
import { AppConfig } from "./app.config";
import { commonUtils } from '../providers/common-utils';
// import { HotCodePush } from '@ionic-native/hot-code-push';
declare var chcp: any;
@Component({
templateUrl: 'app.html',
})
export class MyApp {
@ViewChild('mynav') nav: Nav;
rootPage;
loginInfo: UserInfoData;
msgCount: 0;
constructor(
private context: TeacherEduHttpService,
private platform: Platform,
statusBar: StatusBar,
splashScreen: SplashScreen,
private sys: SystemService,
private st: StorageService,
private file: File,
private httpService: HttpService,
private autostart: Autostart,
private jp: JPush,
private device: Device,
private bs: BrowserService,
private alertCtrl: AlertController,
private keyboard: Keyboard,
public utils: commonUtils,
// private hotCodePush: HotCodePush
) {
platform.ready().then(() => {
AppConfig.platformType = this.platform.is("ios") ? 'ios' : 'android';
var type = this.st.read<number>("login_type");
if (type != null) {
AppConfig.lastLoginType = type;
}
//manual mode. remove this for automatic mode
// platform.resume.subscribe(() => this.fetchUpdate());
statusBar.styleDefault();
splashScreen.hide();
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
// statusBar.styleDefault();
// statusBar.overlaysWebView(true);
// statusBar.hide();
splashScreen.hide();
this.keyboard.hideKeyboardAccessoryBar(false);
this.nav.rootParams = {
selectedIndex: 1,
msgCount: 0
};
this.StartJpush();
this.setBadge();
this.resetImage();
this.rootPage = StartPage;
//設定為自啟動
if (this.device.platform == "Android") {
this.autostart.enable();
}
//資源包熱更新資訊檢測
this.CheckResource();
//外殼版本資訊檢測
this.sys.CheckNewVersion((hasNewVersion) => {
console.log("外殼版本資訊檢測");
if (hasNewVersion) {
this.presentConfirm();
}
});
//程式在前臺切換到後臺
document.addEventListener("pause", () => {
console.log("後臺隱藏:");
this.pause();
}, false);
//程式在後臺切換到前臺操作
document.addEventListener("resume", () => {
console.log("前臺顯示:");
// this.CheckResource();
this.resume();
}, false);
});
}
// 是否更新-----------------------------資源包熱更新
//CheckResourceVersion這個是後臺調介面方法,用於判斷是否更新,以及哪種更新方式等.isUpdate更新為true,不更新為false
type:1.靜默更新,2,提示更新,3,強制更新
//資源包熱更新資訊檢測
CheckResource() {
this.sys.CheckResourceVersion((isUpdate, type) => {
console.log("是否有更新===="+isUpdate);
console.log("更新型別為===="+type);
if (isUpdate) {
if (type == 0) {
this.silentUpdate();
} else if (type == 1) {
this.hintUpdate(type);
} else if (type == 2) {
this.forceUpdate(type);
}
}
});
}
// 靜默更新 -- 使用者不曉得更新了.
silentUpdate() {
console.log("靜默更新");
this.fetchUpdate();
}
// 提示更新
hintUpdate(type) {
this.popConfirm(type);
}
// 強制更新 -- 使用者曉得更新了.
forceUpdate(type) {
this.popConfirm(type);
}
// 提示更新彈窗
popConfirm(type) {
let buttons = [{
text: '現在更新',
handler: () => {
this.utils.showLoading('正在下載更新資源包...,下載完成後將自動重啟APP,請稍候...',20000);
this.fetchUpdate();
}
}];
let msg = "";
if (type == 1) {
msg = "是否立即更新?";
buttons.unshift({
text: '以後再說',
handler: () => {
AppConfig.updateModal.dismiss();
}
});
}
else if (type == 2) {
msg = "(請現在下載更新後使用,必須更新)";
}
AppConfig.updateModal = this.alertCtrl.create({
title: '升級提示',
message: '【足下校園APP】發現有新資源包.' + msg,
buttons: buttons
});
AppConfig.updateModal.onDidDismiss(() => {
AppConfig.updateModal = null;
});
AppConfig.updateModal.present();
}
// 匹配更新 ----------這裡是核心程式碼.
//官網地址:
//
https://github.com/ralscha/blog/blob/master/hotcodepush/src/app/app.component.ts
//
https://golb.hplar.ch/2017/01/Hot-deploy-updates-with-the-cordova-hot-code-push-plugin.html
fetchUpdate() {
const options = {
'config-file': 'http://192.168.131.2:1111/hot/chcp.json'
};
chcp.fetchUpdate(this.updateCallback, options);
}
updateCallback(error, data) {
if (error) {
console.log('Failed to load the update with error code: ' + error.code);
console.log(error.description);
} else {
console.log('Update is loaded');
chcp.installUpdate(error => {
if (error) {
console.log('Failed to install the update with error code: ' + error.code);
console.log(error.description);
} else {
console.log('Update installed!');
}
});
}
}
//重置未載入的圖片資料
resetImage() {
var images = this.st.read<Array<ImageData>>("image_data");
images = images ? images : [];
var dataDir = this.file.dataDirectory;
images = images.filter(image => {
if (image.stats == 1) {
this.file.removeFile(dataDir + image.local, image.name);
return false;
}
else if (image.stats == 2) {
return true;
}
});
this.st.write("image_data", images);
}
//初始化訊息推送
StartJpush() {
this.jp.init().then(() => {
});
// this.jp.resumePush();
// let page=this;
document.addEventListener('jpush.receiveRegistrationId', function (event) {
console.log(event)
}, false);
this.jp.isPushStopped().then((value) => {
if (value == 0) {
// 開啟
} else {
// alert("你已經關閉訊息推送");
}
});
// //應用程式處於後臺時
// document.addEventListener("jpush.backgroundNotification", function (event) {
// var alertContent
// if(page.device.platform == "Android") {
// alertContent = event["alert"]
// } else {
// alertContent = event["aps"]["alert"]
// }
// // page.setBadge();
// console.log("backgroundNotification")
// }, false)
// //應用程式處於前臺時
// document.addEventListener("jpush.receiveNotification", function (event) {
// page.nav.rootParams.selectedIndex = 0;
// var alertContent
// if(page.device.platform == "Android") {
// alertContent = event["alert"]
// } else {
// alertContent = event["aps"]["alert"]
// }
// // page.setBadge();
// console.log(event);
// // alert("receiveNotification")
// }, false)
// //點選通知啟動或喚醒應用程式時
// document.addEventListener("jpush.openNotification", function (event) {
// page.nav.rootParams.selectedIndex = 0;
// var alertContent
// if(page.device.platform == "Android") {
// alertContent = event["alert"]
// } else {
// alertContent = event["aps"]["alert"]
// }
// console.log(event);
// // page.setBadge();
// alert("openNotification")
// page.setBadge();
// }, false)
// //收到應用內訊息時
// document.addEventListener("jpush.receiveMessage", function (event) {
// var message
// if(page.device.platform == "Android") {
// message = event["message"];
// } else {
// message = event["content"];
// }
// alert("receiveMessage")
// page.setBadge();
// }, false)
}
setBadge() {
var page = this;
page.sys.getAppversion().getVersionNumber().then((ver) => {
page.context.GetOverviewMessage({ appVersion: ver.toString() }, (res) => {
if (res.Success) {
page.msgCount = 0;
res.Data.forEach(m => {
page.msgCount += m.MsgCount;
});
page.jp.setApplicationIconBadgeNumber(page.msgCount);
page.jp.setBadge(page.msgCount);
page.nav.rootParams.msgCount = page.msgCount > 99 ? 99 : page.msgCount;
}
}, null);
});
}
pause() {
}
resume() {
let page = this;
this.jp.resumePush();
page.sys.CheckNewVersion(function (hasNewVersion, isForce) {
if (hasNewVersion) {
page.presentConfirm(isForce);
}
});
this.loginInfo = this.st.read<UserInfoData>("logindata");
if (this.loginInfo)
this.autoLogin(this.loginInfo);
}
presentConfirm(isForce: boolean = false) {
if (AppConfig.updateModal) {
// this.tipspage.dismiss();
// this.tipspage = null;
return;
// this.tipspage.present();
}
let buttons = [{
text: '現在更新',
handler: () => {
this.bs.goDownload();
}
}];
let msg = "";
if (isForce) {
msg = "(必須更新)";
}
else {
msg = "是否跳轉下載更新?";
buttons.unshift({
text: '以後再說',
handler: () => {
AppConfig.updateModal.dismiss();
}
});
}
AppConfig.updateModal = this.alertCtrl.create({
title: '系統提示',
message: '【足下校園APP】發現有新版本.' + msg,
enableBackdropDismiss: !isForce,
buttons: buttons
});
AppConfig.updateModal.onDidDismiss(() => {
AppConfig.updateModal = null;
});
AppConfig.updateModal.present();
}
autoLogin(data) {
var st = this.st;
//學生登入
if (this.loginInfo.type == 0) {
var postdata = {
client_id: 'f458da7b85b14452',
client_secret: 'ea2dfee53ab440e08451375a834841cf',
scope: 'accounts menus roles',
grant_type: 'password',
username: data.LoginID,
password: data.LoginPwd
};
this.httpService.httpPostNoAuth('http://learning.cqzuxia.com/oauth/token', postdata, function (res) {
res.userType = 0;
data.UserToken = res.access_token;
st.write("logindata", data);
st.write('authData', res);
st.write("username", data.LoginID);
st.write("password", data.LoginPwd);
st.write("lastusername", data.LoginID);
st.write("lastpassword", data.LoginPwd);
console.log(res);
}, function (err) {
});
} else {
//老師登入
postdata = {
client_id: 'i5g0n1MUJ0YwGZnG',
client_secret: 'gP90SCHhow3KE4fFwuOfiM5cghmNM9TV',
scope: 'accounts menus roles',
grant_type: 'password',
username: data.LoginID,
password: data.LoginPwd
};
this.httpService.httpPostNoAuth('http://passport.cqzuxia.com/oauth/token', postdata, function (res) {
res.userType = 1;
st.write('authData', res);
st.write("logindata", data);
}, function (err) {
});
}
}
}
資源包更新和外殼更新調介面方法
public CheckNewVersion(callback) {
let thispage = this;
if(AppConfig.lastLoginType != undefined) { //如果從未登入過,則不推送
this.http.httpGetNoAuth("http://app.cqzuxia.com/api/Values/GetCampusAppVersionNewWithType", {}, function (res) {
let serverversion = res;
let isForce: boolean = false;
if(AppConfig.lastLoginType == 0) {
isForce = serverversion.Data.force.student;
}
else if(AppConfig.lastLoginType == 1) {
isForce = serverversion.Data.force.teacher;
}
// thispage.appversion.getVersionNumber().then((appres) => {
let serverMuberarry = [];
if (thispage._device.platform.toLowerCase() == "android") {
if (!serverversion.Data.android) { return }
if(AppConfig.lastLoginType == 0) { //學生
serverMuberarry = serverversion.Data.android.student.toString().split('.');
}
else if(AppConfig.lastLoginType == 1) { //老師
serverMuberarry = serverversion.Data.android.teacher.toString().split('.');
}
}
else if (thispage._device.platform.toLowerCase() == "ios") {
if (!serverversion.Data.ios) { return }
if(AppConfig.lastLoginType == 0) { //學生
serverMuberarry = serverversion.Data.ios.student.toString().split('.');
}
else if(AppConfig.lastLoginType == 1) { //老師
serverMuberarry = serverversion.Data.ios.teacher.toString().split('.');
}
}
let mersionmuberarry = AppConfig.localVersion().split(".");
let len = Math.min(serverMuberarry.length, mersionmuberarry.length);
for (let i = 0; i < len; i++) {
//如果本地小於伺服器,肯定要升級;如果本地大於伺服器,證明伺服器在降級,也需要升級(降級)
if (mersionmuberarry[i] < serverMuberarry[i] || mersionmuberarry[i] > serverMuberarry[i]) {
callback(true, isForce);
return;
}
}
callback(false, isForce);
// });
});
}
}
public getAppversion() {
return this.appversion;
}
public CheckResourceVersion(callback: Function) {
let thispage = this;
if(AppConfig.lastLoginType != undefined) { //如果從未登入過,則不推送
this.http.httpGetNoAuth("http://app.cqzuxia.com/api/Values/GetCampusAppResourceVersion", {}, function (res) {
let serverversion = res;
let type: number = 0;
// thispage.appversion.getVersionNumber().then((appres) => {
let serverMuberarry = [];
if (thispage._device.platform.toLowerCase() == "android") {
if (!serverversion.Data.android) { return }
if(AppConfig.lastLoginType == 0) { //學生
serverMuberarry = serverversion.Data.android.student.toString().split('.');
type = serverversion.Data.android.studentType;
}
else if(AppConfig.lastLoginType == 1) { //老師
serverMuberarry = serverversion.Data.android.teacher.toString().split('.');
type = serverversion.Data.android.teacherType;
}
}
else if (thispage._device.platform.toLowerCase() == "ios") {
if (!serverversion.Data.ios) { return }
if(AppConfig.lastLoginType == 0) { //學生
serverMuberarry = serverversion.Data.ios.student.toString().split('.');
type = serverversion.Data.android.studentType;
}
else if(AppConfig.lastLoginType == 1) { //老師
serverMuberarry = serverversion.Data.ios.teacher.toString().split('.');
type = serverversion.Data.android.teacherType;
}
}
let mersionmuberarry = AppConfig.localResourceVersion().split(".");
let len = Math.min(serverMuberarry.length, mersionmuberarry.length);
for (let i = 0; i < len; i++) {
//如果本地小於伺服器,肯定要升級;如果本地大於伺服器,證明伺服器在降級,也需要升級(降級)
if (mersionmuberarry[i] < serverMuberarry[i] || mersionmuberarry[i] > serverMuberarry[i]) {
callback(true, type);//type=0靜默更新=1提示更新.=2強制更新
return;
}
}
callback(false);
// });
});
}
}
—————熱更新嘗試-以及中途遇到的問題—————- 1. 配置熱更新在啟動的時候.
安裝 cordova plugin add cordova-android-support-gradle-release rm後可以打包,successful
3.新增去重複的程式碼 4.platforms/android/build.gradle中新增程式碼,這段程式碼有用,寫上去就不報錯
configurations.all {
// OkHttp 3.5.0+ includes the websockets API, so we need this to prevent a conflict
exclude module: 'okhttp-ws'
}
5.compile "com.android.support:support-v4:23.1.0"
project.properties中
# cordova.system.library.5=com.android.support:support-v4:24.1.1+
//6.不新增以下程式碼也可以打包,successful
d