多线程GET下载轮询等待工具
多线程GET下载轮询等待工具/**
* AIWROK 轮询等待工具 - 演示脚本(录屏版)
* 故事线:模拟 APK 下载 → 等待文件生成 → 等待安装完成 → 成功启动
*
*/
importClass(java.lang.Thread);
// ======================== 基础 ========================
function doSleep(ms) { try { Thread.sleep(ms); } catch (e) {} }
// 带节奏的打印:换行 + 延时
function step(msg, delayMs) {
printl(msg);
doSleep(delayMs || 300);
}
function separator() {
printl("");
printl("──────────────────────────────────────────────────");
printl("");
}
// ======================== 通用轮询(核心) ========================
/**
* 轮询等待某个条件成立
* @param {Function} condition—— 每次返回 truthy 表示成立
* @param {int} timeoutMs —— 总超时(默认 10s)
* @param {int} intervalMs —— 轮询间隔(默认 200ms)
* @param {String} label —— 日志标签
* @returns {Object} { success, waitedMs, lastValue, rounds }
*/
function poll(condition, timeoutMs, intervalMs, label) {
timeoutMs= timeoutMs|| 10000;
intervalMs = intervalMs || 200;
label = label || "轮询";
var start= new Date().getTime();
var rounds = 0;
var lastVal = null;
while (true) {
lastVal = condition();
if (lastVal) {
var waited = new Date().getTime() - start;
printl("✅ [" + label + "] 条件达成!第 " + rounds + " 次轮询 · 耗时 " + waited + " ms");
return { success: true, waitedMs: waited, lastValue: lastVal, rounds: rounds };
}
// 每 5 次打一次进度(录屏看着不密不疏)
if (rounds % 5 === 0) {
var elapsed = rounds * intervalMs;
printl("⏳ [" + label + "] 等待中... 已轮询 " + rounds + " 次 · " + elapsed + " / " + timeoutMs + " ms");
}
if (new Date().getTime() - start >= timeoutMs) {
printl("❌ [" + label + "] 超时!(" + timeoutMs + " ms 内未达成)");
return { success: false, waitedMs: timeoutMs, lastValue: lastVal, rounds: rounds };
}
doSleep(intervalMs);
rounds++;
}
}
// ======================== 便捷方法 ========================
/** 等待文件存在 */
function waitForFile(path, timeoutMs, intervalMs) {
return poll(function() {
try { return new java.io.File(path).exists(); } catch (e) { return false; }
}, timeoutMs, intervalMs, "文件出现 " + path);
}
/** 等待文件大小 >= minBytes(模拟下载中) */
function waitForFileSize(path, minBytes, timeoutMs, intervalMs) {
var label = "文件大小 ≥ " + minBytes + " 字节";
return poll(function() {
try {
var f = new java.io.File(path);
if (!f.exists()) return false;
var size = f.length();
// 顺便打印当前进度(录屏好看)
if (size > 0 && Math.random() < 0.3) {
printl(" 📥 当前大小: " + size + " / " + minBytes + " 字节 " +
Math.floor(size / minBytes * 100) + "%");
}
return size >= minBytes;
} catch (e) { return false; }
}, timeoutMs, intervalMs, label);
}
/** 等待 HTTP 200 */
function waitForHttp(url, timeoutMs, intervalMs) {
return poll(function() {
try {
var res = http.get(url);
return res && res.code === 200;
} catch (e) { return false; }
}, timeoutMs, intervalMs, "接口 " + url);
}
/** 等待某条件返回 true,同时打印阶段标题 */
function stage(title, condition, timeoutMs, intervalMs, label) {
printl("");
printl("▶ " + title);
// 造一条下划线(ES5 兼容)
var line = "";
for (var i = 0; i < title.length + 2; i++) line += "─";
printl(line);
return poll(condition, timeoutMs, intervalMs, label || title);
}
// ======================== 录屏演示 ========================
function demo() {
// ══════════════════════════════════════════════════
//开场
// ══════════════════════════════════════════════════
printl("╔══════════════════════════════════════════════╗");
printl("║ ║");
printl("║ 🛠️AIWROK sleep 轮询等待工具 演示 ║");
printl("║ ║");
printl("║ 「模拟完整 APK 下载 → 安装流程」 ║");
printl("║ ║");
printl("╚══════════════════════════════════════════════╝");
doSleep(1200); // 给录屏一个"标题定帧"时间
printl("");
printl("📖 场景说明:");
printl(" 我们将模拟一个完整的自动化流程 ——");
printl(" 从服务器下载 APK 文件 → 等待写入磁盘 →");
printl(" 等待文件大小达标 → 最后验证接口就绪。");
printl("");
printl(" 用到的核心函数只有一个:poll()");
printl(" 它负责:每隔 N ms 检查一次条件,");
printl(" 条件成立立即返回,超时则返回失败。");
printl("");
doSleep(1500);
// ══════════════════════════════════════════════════
//第一幕:后台线程模拟下载
// ══════════════════════════════════════════════════
separator();
printl("【第一幕】启动后台下载任务");
doSleep(800);
var apkPath = "/sdcard/Download/demo_app_v1.2.apk";
var targetSize = 4096; // 模拟 4KB 的"APK"(实际写文字进去)
// 先清理残留
try { new java.io.File(apkPath).delete(); } catch (e) {}
step("📱 目标 APK: " + apkPath, 500);
step("📦 模拟大小: " + targetSize + " 字节", 500);
step("🚀 启动下载线程...", 800);
// 后台线程:分 4 次逐步写入文件(模拟渐进下载)
new thread().runJsCode(function() {
printl(" [后台] 连接服务器...");
doSleep(400);
printl(" [后台] 开始下载...");
var w = new java.io.FileWriter(apkPath);
var chunk = 1024;
var total = 0;
for (var i = 0; i < 4; i++) {
// 写 1KB:循环写单字符
var data = "";
for (var j = 0; j < chunk; j++) data += String.fromCharCode(65 + i);
w.write(data);
w.flush();
total += chunk;
printl(" [后台] 已写入 " + total + " / " + targetSize + " 字节");
doSleep(500); // 故意慢一点,让轮询有东西可看
}
w.close();
printl(" [后台] ✅ 下载完成,文件已落盘");
}, "APK下载线程");
doSleep(800); // 让后台线程先跑起来打一行
// ══════════════════════════════════════════════════
//第二幕:轮询等待文件出现
// ══════════════════════════════════════════════════
separator();
printl("【第二幕】主线程轮询等待文件出现");
printl(" 场景:后台在下载,主线程不能干等,");
printl(" 需要时刻盯着文件是否已创建。");
doSleep(1000);
var r1 = stage("等待 APK 文件出现...", function() {
try { return new java.io.File(apkPath).exists(); } catch (e) { return false; }
}, 5000, 200, "waitForFile");
if (r1.success) {
step("📂 文件已在磁盘上!耗时 " + r1.waitedMs + " ms", 600);
} else {
step("❌ 文件迟迟没出现,下载可能卡住了", 600);
}
doSleep(1000);
// ══════════════════════════════════════════════════
//第三幕:轮询等待文件大小达标
// ══════════════════════════════════════════════════
separator();
printl("【第三幕】继续等 —— 文件大小达标才算下载完成");
printl(" 场景:文件虽然创建了,但可能只写了一半。");
printl(" 需要等大小 ≥ 期望值,才能开始安装。");
doSleep(1000);
var r2 = waitForFileSize(apkPath, targetSize, 8000, 300);
if (r2.success) {
step("📊 文件大小达标!已完整写入 " + targetSize + " 字节", 600);
}
doSleep(1000);
// ══════════════════════════════════════════════════
//第四幕:演示超时场景(等一个不存在的东西)
// ══════════════════════════════════════════════════
separator();
printl("【第四幕】演示:条件永远不成立 → 触发超时");
printl(" 场景:实际业务中经常遇到 ——");
printl(" 网络断了、接口挂了、权限没给...");
printl(" poll() 不会无限等,到点就返回 false。");
doSleep(1200);
var fakePath = "/sdcard/this_file_never_exists.xyz";
step("🔍 去等一个不存在的文件: " + fakePath, 600);
step("⏱️ 超时设为 1.5 秒,间隔 300 ms", 600);
var r3 = waitForFile(fakePath, 1500, 300);
step("📝 poll() 返回值: { success: " + r3.success + ", waitedMs: " + r3.waitedMs + " }", 800);
step("💡 业务代码里可以这样写:", 400);
printl(" var r = waitForFile(xxx, 5000);");
printl(" if (!r.success) { printl(\"下载超时,重试或报错\"); }");
doSleep(1200);
// ══════════════════════════════════════════════════
//第五幕:自定义条件 —— 组合使用
// ══════════════════════════════════════════════════
separator();
printl("【第五幕】自定义条件 & 组合使用");
printl(" poll() 的第一个参数是任意函数 ——");
printl(" 想等啥就写啥,完全不挑。");
doSleep(1000);
// 演示:等文件出现 AND 大小达标(合并条件)
step("🎯 示例:等「文件存在 AND 大小 ≥ 80%」—— 一条 poll 搞定", 800);
var r4 = poll(function() {
try {
var f = new java.io.File(apkPath);
if (!f.exists()) return false;
return f.length() >= targetSize * 0.8;
} catch (e) { return false; }
}, 5000, 200, "文件存在 + 大小 ≥ 80%");
if (r4.success) {
step("🎊 两条条件同时满足!文件已准备好安装", 800);
}
doSleep(1200);
// ══════════════════════════════════════════════════
//收尾
// ══════════════════════════════════════════════════
separator();
printl("╔══════════════════════════════════════════════╗");
printl("║ ║");
printl("║ ✨ 演示总结 ║");
printl("║ ║");
printl("║ poll(condition, timeout, interval, label)║");
printl("║ ║");
printl("║ · 想等控件? → waitForView() ║");
printl("║ · 想等文字? → waitForText() ║");
printl("║ · 想等文件? → waitForFile() ║");
printl("║ · 想等接口? → waitForHttp() ║");
printl("║ · 想等啥都行 → poll(function(){ ... }) ║");
printl("║ ║");
printl("╚══════════════════════════════════════════════╝");
doSleep(2000);
// 清理
try { new java.io.File(apkPath).delete(); } catch (e) {}
printl("");
printl("🎬 演示结束,感谢观看!");
}
demo();
页:
[1]