uniapp热更新和整包更新思路

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

我们知道,在打包Android App之前,我们需要先通过HX生成打包资源。如果是通过cli创建的项目,则通过以下命令生成打包资源:

1
yarn build:app-plus

生成打包资源后的目录长这样:

然后将整个目录中的所有文件拷贝到Android项目的 assets/apps/<appid>/www 中:

可以看出,所有生成的文件,其实只是一个资源目录。

热更新的原理就是:替换资源目录中的所有打包资源

热更新包分析

我们通过HX生成的热更新包:

生成的热更新包长这样:

 

可以看出,wgt其实就是一个压缩文件,将生成的资源文件全部打包。

知道原理后,我们就不一定需要通过HX创建wgt了,我们可以使用yarn build:app-plus命令先生成打包资源目录,再将其压缩为zip包,修改扩展名为wgt即可

注意:wgt包中,必须将manifest,json所在路径当做根节点进行打包。

打完包后,我们可以将其上传到OSS。

热更新方案

热更新方案:通过增加当前APP资源的版本号(versionCode),跟上一次打包时的APP资源版本号进行对比,如果比之前的资源版本号高,即进行热更新。

热更新原理:uniapp的热更新,其实是将build后的APP资源,打包为一个zip压缩包(扩展名改为wgt)。

涉及到的版本信息文件:

  • src/manifest.json
  • app.json (自己创建,用于版本对比)
  • platforms/android/app/build.gradle

注意事项:

保证以上文件的versionNameversionCode均保持一致。

热更新核心代码

以下为热更新的核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// #ifdef APP-PLUS
let downloadPath = "https://xxx.cn/apk/app.wgt"
uni.downloadFile({
    url: downloadPath,
    success: (downloadResult) => {
        if (downloadResult.statusCode === 200) {
            plus.runtime.install(downloadResult.tempFilePath, {
                force: true // 强制更新
            }, function() {
                console.log('install success...');
                plus.runtime.restart();
            }, function(e) {
                console.error(e);
                console.error('install fail...');
            });
        }
    }
})
// #endif

这里是下载wgt包,并进行安装的代码。以上代码无论如何都会下载wgt进行安装。

更新接口

实际上,在这之前,我们还需要判断是否需要更新,这就涉及到接口的部分。在此,只讲讲思路:

  1. 获取安装的版本名、版本号等信息,将其当做参数调用对应的更新接口;
  2. 接口取到这些信息,与最新版本进行对比,如果版本已经更新,返回需要更新的信息;
  3. 接口可以自行约定,怎么方便这么来。

我自己做的话,根本没写什么接口,只是创建了一个app.json文件,用于存放最新版本信息:

1
2
3
4
{
  "versionCode": "100",
  "versionName": "1.0.0"
}

将其上传到OSS,然后在下载wgt包之前进行版本检查即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// #ifdef APP-PLUS
plus.runtime.getProperty(plus.runtime.appid, function(widgetInfo) {
    console.log(widgetInfo);
    uni.request({
        url: 'https://xxx.cn/apk/app.json',
        success: (result) => {
            let { versionCode, versionName } = result.data
            console.log({ versionCode, versionName });
            // 判断版本名是否一致
            if (versionName === widgetInfo.version) {
                // 如果安装的版本号小于最新发布的版本号,则进行更新
                if (parseInt(widgetInfo.versionCode) < parseInt(versionCode)) {
                    // 下载wgt更新包
                    let downloadPath = "https://xxx.cn/apk/app.wgt"
                    uni.downloadFile({
                        url: downloadPath,
                        success: (downloadResult) => {
                            if (downloadResult.statusCode === 200) {
                                plus.runtime.install(downloadResult.tempFilePath, {
                                    force: true // 强制更新
                                }, function() {
                                    console.log('热更新成功');
                                    plus.runtime.restart();
                                }, function(e) {
                                    console.error('热更新失败,错误原因:' + e);
                                });
                            }
                        }
                    })
                } else {
                    console.log('你的版本为最新,不需要热更新');
                }
            } else {
                console.log('版本名不一致,请使用整包更新');
            }
        }
    });
});
// #endif

