找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 6|回复: 1

应用启动秒表从拉起命令到真正出现在前台

[复制链接]
发表于 21 分钟前 | 显示全部楼层 |阅读模式
应用启动秒表从拉起命令到真正出现在前台

QQ截图20261112083817.png

QQ截图20261112083801.png


  1. /**
  2. * 案例42:应用启动秒表从拉起命令到真正出现在前台
  3. *
  4. * 主要功能:自动清点手机上的第三方 App,逐个发射拉起并实测"从拉起命令到真正出现在前台"
  5. * 要花几秒;等待中每 0.3 秒盯一次前台、每秒滚一行进度;最后按耗时排出 🥇🥈🥉
  6. * 启动速度排行榜,成绩写入 CSV 台账存档复查。
  7. *
  8. * 实际作用:
  9. *   1. 设备农场挑机器:同批云手机各跑一遍,启动普遍慢的就是性能拉胯的,拿数据说话;
  10. *   2. 揪出僵尸 App:拉不起、卡启动页的直接暴露,客户报障时这就是证据;
  11. *   3. 验证优化效果:清缓存/瘦身前后各跑一遍,秒数对比就是体检报告。
  12. *
  13. * 配置:测几个=抽测个数(0=全部第三方App);超时秒=单App最长等待。
  14. * 用法:直接运行,每个App约20秒,全程倒计时+秒数滚动,适合录屏演示。
  15. * 写法:Rhino ES5,全 var,函数先定义后调用,main() 末尾启动;等待只用 sleep.millisecond。
  16. */

  17. // ========== 配置区 ==========
  18. var 测几个 = 0;
  19. var 超时秒 = 15;
  20. var 落盘目录 = "/sdcard/auto";
  21. var 台账文件 = 落盘目录 + "/启动秒表_台账.csv";
  22. var 是否落盘 = true;

  23. // ========== 节奏输出 ==========
  24. function 节奏倒计时(秒数, 干什么) {
  25.     for (var i = 秒数; i > 0; i--) {
  26.         printl("⏰ " + i + " 秒后:" + 干什么);
  27.         sleep.millisecond(1000);
  28.     }
  29.     printl("🎬 " + 干什么 + " —— 开始");
  30.     sleep.millisecond(400);
  31. }

  32. // ========== 基础封装 ==========
  33. function adb跑(命令) {
  34.     try {
  35.         var 数据 = JSON.parse(adb.exec(命令, false));
  36.         var 成 = 数据.success == null ? "" : ("" + 数据.success);
  37.         var 错 = 数据.errorMsg == null ? "" : ("" + 数据.errorMsg).trim();
  38.         return { 成: 成, 错: 错 };
  39.     } catch (e) {
  40.         return { 成: "", 错: "exec异常 " + e };
  41.     }
  42. }

  43. function 取前台包名() {
  44.     try {
  45.         var xml = auto.getXml();
  46.         var m = xml.match(/package="([^"]+)"/);
  47.         return m == null ? "" : m[1];
  48.     } catch (e) {
  49.         return "";
  50.     }
  51. }

  52. // 引擎App界面是WebView,getXml读不到节点;monkey拉起桌面后通道即恢复
  53. function 回桌面() {
  54.     printl("🏠 前台节点读不到(可能停在引擎自己的界面),monkey 拉起桌面自救");
  55.     adb跑("monkey -p com.android.launcher3 -c android.intent.category.LAUNCHER 1");
  56.     sleep.millisecond(2000);
  57. }

  58. function 补零(n) { return n < 10 ? "0" + n : "" + n; }

  59. function 当前时刻() {
  60.     var d = new Date();
  61.     return 补零(d.getHours()) + ":" + 补零(d.getMinutes()) + ":" + 补零(d.getSeconds());
  62. }

  63. // ========== 一、清点第三方 App ==========
  64. function 拍第三方清单() {
  65.     var r = adb跑("pm list packages -3");
  66.     if (r.成 === "") {
  67.         printl("❌ pm list packages -3 失败: " + r.错);
  68.         return [];
  69.     }
  70.     var 行 = r.成.split("\n");
  71.     var 清单 = [];
  72.     for (var i = 0; i < 行.length; i++) {
  73.         var m = 行[i].match(/package:([A-Za-z0-9._]+)/);
  74.         if (m != null) 清单.push(m[1]);
  75.     }
  76.     return 清单;
  77. }

  78. // ========== 二、单App测速 ==========
  79. function 测一个(序号, 总数, 包名) {
  80.     节奏倒计时(3, "拉起第 " + 序号 + "/" + 总数 + " 个: " + 包名);
  81.     var 基线 = 取前台包名();
  82.     printl("👀 拉起前前台: " + (基线 === "" ? "读取失败" : 基线));
  83.     printl("🚀 monkey -p " + 包名 + " 发射");
  84.     var 发 = adb跑("monkey -p " + 包名 + " -c android.intent.category.LAUNCHER 1");
  85.     if (发.成.indexOf("No activities found") >= 0) {
  86.         printl("⏱ " + 包名 + " → 没有启动入口 ❌(工具/输入法类,桌面上都点不开,不占超时)");
  87.         return { 包: 包名, 秒: -1, 成: false, 型: "NOENTRY" };
  88.     }
  89.     var t0 = new Date().getTime();
  90.     var 报过 = 0;
  91.     var 连败 = 0;
  92.     var 自救过 = false;
  93.     while (true) {
  94.         var 前台 = 取前台包名();
  95.         var 已等 = (new Date().getTime() - t0) / 1000;
  96.         if (前台 === 包名) {
  97.             var 秒 = Math.round(已等 * 10) / 10;
  98.             printl("⏱ " + 包名 + " → " + 秒 + " 秒到前台 ✅");
  99.             return { 包: 包名, 秒: 秒, 成: true, 型: "OK" };
  100.         }
  101.         if (前台 === "") {
  102.             连败++;
  103.             if (连败 >= 2) {
  104.                 if (!自救过) {
  105.                     自救过 = true;
  106.                     连败 = 0;
  107.                     回桌面();
  108.                 } else {
  109.                     printl("🛑 回桌面自救后仍连读 " + 连败 + " 次失败,通道确实不通,不再白等");
  110.                     return { 包: 包名, 秒: -1, 成: false, 型: "NOCHANNEL" };
  111.                 }
  112.             }
  113.         } else {
  114.             连败 = 0;
  115.         }
  116.         if (已等 >= 超时秒) {
  117.             printl("⏱ " + 包名 + " → 超 " + 超时秒 + " 秒没到前台 ❌(拉起了但卡在启动页)");
  118.             return { 包: 包名, 秒: -1, 成: false, 型: "TIMEOUT" };
  119.         }
  120.         var 整 = Math.floor(已等);
  121.         if (整 > 报过) {
  122.             报过 = 整;
  123.             printl("   …已等 " + 整 + " 秒,当前前台: " + 前台);
  124.         }
  125.         sleep.millisecond(300);
  126.     }
  127. }

  128. // ========== 三、台账 ==========
  129. function 记台账(行内容) {
  130.     if (!是否落盘) return;
  131.     var r = adb跑("echo " + 行内容 + " >> " + 台账文件);
  132.     if (r.错 !== "") printl("   ⚠ 台账写入失败: " + r.错);
  133. }

  134. // ========== 主流程 ==========
  135. function main() {
  136.     printl("=== 案例42 应用启动秒表 启动:" + (测几个 <= 0 ? "全测第三方App" : "抽测 " + 测几个 + " 个") + ",单App超时 " + 超时秒 + " 秒 ===");
  137.     sleep.millisecond(500);
  138.     if (取前台包名() === "") 回桌面();
  139.     节奏倒计时(3, "清点第三方App(pm list packages -3)");
  140.     var 清单 = 拍第三方清单();
  141.     if (清单.length === 0) {
  142.         printl("❌ 一个第三方App都没清点出来,收工");
  143.         return;
  144.     }
  145.     var 显示数 = 清单.length < 15 ? 清单.length : 15;
  146.     printl("📦 第三方App共 " + 清单.length + " 个,前 " + 显示数 + " 个:");
  147.     for (var i = 0; i < 显示数; i++) {
  148.         printl("   " + (i + 1) + ". " + 清单[i]);
  149.         sleep.millisecond(120);
  150.     }
  151.     if (清单.length > 显示数) printl("   …其余 " + (清单.length - 显示数) + " 个略");

  152.     var 待测数 = (测几个 <= 0 || 清单.length < 测几个) ? 清单.length : 测几个;
  153.     printl("🎯 本次" + (测几个 <= 0 ? "全测 " : "抽测前 ") + 待测数 + " 个");
  154.     if (是否落盘) {
  155.         adb跑("mkdir -p " + 落盘目录);
  156.         adb跑("echo 时刻,包名,耗时秒,结果 >> " + 台账文件);
  157.     }

  158.     var 结果 = [];
  159.     var 通道断了 = false;
  160.     for (var j = 0; j < 待测数; j++) {
  161.         var 果 = 测一个(j + 1, 待测数, 清单[j]);
  162.         结果.push(果);
  163.         记台账(当前时刻() + "," + 果.包 + "," + 果.秒 + "," + 果.型);
  164.         if (果.型 === "NOCHANNEL") { 通道断了 = true; break; }
  165.         sleep.millisecond(1500);
  166.     }
  167.     if (通道断了) {
  168.         printl("⚠ 回桌面自救后前台节点仍读不到,剩余App不再测;请检查无障碍服务是否开启");
  169.     }

  170.     printl("========== 🏁 启动速度排行榜 ==========");
  171.     var 成功 = [];
  172.     for (var k = 0; k < 结果.length; k++) {
  173.         if (结果[k].成) 成功.push(结果[k]);
  174.     }
  175.     成功.sort(function (a, b) { return a.秒 - b.秒; });
  176.     var 奖牌 = ["🥇", "🥈", "🥉"];
  177.     for (var x = 0; x < 成功.length; x++) {
  178.         printl("  " + (奖牌[x] == null ? " ·" : 奖牌[x]) + " " + 成功[x].秒 + " 秒  " + 成功[x].包);
  179.         sleep.millisecond(400);
  180.     }
  181.     var 超时数 = 0;
  182.     var 断数 = 0;
  183.     for (var y = 0; y < 结果.length; y++) {
  184.         if (!结果[y].成) {
  185.             if (结果[y].型 === "NOCHANNEL") 断数++;
  186.             else 超时数++;
  187.         }
  188.     }
  189.     if (超时数 > 0) printl("  ❌ 超时/无入口未上榜 " + 超时数 + " 个");
  190.     if (断数 > 0) printl("  🛑 通道断未测完 " + 断数 + " 个");

  191.     printl("=== 战报:实测 " + 结果.length + " | 上榜 " + 成功.length + " | 超时/无入口 " + 超时数 + " | 通道断 " + 断数 + " ===");
  192.     if (是否落盘) {
  193.         var 查 = adb跑("stat -c %s " + 台账文件);
  194.         printl("📒 台账已落盘: " + 台账文件 + "(" + 查.成.trim() + " 字节)");
  195.     }
  196.     printl("=== 案例42 收工 ===");
  197. }

  198. main();
复制代码


QQ截图20261112083638.png

0

主题

4

回帖

8

积分

软件网站新手

积分
8
发表于 10 分钟前 | 显示全部楼层

应用启动秒表从拉起命令到真正出现在前台

楼主这个思路可以,账号最好按任务稳定映射,不要随机轮换,新号一上来跑满负荷很容易被限。要判断值不值得投入,就看这个场景一天重复多少次,一天十次的自动化维护成本可能比手工还高。权限那块要单独开无障碍和电池白名单,否则跑到一半会被系统杀掉,最常见的问题就是这个。请问具体用的什么设备跑的?
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|

GMT+8, 2026-11-13 06:51

安卓脚本软件 · 键鼠盒子 · HID · 机械臂

快速回复 返回顶部 返回列表

随机推荐 · 热帖 / 新帖 / 随便看看

社区会员(随机展示)