YYPOST群发软件 发表于 2026-9-24 06:49:41

多线程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("   &#128229; 当前大小: " + 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("║   &#128736;️AIWROK sleep 轮询等待工具 演示       ║");
    printl("║                                              ║");
    printl("║   「模拟完整 APK 下载 → 安装流程」         ║");
    printl("║                                              ║");
    printl("╚══════════════════════════════════════════════╝");

    doSleep(1200);   // 给录屏一个"标题定帧"时间

    printl("");
    printl("&#128214; 场景说明:");
    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("&#128241; 目标 APK: " + apkPath, 500);
    step("&#128230; 模拟大小: " + targetSize + " 字节", 500);
    step("&#128640; 启动下载线程...", 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("&#128194; 文件已在磁盘上!耗时 " + 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("&#128202; 文件大小达标!已完整写入 " + targetSize + " 字节", 600);
    }

    doSleep(1000);

    // ══════════════════════════════════════════════════
    //第四幕:演示超时场景(等一个不存在的东西)
    // ══════════════════════════════════════════════════
    separator();
    printl("【第四幕】演示:条件永远不成立 → 触发超时");
    printl("   场景:实际业务中经常遇到 ——");
    printl("         网络断了、接口挂了、权限没给...");
    printl("         poll() 不会无限等,到点就返回 false。");
    doSleep(1200);

    var fakePath = "/sdcard/this_file_never_exists.xyz";
    step("&#128269; 去等一个不存在的文件: " + fakePath, 600);
    step("⏱️ 超时设为 1.5 秒,间隔 300 ms", 600);

    var r3 = waitForFile(fakePath, 1500, 300);

    step("&#128221; poll() 返回值: { success: " + r3.success + ", waitedMs: " + r3.waitedMs + " }", 800);
    step("&#128161; 业务代码里可以这样写:", 400);
    printl("      var r = waitForFile(xxx, 5000);");
    printl("      if (!r.success) { printl(\"下载超时,重试或报错\"); }");

    doSleep(1200);

    // ══════════════════════════════════════════════════
    //第五幕:自定义条件 —— 组合使用
    // ══════════════════════════════════════════════════
    separator();
    printl("【第五幕】自定义条件 & 组合使用");
    printl("   poll() 的第一个参数是任意函数 ——");
    printl("   想等啥就写啥,完全不挑。");
    doSleep(1000);

    // 演示:等文件出现 AND 大小达标(合并条件)
    step("&#127919; 示例:等「文件存在 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("&#127882; 两条条件同时满足!文件已准备好安装", 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("&#127916; 演示结束,感谢观看!");
}

demo();



页: [1]
查看完整版本: 多线程GET下载轮询等待工具