OK,至此,热更新就完成了。

Android整包更新

看到上面更新逻辑,如果版本名不一致,则需要下载最新的apk进行安装,在下载之前,建议给用户一个更新提示:

1
2
3
4
5
6
7
8
9
10
11
console.log('版本名不一致,请使用整包更新');
let url = "https://xxx.cn/apk/app.apk"
uni.showModal({ //提醒用户更新
    title: "更新提示",
    content: "有新的更新可用,请升级",
    success: (res) => {
        if (res.confirm) {
            plus.runtime.openURL(url);
        }
    }
})

以上代码是官方提供的,其实也可以下载apk成功后,直接调用install进行安装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
console.log('版本名不一致,请使用整包更新');
let downloadPath = "https://zys201811.boringkiller.cn/shianonline/apk/app.apk"
uni.showModal({ //提醒用户更新
    title: "更新提示",
    content: "有新的更新可用,请升级",
    success: (res) => {
        if (res.confirm) {
            // plus.runtime.openURL(downloadPath);
            uni.downloadFile({
                url: downloadPath,
                success: (downloadResult) => {
                    if (downloadResult.statusCode === 200) {
                        console.log('正在更新...');
                        plus.runtime.install(downloadResult.tempFilePath, {
                            force: true // 强制更新
                        }, function() {
                            console.log('整包更新成功');
                            plus.runtime.restart();
                        }, function(e) {
                            console.error('整包更新失败,错误原因:' + e);
                        });
                    }
                }
            })
        }
    }
})

热更新的自动化处理

知道原理后,就好办了,我们可以将其繁杂的工作自动化,以减少重复劳动。

修改package.json的相关打包脚本:

1
2
3
4
5
6
7
8
9
10
11
{
  "name": "shianaonline",
  "version": "0.1.224",
  "private": true,
  "scripts": {
    "apk": "node deploy/scripts/build-apk.js",
    "wgt": "node deploy/scripts/build-wgt.js",
    "build:app-plus-android": "cross-env NODE_ENV=production UNI_PLATFORM=app-plus UNI_OUTPUT_DIR=./platforms/android/app/src/main/assets/apps/your appid/www vue-cli-service uni-build",
    "build:app-plus-ios": "cross-env NODE_ENV=production UNI_PLATFORM=app-plus UNI_OUTPUT_DIR=./platforms/iOS/apps/your appid/www vue-cli-service uni-build",
  }
}

其中,需要替换的地方是your appid,换为自己的uniapp appid

创建app.json,用于存储当前app的版本信息:

1
2
3
4
5
6
{
  "versionName": "1.0.27",
  "versionCode": 336,
  "appPath": "https://xxx.oss.com/apk/app-release.apk",
  "wgtPath": "https://xxx.oss.com/apk/www.wgt"
}

创建自动化打包脚本build-wgt.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
const fs = require('fs')
const { execSync } = require('child_process')
const join = require('path').join
 
// 修改版本号
let app = require('../../app.json')
let manifest = require('../../src/manifest.json')
 
if (app.versionName !== manifest.versionName) {
  console.info('manifest.json和app.json的versionName不一致,请检查')
  return
}
 
if (app.versionCode !== manifest.versionCode) {
  console.info('manifest.json和app.json的versionCode不一致,请检查')
  return
}
 
// 获取build.gradle的版本名
let gradleFilePath = '../../platforms/android/app/build.gradle'
let data = fs.readFileSync(__dirname + '/' + gradleFilePath, {
  encoding: 'utf-8'
})
 
let reg = new RegExp(`versionCode ${app.versionCode}`, "gm")
 
if (!reg.test(data)) {
  console.log('platforms/android/app/build.gradle的versionCode不一致,请检查')
  return
}
 
