const apkUrl = "https://down.pics/down/shizuku.zip";
const savePath = "/sdcard/Download/shizuku.zip";
if (!files.exists(savePath)) {
log("📥 开始下载 Shizuku");
//showStatus("📥 开始下载 Shizuku",2);
let res = http.get(apkUrl);
files.writeBytes(savePath, res.body.bytes());
log("✅ 下载完成");
//showStatus("✅ 下载完成",2);
} else {
log("✅ 使用本地安装包");
} 这个下载代码为什么会出错
发生错误:JavaException: okhttp3.internal.http2.ConnectionShutdownException: null
出现这个错误,什么原因?
回答
这段代码在 Hamibot 环境中运行出错,通常由以下几个核心原因导致:
- 存储权限未授予:这是最常见的原因。Android 系统要求应用访问外部存储(如
/sdcard/Download/)时必须获得“存储权限”。如果未在系统设置或 Hamibot 授权弹窗中允许,files.writeBytes会抛出异常。 - 缺乏异常处理:代码没有使用
try-catch包裹网络请求和文件写入操作。一旦网络波动、DNS 解析失败或磁盘空间不足,脚本会直接中断且无法定位具体错误。 - 未校验 HTTP 状态码:
http.get建立连接成功不代表资源获取成功。如果服务器返回 404 或 500,res.body.bytes()可能获取到错误的数据包,导致文件损坏。 - 父目录不存在风险:虽然
files.writeBytes通常会自动创建缺失的父目录,但在某些 Android 版本或特定路径下,如果父目录不可写,写入仍会失败。
以下是优化后的代码示例,增加了权限检查提示、异常捕获和状态码验证:
// 文档地址 https://docs.hamibot.com/
try {
const apkUrl = "https://down.pics/down/shizuku.zip";
const savePath = "/sdcard/Download/shizuku.zip";
// 检查文件是否已存在
if (!files.exists(savePath)) {
log("📥 开始下载 Shizuku");
// 发起网络请求
let res = http.get(apkUrl);
// 检查响应状态码是否为 200
if (res.statusCode !== 200) {
throw new Error("下载失败,服务器返回状态码:" + res.statusCode);
}
// 写入二进制文件
files.writeBytes(savePath, res.body.bytes());
log("✅ 下载完成");
} else {
log("✅ 使用本地安装包");
}
} catch (e) {
// 捕获并打印错误信息,方便调试
log("❌ 发生错误:" + e);
toast("下载出错,请检查网络和权限");
}
关键改进说明:
- 增加 try-catch:捕获网络超时、IO 异常等运行时错误,防止脚本崩溃且能输出具体报错信息。
- 校验 statusCode:确保只有当服务器返回 200 OK 时才进行文件写入,避免下载错误页面。
- 权限提醒:如果运行时报错
Permission denied,请前往手机设置 -> 应用管理 -> Hamibot -> 权限,开启“存储”或“文件和媒体”权限。 - 兼容性:代码遵循 Rhino 引擎规范,未使用箭头函数或默认参数,确保在 Hamibot 云端或本地环境稳定运行。
如果问题依旧,建议尝试将保存路径改为 files.cwd() + '/shizuku.zip' 测试是否为特定目录权限问题。
这个错误 JavaException: okhttp3.internal.http2.ConnectionShutdownException: null 表明底层网络库 OkHttp 的 HTTP/2 连接被意外中断了。这通常不是代码逻辑错误,而是网络环境或服务器交互层面的问题。
常见原因:
- 网络不稳定:手机在网络切换(如 Wi-Fi 切到移动数据)或信号弱时,TCP 连接可能重置,导致正在进行的 HTTP/2 流中断。
- 服务器主动断开:目标服务器可能因为超时、负载过高或安全策略,主动关闭了长连接。
- HTTP/2 协议兼容性问题:部分老旧服务器或特定配置对 HTTP/2 支持不佳,容易在传输过程中断开。
- 中间设备干扰:某些路由器、防火墙或杀毒软件可能会拦截并切断长时间空闲的连接。
解决方案:
- 增加重试机制:这是最有效的办法。将网络请求包裹在循环中,遇到此类异常时自动重试几次。
- 检查网络连接:确保设备网络通畅,尝试切换 Wi-Fi 或流量测试。
- 缩短超时时间:如果请求耗时过长,更容易触发连接断开,尽量优化请求效率。
- 避免大文件直接下载:如果是下载大文件,考虑分块下载或使用更稳定的 CDN 链接。
以下是增加了重试逻辑的优化代码,能够自动处理临时的网络中断:
// 文档地址 https://docs.hamibot.com/
function downloadFileWithRetry(url, savePath, maxRetries) {
var retries = 0;
while (retries < maxRetries) {
try {
log("📥 开始下载,尝试次数:" + (retries + 1));
var res = http.get(url);
if (res.statusCode !== 200) {
throw new Error("状态码异常:" + res.statusCode);
}
files.writeBytes(savePath, res.body.bytes());
log("✅ 下载成功");
return true;
} catch (e) {
retries++;
log("⚠️ 下载失败:" + e);
if (retries >= maxRetries) {
log("❌ 已达到最大重试次数,放弃下载");
return false;
}
log("⏳ 等待 3 秒后重试...");
sleep(3000);
}
}
}
// 调用示例
var apkUrl = "https://down.pics/down/shizuku.zip";
var savePath = "/sdcard/Download/shizuku.zip";
if (!files.exists(savePath)) {
var success = downloadFileWithRetry(apkUrl, savePath, 3);
if (!success) {
toast("下载失败,请检查网络");
}
} else {
log("✅ 使用本地安装包");
}
代码改进点:
- 封装重试函数:将下载逻辑封装为
downloadFileWithRetry,便于复用和管理重试次数。 - 异常捕获与循环:使用
while循环配合try-catch,捕获ConnectionShutdownException等网络异常并自动重试。 - 延时等待:在重试前加入
sleep(3000),避免频繁请求导致被封禁或加重网络负担。 - 变量声明:使用
var声明变量,确保在 Rhino 1.7.15 引擎下的最大兼容性。
如果多次重试后仍然报错,建议检查该下载链接在手机浏览器中是否能正常打开,以排除服务器端问题。