安卓脚本开发脚本分发

安卓自动化脚本热更新怎么做?不重新打包 APK 也能更新代码

不重新打包 APK 也能把新代码下发到用户手机:update.json 配置、服务端接口返回格式、UI 启动自动更新与脚本内手动更新的完整流程。

约 9 分钟

热更新热的是 iec,先看 update.json

很多人做安卓自动化脚本,都经历过这种场景:脚本部署到几十台手机上,突然发现一个逻辑 bug 或者想加个新功能,只能重新打包 APK、一个个传包、让用户重新安装。一台两台还能忍,几十台上百台就是灾难。一定要重装吗?不用。热更新就是解决这个问题的——不重新打包 APK,直接把编译后的代码文件下发到手机。

EasyClick 工作流画布展示代码热更新与版本管理流程

很多人第一次接触“热更新”会误以为是要更新 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 落地。官网提供完整文档、开发工具与自动化产品,免费体验。

访问 iEasyClick 官网 →