app.versionCode += 1
manifest.versionCode += 1
 
console.log('====================');
console.log('newVersion:' + app.versionName + "." + app.versionCode);
console.log('====================');
 
let appJSON = JSON.stringify(app, null, 2)
let manifestJSON = JSON.stringify(manifest, null, 2)
 
let replaceFiles = [{
  path: '../../app.json',
  name: 'app.json',
  content: appJSON
}, {
  path: '../../src/manifest.json',
  name: 'manifest.json',
  content: manifestJSON
}]
 
replaceFiles.forEach(file => {
  fs.writeFileSync(__dirname + '/' + file.path, file.content, {
    encoding: 'utf-8'
  })
  console.log(file.name + ': 替换成功');
})
 
 
// 替换build.gradle的版本名
let result = data.replace(reg, `versionCode ${app.versionCode}`)
fs.writeFileSync(__dirname + '/' + gradleFilePath, result, {
  encoding: 'utf-8'
})
console.log('platforms/android/build.gradle: 替换成功')
 
console.log('====================');
 
// 编译
console.log(execSync('yarn build:app-plus-android', { encoding: 'utf-8'}))
 
// 打包
const compressing = require('compressing');
 
const tarStream = new compressing.zip.Stream();
 
const targetPath = './platforms/android/app/src/main/assets/apps/your appid/www'
const targetFile = './www.wgt'
 
let paths = fs.readdirSync(targetPath);
paths.forEach(function (item) {
  let fPath = join(targetPath, item);
  tarStream.addEntry(fPath);
});
 
tarStream
  .pipe(fs.createWriteStream(targetFile))
  .on('finish', upToOss)
 
// 上传至OSS
let OSS = require('ali-oss');
 
function upToOss() {
  let client = new OSS({
    region: 'oss-cn-shenzhen',
    accessKeyId: 'your accessKeyId',
    accessKeySecret: 'your accessKeySecret'
  });
 
  client.useBucket('your bucketName');
 
  let ossBasePath = `apk`
 
  put(`${ossBasePath}/www.wgt`, 'www.wgt')
  put(`${ossBasePath}/wgts/${app.versionCode}/www.wgt`, 'www.wgt')
  put(`webview/vod.html`, 'src/hybrid/html/vod.html')
  put(`${ossBasePath}/app.json`, 'app.json')
 
  async function put (ossPath, localFile) {
    try {
      await client.put(ossPath, localFile);
      console.log(`${localFile}上传成功:${ossPath}`);
    } catch (err) {
      console.log(err);
    }
  }
}
 
console.log('====================');
console.log('更新完毕,newVersion:' + app.versionName + "." + app.versionCode);
console.log('====================');

以上打包脚本,做了以下工作:

  1. 验证版本号和版本名是否正确,如果不正确,终止脚本
  2. 修改当前APP版本号
  3. 生成APP打包资源
  4. 将打包资源做成zip包(扩展名改为wgt)
  5. 上传wgt资源包到OSS

一键式操作,打包为wgt只需要执行:

1
yarn wgt

Android整包更新的自动化处理

Android整包更新需要在AndroidManifest.xml中配置:

1
2
<uses-permission android:name="android.permission.INSTALL_PACKAGES"/>
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>

Android整包更新的业务代码跟热更新一样,都可以调用plus.runtime.install来实现。

主要还是说一下打包apk的自动化脚本build-apk.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
const fs = require('fs')
const { execSync } = require('child_process')
 
let app = require('../../app.json')
let manifest = require('../../src/manifest.json')
 
if (app.versionName !== manifest.versionName) {
  console.log('manifest.json和app.json的versionName不一致,请检查')
  return
}
 
if (app.versionCode !== manifest.versionCode) {
  console.log('manifest.json和app.json的versionCode不一致,请检查')
  return
}
 
// 获取build.gradle的版本名
let gradleFilePath = '../../platforms/android/app/build.gradle'
let data = fs.readFileSync(__dirname + '/' + gradleFilePath, {
  encoding: 'utf-8'
})
 
