热更新热的是 iec,先看 update.json
很多人做安卓自动化脚本,都经历过这种场景:脚本部署到几十台手机上,突然发现一个逻辑 bug 或者想加个新功能,只能重新打包 APK、一个个传包、让用户重新安装。一台两台还能忍,几十台上百台就是灾难。一定要重装吗?不用。热更新就是解决这个问题的——不重新打包 APK,直接把编译后的代码文件下发到手机。
很多人第一次接触“热更新”会误以为是要更新 APK,不是:热更新的是编译后的 iec 文件,不是打包的脚本 APK。 APK 只是壳,装一次就不用动了,业务逻辑编译成 iec 文件、通过热更新机制下发,好处非常直接——免发版,改代码不用重新走打包、签名、分发流程;秒级下发,用户下次启动脚本自动拉取最新代码;故障止损,线上出问题改一版代码立刻覆盖,不用等用户手动升级。
在工程目录下找到(或新建)update.json,热更新的所有基础配置都在这里:
{
"update_url": "http://baidu.com/update",
"version": "1.0.0",
"appendDeviceInfo": true,
"timeout": 30000
}
各字段含义:
| 字段 | 说明 |
|---|---|
update_url |
服务端更新接口地址,需要自己编写服务端接口(或用官方热更新服务) |
version |
当前脚本的版本号 |
timeout |
请求超时时间,单位毫秒,最低 1000 |
appendDeviceInfo |
是否在请求时附加设备基础信息 |
把 appendDeviceInfo 设为 true 后,请求会自动带上设备参数,方便服务端做差异化下发,地址会变成类似:
http://baidu.com/update?version=1&deviceId=7521e5d9eeec4f58b71dea8b78c414d5&apkVersion=9.22.0&osVersion=12&pkgName=com.gibb.easyclick&model=LNA-AL00&ecVersion=9.22.0&brand=HUAWEI&androidId=82a3b055470ebe1a
参数含义:
| 参数 | 说明 |
|---|---|
version |
当前运行的 iec 版本 |
deviceId |
EC 生成的设备 ID(可能丢失,不一定能作为设备唯一标识) |
apkVersion |
APK 打包版本 |
osVersion |
系统版本 |
ecVersion |
实际使用的 EC 版本 |
pkgName |
打包的包名 |
model / brand |
机型与品牌 |
androidId |
Android ID |
服务端怎么写,两种更新时机
配置好后打包运行,客户端会自动用 GET 方式请求 update_url 并带上版本参数,例如 http://baidu.com/update?version=1.0.0,版本比较逻辑在服务端自行处理。服务端返回分两种情况:无需更新时直接返回空字符串即可,不要返回 JSON;需要更新时返回更新信息 JSON:
{
"download_url": "http://baidu.com/aaa.iec",
"version": "1.1.0",
"dialog": true,
"msg": "优化部分问题",
"force": false
}
字段说明:
| 字段 | 说明 |
|---|---|
download_url |
新包的下载地址 |
version |
新包的版本号 |
dialog |
是否用对话框形式展示更新提示(true 弹窗 / false 静默) |
msg |
对话框中要显示的消息 |
force |
对话框模式下是否强制更新(true 无法取消) |
客户端拿到这个 JSON 后,会下载最新的 iec 包并加载使用。担心下载失败可以用严格模式校验 MD5,返回里加上 md5(iec 文件的 MD5 值,有该字段会强制校验文件完整性)和 download_timeout(下载超时时间,秒,不填默认 60 秒):
{
"download_url": "http://baidu.com/aaa.iec",
"version": "1.1.0",
"dialog": true,
"msg": "优化部分问题",
"force": false,
"md5": "服务器自行校验的 iec 文件的 md5 值",
"download_timeout": 60
}
更新时机有两种。UI 启动更新:只要 update.json 配置无误,用户打开脚本界面时会自动请求更新,无需任何额外代码,这是最常用的方式——用户打开脚本,最新代码已经在路上了。脚本内热更新:脚本执行期间也可以配合代码触发,核心流程是请求更新 → 下载新包 → 重启脚本:
function main() {
// 从项目文件的 update.json 中获取版本号
let version = JSON.parse(readIECFileAsString("update.json")).version
toast("Hello World - " + version);
// 请求服务器是否有新版本(使用自定义 url 模式)
let updateResult = hotupdater.updateReq("http://baidu.com", version, true, 9000);
logd("请求更新是否有: " + updateResult);
if (!updateResult) {
logw("请求失败错误信息: " + hotupdater.getErrorMsg());
} else {
// 有更新,下载新的版本
let path = hotupdater.updateDownload();
logd("下载路径为: " + path);
if (!path) {
logw("下载 IEC 文件错误信息: " + hotupdater.getErrorMsg());
} else {
// 重启脚本,加载新版本
restartScript(path, true, 3)
return;
}
}
}
main();
脚本内热更新用到的方法:
| 方法 | 作用 |
|---|---|
hotupdater.updateReq(updateUrl, version, appendDeviceInfo, timeout) |
请求更新接口,返回 true 表示需要更新,false 可能是无需更新或请求失败(用 getErrorMsg 查看具体信息) |
hotupdater.updateDownload() |
下载热更新返回的 iec 文件,返回下载后文件路径 |
hotupdater.getUpdateResp() |
获取热更新的请求结果 |
hotupdater.getErrorMsg() |
获取请求或下载的错误信息 |
updateReq 的第一个参数不传时,会使用 update.json 里配置的数据;version 建议使用整型数字。下载完成后调用 restartScript(path, true, 3) 重启脚本,新代码立即生效。
几个容易踩的坑,和从玩具到产品
几个容易踩的坑。版本号要一致:update.json 里的版本号必须和服务端接口返回的版本号逻辑保持一致,否则可能更新异常。无需更新别返回 JSON:服务端判断无更新时直接返回空字符串,返回 JSON 会被当成更新数据处理。设备指纹别当唯一标识:deviceId 是 EC 生成的,可能丢失,做设备绑定类逻辑不要只依赖它。生产环境注意时效:建议把 appendDeviceInfo 打开,按设备维度控制灰度发布,避免一次全量下发出问题。
热更新是安卓脚本从“个人玩具”走向“商业产品”的关键能力:配置一次、长期受益,update.json 配好后后续每次迭代都走热更新,告别打包传包;两种更新方式,UI 启动自动更新适合绝大多数场景,脚本内手动更新适合对时机有要求的业务;严格模式更稳,生产环境建议带上 md5 校验,防止下载不完整导致脚本异常;不想写服务端可以直接用官方热更新服务(对接阿里云 OSS),上传 iec 文件即可,自动生成下载地址和 md5。对于把脚本卖出去、部署到几十上百台设备的团队,热更新是运维效率的刚需。改一行代码要重装一次 APK 的时代,该翻篇了。还靠手动传包?太慢了。
想要真实跑起来?
本文介绍的方案均可基于 EasyClick 能力在 iEasyClick 落地。官网提供完整文档、开发工具与自动化产品,免费体验。