ionic3 热更新 填坑过程

最近公司做了手机app需要使用到热更新,先对热更新进行一个简单的介绍吧;`

热更新

热更新是一种app的常用更新方式。简单说就是当你的手机上已经用了app之后,打开app的时候及时更新。

对于热更新的重大打击

2017年6月,AppStore审核团队针对AppStore中“热更新”的App开发者发送邮件,要求移除所有相关的代码、框架或SDK,并重新提交审核,否则就会在AppStore中下架该软件。

该文章主要是分享基于 ionic3 的热更新

第一步 安装插件

安装热更新cli

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

安装热更新插件

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

第二步 打包编译代代码

一、在项目根目录 创建一个 cordova-hcp.json

创建cordova-hcp.json

解释一下每个key的意思 :

1.autogenerated自动生成的意思  默认是 true

2. update 热更新的触发方式 目前有三种 

    start(app启动的时候触发, 默认是start);

    resume(app从后台切换回来的时候触发);

    now (web内容下载完毕)

3. min_native_interface 用于控制app的外壳版本;来判断当前app是直接内壳更新还是 需要下载app进行外壳更新(下面会有详细的介绍)

4.content_url 来配置你服务器的地址 用于后续 app触发更新时 和服务器上的文件进行比对 和下载更新用(下面也会有详细的介绍)

二、配置config.xml

基本配置

   

      # 你的app 的当前版本

    #你服务器上面的地址

   

自动下载(默认是true 只要触发了就好自动下载)

 

改成false 可以通过代码去触发 更新下载

自动安装(默认是true 只要下载好了就会安装)

 

改成false 可以通过代码去触发 安装

可用事件

chcp_updateIsReadyToInstall - web内容已经下载并可以安装时触发.

chcp_updateLoadFailed - 插件无法下载web更新时触发. 详细错误信息在事件参数里.

chcp_nothingToUpdate - 无可用更新下载时触发.

chcp_updateInstalled - web内容安装成功时触发.

chcp_updateInstallFailed - web内容安装失败时触发. 详细错误信息在事件参数里.

chcp_nothingToInstall -无可用更新安装时触发.

chcp_assetsInstalledOnExternalStorage - 插件成功把app内置的web内容拷贝到外置存储中时触发. 你可能需要开发调试时用到这个事件,也许不会.

chcp_assetsInstallationError -插件无法拷贝app内置的web内容到外置存储中时触发. 如果此事件发生了 - 插件不再工作. 也许是设备没有足够的存储空间导致.  详细错误信息在事件参数里.

三、corodva-hcp build

根目录下的www 会生成两个文件 chcp.json文件 和chcp.manifest文件

chcp.json

每次运行corodva-hcp build   chcp.json  都会更根据 cordova-hcp.json 文件的配置进行更新  release是当时运行的时间戳(下面会详细说release的用途)

四、配置 content_url 

这里就需要自己去配置 我就以我的情况来简单说明一下,我有个远程服务器 在该服务器上安装了nginx,我配置了一个http://{你的服务器地址}/hotcode 的路径 然后我把根目录下的www你们的文件拷贝到服务器上

我是nginx配置里面配置的文件为update,访问路径配置的hotcode具体配置可以百度一下nginx配置

五、打包一个apk 安装到手机

这样基本配置就ok了 

第三步 测试热更新

先介绍一下热更新的更新机制我通常分为外壳更新和内壳更新

外壳更新就是 当app添加新的插件和配置的时候无法通过更新html、js、css来实现的需要使用外壳更新 下载新的app来覆盖安装

内壳更新就是 app内部做的样式图片代码逻辑bug的修复更新可以直接推送到手机上进行实时更新(现在只能android版本使用)

先说内壳更新

每次运行corodva-hcp build 之后 chcp.json  都会更根据 cordova-hcp.json 文件的配置进行更新  release是当时运行的时间戳

然后你重新编译你的代码 ionic-app-scripts build 生成 www文件后 同步到你配置好的服务器上

这个时候 开app

建议可以使用Android Studio连接手机来调试 如下

chcp热更新会跟服务器比对chcp.json 文件

如果chcp.json文件中min_native_interface 一样 但是 release不一样 时会判断为内壳更新会从服务器上开始更新代码的你的手机上

提示:

如果没有没有像我图片中的效果 有可能是你的服务器配置有问题 先测试一下 你服务器是否能访问,再就是可能你app中的chcp.json和服务器上min_native_interface 、release一样 所以不用更新 log会提示

没有更新

再来说外壳更新

外壳更新主要用于你更新了新的插件和一些app配置的时候使用,需要注意的有 你app的版外壳版本就是min_native_interface,只有当app中chcp.json的min_native_interface 比 服务器中的chcp.json中的min_native_interface小的时候 他会出发一个报错 chcp_updateLoadFailed , chcp.error.APPLICATION_BUILD_VERSION_TOO_LOW

热更新的报错码

特别注意如果 在config.xml中 要配置 android-versionCode的版本要和 native-interface保持一致 这样才能你下载好app安装后 不会去自动更新以前老的不合适的代码;

配置app版本

再来说如何去再app中触发提示框下载新版本的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('----------进入更新模块---------');

    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);

      // 当检测出内核版本过小

      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);

    /!*插件开始在外部存储上安装应用程序资产之前立即调度事件*!/

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

console.log('chcp_beforeAssetsInstalledOnExternalStorage');

alert('chcp_beforeAssetsInstalledOnExternalStorage');

}, false);

    /!*插件无法拷贝app内置的web内容到外置存储中时触发. *!/

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内容已经下载并可以安装时触发.*!/

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:'有新的版本,请下载更新',

      message:'您当前版本为' +VERSION_NUMBER +',发现新版本,是否下载新版本',

      buttons:[

        {

text:'下次再说',

          role:'cancel',

          handler:() => {

this.firstFlag =true;

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

            console.log('Cancel clicked');

          }

},

        {

text:'立即升级',

          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://{自己服务器地址}/download/{app名字}.apk",dir.nativeURL+'{自己定义}.apk', (entry) => {

// 打开下载下来的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('已经下载:' +this.downloadProgress);

    };

  }

presentLoadingDefault() {

this.downloading =this.loadingCtrl.create({

content:'已经下载:0%'

    });

    this.downloading.present();

    this.downloadfile(this.downloading);

    this.timer =setInterval(() => {

     this.downloading.setContent('已经下载' +this.downloadProgress.toString().split('.')[0] +'%');

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

      if (this.downloadProgress >99) {

       clearInterval(this.timer);

        this.downloading.dismiss();

      }

}, 300)

  }

}

这个文件引入 app.module.ts 如果在调试中不会打印出console.log('----------进入更新模块---------'); 说明没有触发这个模块 可以试着在app.component.ts中当app启动的时候主动触发一次

(特别注意)

1.Android8以后 app自己下载的apk是需要用户信任;建议加上这个

   

2.再就是apk自安装的时候会报错打不开

需要修改platform/android/mainfest.xml中修改uses-sdk的值,其中android:targetSdkVersion最大 值不能超过23,否则会出错.

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

非常感谢这个作者 解决了的一个大麻烦

作者:cangahi09025566

来源:CSDN

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

到这里热更新的坑算是填了差不多了,还有很多的细节我自己也还没全部搞明白,也是记录我自己这几天捣鼓的心路历程,希望能对你有些许帮助;

最后感谢几个作者 

ionic3 项目热更新使用 -

CSDN 热更新详解

你可能感兴趣的:(ionic3 热更新 填坑过程)