let reg = new RegExp(`versionName "${app.versionName}"`, "gm")
 
if (!reg.test(data)) {
  console.info('platforms/android/app/build.gradle的versionName不一致,请检查')
  return
}
 
let regCode = new RegExp(`versionCode ${app.versionCode}`, "gm")
if (!regCode.test(data)) {
  console.info('platforms/android/app/build.gradle的versionCode不一致,请检查')
  return
}
 
// 修改版本名
let appVersionName = app.versionName.split('.')
let manifestVersionName = manifest.versionName.split('.')
 
let appVersionLast = Number(appVersionName[2])
let manifestVersionLast = Number(manifestVersionName[2])
 
appVersionLast += 1
manifestVersionLast += 1
 
app.versionName = appVersionName[0] + '.' + appVersionName[1] + '.'  + appVersionLast
manifest.versionName = manifestVersionName[0] + '.'  + manifestVersionName[1] + '.'  + manifestVersionLast
 
console.log('====================');
console.log('newVersion:' + app.versionName + "." + app.versionCode);
console.log('====================');
 
let appJSON = JSON.stringify(app, null, 2)
let manifestJSON = JSON.stringify(manifest, null, 2)
 
// 替换项目版本名
let replaceFiles = [{
  path: '../../app.json',
  name: 'app.json',
  content: appJSON
}, {
  path: '../../src/manifest.json',
  name: 'manifest.json',
  content: manifestJSON
}]
 
replaceFiles.forEach(file => {
  fs.writeFileSync(__dirname + '/' + file.path, file.content, {
    encoding: 'utf-8'
  })
  console.log(file.name + ': 替换成功');
})
 
// 替换build.gradle的版本名
let result = data.replace(reg, `versionName "${app.versionName}"`)
fs.writeFileSync(__dirname + '/' + gradleFilePath, result, {
  encoding: 'utf-8'
})
console.log('platforms/android/build.gradle: 替换成功')
 
console.log('====================');
 
// 打包资源
console.log(execSync(`yarn build:app-plus-android`, { encoding: 'utf-8'}))
 
// 打包apk
console.log(execSync(`cd platforms/android && gradle assembleRelease`, { encoding: 'utf-8'}))
 
// 上传至OSS
let OSS = require('ali-oss');
 
function upToOss() {
  let client = new OSS({
    region: 'oss-cn-shenzhen',
    accessKeyId: 'your accessKeyId',
    accessKeySecret: 'your accessKeySecret'
  });
 
  client.useBucket('your bucketName');
 
  let ossBasePath = `apk`
 
  put(`${ossBasePath}/app-release.apk`, 'platforms/android/app/build/outputs/apk/release/app-release.apk')
  put(`${ossBasePath}/apks/${app.versionName}/app-release.apk`, 'platforms/android/app/build/outputs/apk/release/app-release.apk')
  put(`${ossBasePath}/apks/${app.versionName}/output.json`, 'platforms/android/app/build/outputs/apk/release/output.json')
  put(`webview/vod.html`, 'src/hybrid/html/vod.html')
  put(`${ossBasePath}/app.json`, 'app.json')
 
  async function put (ossPath, localFile) {
    try {
      await client.put(ossPath, localFile);
      console.log(`${localFile}上传成功:${ossPath}`);
    } catch (err) {
      console.log(err);
    }
  }
}
 
upToOss()
 
console.log('====================');
console.log('更新完毕,newVersion:' + app.versionName + "." + app.versionCode);
console.log('====================');

以上打包脚本,做了以下工作:

  1. 验证版本号和版本名是否正确,如果不正确,终止脚本
  2. 修改当前APP版本名
  3. 生成APP打包资源
  4. 打包Android APP(扩展名apk)
  5. 上传apk到OSS

一键式操作,打包为apk只需要执行:

1
yarn apk

安装更新

我们看看plus.runtime.install的官方文档:

