|
|
应用启动秒表从拉起命令到真正出现在前台
- /**
- * 案例42:应用启动秒表从拉起命令到真正出现在前台
- *
- * 主要功能:自动清点手机上的第三方 App,逐个发射拉起并实测"从拉起命令到真正出现在前台"
- * 要花几秒;等待中每 0.3 秒盯一次前台、每秒滚一行进度;最后按耗时排出 🥇🥈🥉
- * 启动速度排行榜,成绩写入 CSV 台账存档复查。
- *
- * 实际作用:
- * 1. 设备农场挑机器:同批云手机各跑一遍,启动普遍慢的就是性能拉胯的,拿数据说话;
- * 2. 揪出僵尸 App:拉不起、卡启动页的直接暴露,客户报障时这就是证据;
- * 3. 验证优化效果:清缓存/瘦身前后各跑一遍,秒数对比就是体检报告。
- *
- * 配置:测几个=抽测个数(0=全部第三方App);超时秒=单App最长等待。
- * 用法:直接运行,每个App约20秒,全程倒计时+秒数滚动,适合录屏演示。
- * 写法:Rhino ES5,全 var,函数先定义后调用,main() 末尾启动;等待只用 sleep.millisecond。
- */
- // ========== 配置区 ==========
- var 测几个 = 0;
- var 超时秒 = 15;
- var 落盘目录 = "/sdcard/auto";
- var 台账文件 = 落盘目录 + "/启动秒表_台账.csv";
- var 是否落盘 = true;
- // ========== 节奏输出 ==========
- function 节奏倒计时(秒数, 干什么) {
- for (var i = 秒数; i > 0; i--) {
- printl("⏰ " + i + " 秒后:" + 干什么);
- sleep.millisecond(1000);
- }
- printl("🎬 " + 干什么 + " —— 开始");
- sleep.millisecond(400);
- }
- // ========== 基础封装 ==========
- function adb跑(命令) {
- try {
- var 数据 = JSON.parse(adb.exec(命令, false));
- var 成 = 数据.success == null ? "" : ("" + 数据.success);
- var 错 = 数据.errorMsg == null ? "" : ("" + 数据.errorMsg).trim();
- return { 成: 成, 错: 错 };
- } catch (e) {
- return { 成: "", 错: "exec异常 " + e };
- }
- }
- function 取前台包名() {
- try {
- var xml = auto.getXml();
- var m = xml.match(/package="([^"]+)"/);
- return m == null ? "" : m[1];
- } catch (e) {
- return "";
- }
- }
- // 引擎App界面是WebView,getXml读不到节点;monkey拉起桌面后通道即恢复
- function 回桌面() {
- printl("🏠 前台节点读不到(可能停在引擎自己的界面),monkey 拉起桌面自救");
- adb跑("monkey -p com.android.launcher3 -c android.intent.category.LAUNCHER 1");
- sleep.millisecond(2000);
- }
- function 补零(n) { return n < 10 ? "0" + n : "" + n; }
- function 当前时刻() {
- var d = new Date();
- return 补零(d.getHours()) + ":" + 补零(d.getMinutes()) + ":" + 补零(d.getSeconds());
- }
- // ========== 一、清点第三方 App ==========
- function 拍第三方清单() {
- var r = adb跑("pm list packages -3");
- if (r.成 === "") {
- printl("❌ pm list packages -3 失败: " + r.错);
- return [];
- }
- var 行 = r.成.split("\n");
- var 清单 = [];
- for (var i = 0; i < 行.length; i++) {
- var m = 行[i].match(/package:([A-Za-z0-9._]+)/);
- if (m != null) 清单.push(m[1]);
- }
- return 清单;
- }
- // ========== 二、单App测速 ==========
- function 测一个(序号, 总数, 包名) {
- 节奏倒计时(3, "拉起第 " + 序号 + "/" + 总数 + " 个: " + 包名);
- var 基线 = 取前台包名();
- printl("👀 拉起前前台: " + (基线 === "" ? "读取失败" : 基线));
- printl("🚀 monkey -p " + 包名 + " 发射");
- var 发 = adb跑("monkey -p " + 包名 + " -c android.intent.category.LAUNCHER 1");
- if (发.成.indexOf("No activities found") >= 0) {
- printl("⏱ " + 包名 + " → 没有启动入口 ❌(工具/输入法类,桌面上都点不开,不占超时)");
- return { 包: 包名, 秒: -1, 成: false, 型: "NOENTRY" };
- }
- var t0 = new Date().getTime();
- var 报过 = 0;
- var 连败 = 0;
- var 自救过 = false;
- while (true) {
- var 前台 = 取前台包名();
- var 已等 = (new Date().getTime() - t0) / 1000;
- if (前台 === 包名) {
- var 秒 = Math.round(已等 * 10) / 10;
- printl("⏱ " + 包名 + " → " + 秒 + " 秒到前台 ✅");
- return { 包: 包名, 秒: 秒, 成: true, 型: "OK" };
- }
- if (前台 === "") {
- 连败++;
- if (连败 >= 2) {
- if (!自救过) {
- 自救过 = true;
- 连败 = 0;
- 回桌面();
- } else {
- printl("🛑 回桌面自救后仍连读 " + 连败 + " 次失败,通道确实不通,不再白等");
- return { 包: 包名, 秒: -1, 成: false, 型: "NOCHANNEL" };
- }
- }
- } else {
- 连败 = 0;
- }
- if (已等 >= 超时秒) {
- printl("⏱ " + 包名 + " → 超 " + 超时秒 + " 秒没到前台 ❌(拉起了但卡在启动页)");
- return { 包: 包名, 秒: -1, 成: false, 型: "TIMEOUT" };
- }
- var 整 = Math.floor(已等);
- if (整 > 报过) {
- 报过 = 整;
- printl(" …已等 " + 整 + " 秒,当前前台: " + 前台);
- }
- sleep.millisecond(300);
- }
- }
- // ========== 三、台账 ==========
- function 记台账(行内容) {
- if (!是否落盘) return;
- var r = adb跑("echo " + 行内容 + " >> " + 台账文件);
- if (r.错 !== "") printl(" ⚠ 台账写入失败: " + r.错);
- }
- // ========== 主流程 ==========
- function main() {
- printl("=== 案例42 应用启动秒表 启动:" + (测几个 <= 0 ? "全测第三方App" : "抽测 " + 测几个 + " 个") + ",单App超时 " + 超时秒 + " 秒 ===");
- sleep.millisecond(500);
- if (取前台包名() === "") 回桌面();
- 节奏倒计时(3, "清点第三方App(pm list packages -3)");
- var 清单 = 拍第三方清单();
- if (清单.length === 0) {
- printl("❌ 一个第三方App都没清点出来,收工");
- return;
- }
- var 显示数 = 清单.length < 15 ? 清单.length : 15;
- printl("📦 第三方App共 " + 清单.length + " 个,前 " + 显示数 + " 个:");
- for (var i = 0; i < 显示数; i++) {
- printl(" " + (i + 1) + ". " + 清单[i]);
- sleep.millisecond(120);
- }
- if (清单.length > 显示数) printl(" …其余 " + (清单.length - 显示数) + " 个略");
- var 待测数 = (测几个 <= 0 || 清单.length < 测几个) ? 清单.length : 测几个;
- printl("🎯 本次" + (测几个 <= 0 ? "全测 " : "抽测前 ") + 待测数 + " 个");
- if (是否落盘) {
- adb跑("mkdir -p " + 落盘目录);
- adb跑("echo 时刻,包名,耗时秒,结果 >> " + 台账文件);
- }
- var 结果 = [];
- var 通道断了 = false;
- for (var j = 0; j < 待测数; j++) {
- var 果 = 测一个(j + 1, 待测数, 清单[j]);
- 结果.push(果);
- 记台账(当前时刻() + "," + 果.包 + "," + 果.秒 + "," + 果.型);
- if (果.型 === "NOCHANNEL") { 通道断了 = true; break; }
- sleep.millisecond(1500);
- }
- if (通道断了) {
- printl("⚠ 回桌面自救后前台节点仍读不到,剩余App不再测;请检查无障碍服务是否开启");
- }
- printl("========== 🏁 启动速度排行榜 ==========");
- var 成功 = [];
- for (var k = 0; k < 结果.length; k++) {
- if (结果[k].成) 成功.push(结果[k]);
- }
- 成功.sort(function (a, b) { return a.秒 - b.秒; });
- var 奖牌 = ["🥇", "🥈", "🥉"];
- for (var x = 0; x < 成功.length; x++) {
- printl(" " + (奖牌[x] == null ? " ·" : 奖牌[x]) + " " + 成功[x].秒 + " 秒 " + 成功[x].包);
- sleep.millisecond(400);
- }
- var 超时数 = 0;
- var 断数 = 0;
- for (var y = 0; y < 结果.length; y++) {
- if (!结果[y].成) {
- if (结果[y].型 === "NOCHANNEL") 断数++;
- else 超时数++;
- }
- }
- if (超时数 > 0) printl(" ❌ 超时/无入口未上榜 " + 超时数 + " 个");
- if (断数 > 0) printl(" 🛑 通道断未测完 " + 断数 + " 个");
- printl("=== 战报:实测 " + 结果.length + " | 上榜 " + 成功.length + " | 超时/无入口 " + 超时数 + " | 通道断 " + 断数 + " ===");
- if (是否落盘) {
- var 查 = adb跑("stat -c %s " + 台账文件);
- printl("📒 台账已落盘: " + 台账文件 + "(" + 查.成.trim() + " 字节)");
- }
- printl("=== 案例42 收工 ===");
- }
- main();
复制代码
|
|