1
void plus.runtime.install(filePath, options, installSuccessCB, installErrorCB);

支持以下类型安装包:

  1. 应用资源安装包(wgt),扩展名为'.wgt';
  2. 应用资源差量升级包(wgtu),扩展名为'.wgtu';
  3. 系统程序安装包(apk),要求使用当前平台支持的安装包格式。 注意:仅支持本地地址,调用此方法前需把安装包从网络地址或其他位置放置到运行时环境可以访问的本地目录。

知道了调用方式就好办了,我们封装一个检测更新的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
class Utils {
  ...
 
  // 获取APP版本信息
  getVersion() {
    let {versionName, versionCode} = manifest
    return {
      versionName,
      versionCode,
      version: `${versionName}.${versionCode}`
    }
  }
 
  // 检测更新
  detectionUpdate(needRestartHotTip = false, needRestartFullTip = false) {
    return new Promise(async (resolve, reject) => {
      let appInfo = this.getVersion()
      uni.request({
          url: 'https://xxx.oss.com/apk/app.json',
          success: async (result) => {
            let { versionCode, versionName, appPath, wgtPath } = result.data
            let versionInfo = {
              appPath,
              wgtPath,
              newestVersion: `${versionName}.${versionCode}`,
              newestVersionCode: versionCode,
              newestVersionName: versionName,
              currentVersion: appInfo.version,
              currentVersionCode: appInfo.versionCode,
              currentVersionName: appInfo.versionName
            }
 
            // 判断版本名是否一致
            try {
              if (versionName === appInfo.versionName) {
                // 如果安装的版本号小于最新发布的版本号,则进行更新
                if (appInfo.versionCode < versionCode) {
                  // 下载wgt更新包
                  if (needRestartHotTip) {
                    uni.showModal({
                      title: '提示',
                      content: `检测到新版本 ${versionInfo.newestVersion} (当前版本:${versionInfo.currentVersion}),是否立即更新并重启应用,以使更新生效?`,
                      success: async (res) => {
                        if (res.confirm) {
                          await this.downloadAndInstallPackage(wgtPath)
                          plus.runtime.restart();
                          resolve({code: 1, data: versionInfo})
                        } else if (res.cancel) {
                          await this.downloadAndInstallPackage(wgtPath)
                          resolve({code: 1, data: versionInfo})
                        }
                      }
                    })
                  } else {
                    await this.downloadAndInstallPackage(wgtPath)
                    resolve({code: 1, data: versionInfo})
                  }
                } else {
                  resolve({code: 0, data: versionInfo})
                  console.log('你的版本为最新,不需要热更新');
                }
              } else {
                // 整包更新
                console.log('版本名不一致,请使用整包更新');
                if (needRestartFullTip) {
                  uni.showModal({
                    title: '提示',
                    content: `检测到新版本 ${versionInfo.newestVersion} (当前版本:${versionInfo.currentVersion}),是否立即更新应用?`,
                    success: async (res) => {
                      if (res.confirm) {
                        // await this.downloadAndInstallPackage(appPath)
                        plus.runtime.openURL(appPath)
                        resolve({code: 2, data: versionInfo})
                      } else if (res.cancel) {}
                    }
                  })
                } else {
                  // await this.downloadAndInstallPackage(appPath)
                  plus.runtime.openURL(appPath)
                  resolve({code: 2, data: versionInfo})
                }
              }
            } catch (e) {
              reject(e)
            }
          }
      });
    })
  }
 
  // 下载并安装更新包
  downloadAndInstallPackage(url) {
    console.log('开始下载更新包:' + url)
    return new Promise((resolve, reject) => {
      uni.downloadFile({
        url: url,
        success: (downloadResult) => {
          if (downloadResult.statusCode === 200) {
            console.log('正在更新...');
            plus.runtime.install(downloadResult.tempFilePath, {
              force: true // 强制更新
            }, function() {
              console.log('更新成功');
              resolve()
            }, function(e) {
              console.error('更新失败,错误原因:' + JSON.stringify(e));
              reject(e)
            });
          }
        }
      })
    })
  }
}
 
...

创建Utils的实例,并挂载到Vue的原型中,调用起来非常方便:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
 
let res = await this.$utils.detectionUpdate(false, true)
if (res.code === 1) {
  uni.showModal({
    title: '提示',
    content: `发现新的热更新包,是否立即重启APP以使更新生效?`,
    success: async (res) => {
      if (res.confirm) {
        plus.runtime.restart()
      } else if (res.cancel) {}
    }
  })
}

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...
 
let res = await this.$utils.detectionUpdate(true, true)
if (res.code === 0) {
  let {currentVersion} = res.data
  uni.showModal({
    title: '提示',
    content: `你的APP为最新版本 ${currentVersion},不需要更新!`,
    showCancel: false,
    success: async (res) => {
      if (res.confirm) {
      } else if (res.cancel) {}
    }
  })
}

实战案例代码及过程

思路

1
2
3
服务器中存储着最新版本号,前端进行查询
可以在首次进入应用时进行请求版本号进行一个匹对
如果版本号一致则不提示,反之则提示进行更新执行更新操作

1.封装一个对比版本号的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
 * 对比版本号,如需要,请自行修改判断规则
 * 支持比对 ("3.0.0.0.0.1.0.1", "3.0.0.0.0.1")  ("3.0.0.1", "3.0")  ("3.1.1", "3.1.1.1") 之类的
 * @param {Object} v1
 * @param {Object} v2
 * v1 > v2 return 1
 * v1 < v2 return -1
 * v1 == v2 return 0
 */
function compare(v1 = '0', v2 = '0') {
    v1 = String(v1).split('.')
    v2 = String(v2).split('.')
    const minVersionLens = Math.min(v1.length, v2.length);
 
    let result = 0;
    for (let i = 0; i < minVersionLens; i++) {
        const curV1 = Number(v1[i])
        const curV2 = Number(v2[i])
 
        if (curV1 > curV2) {
            result = 1
            break;
        } else if (curV1 < curV2) {
            result = -1
            break;
        }
    }
 
    if (result === 0 && (v1.length !== v2.length)) {
        const v1BiggerThenv2 = v1.length > v2.length;
        const maxLensVersion = v1BiggerThenv2 ? v1 : v2;
        for (let i = minVersionLens; i < maxLensVersion.length; i++) {
            const curVersion = Number(maxLensVersion[i])
            if (curVersion > 0) {
                v1BiggerThenv2 ? result = 1 : result = -1
                break;
            }
        }
    }
    return result;
}

2.封装更新函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
var updateUseModal = (packageInfo) => {
    const {
        title, // 标题
        contents, // 升级内容
        is_mandatory, // 是否强制更新
        url, // 安装包下载地址
        platform, // 安装包平台
        type // 安装包类型
    } = packageInfo;
 
    let isWGT = type === 'wgt'
    let isiOS = !isWGT ? platform.includes('iOS') : false;
    let confirmText = isiOS ? '立即跳转更新' : '立即下载更新'
 
    return uni.showModal({
        title,
        content: contents,
        showCancel: !is_mandatory,
        confirmText,
        success: res => {
            if (res.cancel) return;
 
            // 安装包下载
            if (isiOS) {
                plus.runtime.openURL(url);
                return;
            }
            let waiting =  plus.nativeUI.showWaiting("正在下载 - 0%"); 
            // uni.showLoading({
            //  title: '安装包下载中'
            // });
            // wgt 和 安卓下载更新
            const downloadTask = uni.downloadFile({
                url,
                success: res => {
                    if (res.statusCode !== 200) {
                        console.error('下载安装包失败', err);
                        return;
                    }
                    // 下载好直接安装,下次启动生效
                    plus.runtime.install(res.tempFilePath, {
                        force: false
                    }, () => {
                        uni.hideLoading()
                        if (is_mandatory) {
                            //更新完重启app
                            plus.runtime.restart();
                            return;
                        }
                        uni.showModal({
                            title: '安装成功是否重启?',
                            success: res => {
                                if (res.confirm) {
                                    //更新完重启app
                                    plus.runtime.restart();
                                }
                            }
                        });
                    }, err => {
                        uni.hideLoading()
                        uni.showModal({
                            title: '更新失败',
                            content: err.message,
                            showCancel: false
                        });
                    });
                },
                //接口调用结束
                complete: ()=>{
                    uni.hideLoading();
                    downloadTask.offProgressUpdate();//取消监听加载进度
                }
            });
            //监听下载进度
            downloadTask.onProgressUpdate(res => {
                // state.percent = res.progress;
                waiting.setTitle("正在下载 - "+res.progress+"%");
                // console.log('下载进度百分比:' + res.progress); // 下载进度百分比
                // console.log('已经下载的数据长度:' + res.totalBytesWritten); // 已经下载的数据长度,单位 Bytes
                // console.log('预期需要下载的数据总长度:' + res.totalBytesExpectedToWrite); // 预期需要下载的数据总长度,单位 Bytes
            });
        }
    });
}

3.用变量接收实现函数(在函数中使用上方封装的函数)并导出

fRequestWithToken为我封装的请求方法,可自行进行使用axios进行请求也行!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
var fCheckVersion = (cb) => {
    // #ifdef APP-PLUS
    plus.runtime.getProperty(plus.runtime.appid, function(widgetInfo) {
        // console.log(widgetInfo.version)
        // console.log(plus.runtime.version)
        // console.log(widgetInfo.version)
        var nVerSta = compare(plus.runtime.version, widgetInfo.version),
            sLaststVer = plus.runtime.version;
        if (widgetInfo.version) {
            if (nVerSta == 1) {
                console.log(plus.runtime.version)
                sLaststVer = plus.runtime.version
            } else if (nVerSta == -1) {
                console.log(widgetInfo.version)
                sLaststVer = widgetInfo.version
            }
        }
        console.log(sLaststVer)
        //发送请求进行匹对,我这里数据库设定的是如果返回null则版本号一致,反之需要更新!!!
        fRequestWithToken({
            ajaxOpts: {
                url: URLS_COM.d_lastVer,
                data: {
                    versionCode: sLaststVer
                }
            },
            showloading: false,
            silence:true
        }).then(data => {
            console.log(data)
            // console.log('################')
            if (data) {
                var sUrl = '',
                    type = '';
                if (data.wgtName) {
                    sUrl = data.wgtName;
                    type = "wgt"
                } else {
                    sUrl = data.pkgName;
                    type = "pkg";
                }
 
                updateUseModal({
                    title: data.title||"",
                    contents: data.note||'',
                    is_mandatory: true,
                    url: sUrl,
                    platform: 'android',
                    type: type // 安装包类型
                })
            }
        }).catch((res)=>{
            cb&&cb()
            console.log(res)
        })
    })
    // #endif
}
 
export {
    fCheckVersion
}

使用

可在App.vue中进行使用,根据项目需求而定

1.引入封装好的函数

路径自己记得填写自己封装的位置

1
import{fCheckVersion} from '@/common/project/checkversion.js'

2.然后可以在onLoad函数中进行触发

1
2
3
onLoad() {
    fCheckVersion();//检查更新
}

这样就实现了热更新
然后的话只需要进行打包个热更新的包

 

 后端进行上传至服务器进行更新数据
本地再进行一个云打包,记得在mainifest.json文件中进行版本号的修改,修改成低于热更新包的版本号即可

本文部分内容转载于:

https://blog.csdn.net/m_xiaozhilei/article/details/126485684

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

posted @   林恒  阅读(2475)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 【.NET】调用本地 Deepseek 模型
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)
欢迎阅读『uniapp热更新和整包更新思路』
点击右上角即可分享
微信分享提示