|
|
应用体重秤按App排缓存重量定向清缓存
- /**
- * 案例40:应用体重秤按App排缓存重量定向清缓存
- *
- * 与案例32(按文件类型清垃圾)不同,本案例按【App】算账:
- * 谁在 /sdcard/Android/data 里吃得最胖、胖在缓存还是数据,一目了然;
- * 清理时只掏每个 App 的 cache 子目录(下次启动自动重建),数据文件一根不碰。
- * 用途:云手机农场定期瘦身 / 客户问"手机怎么满了"直接甩排行榜。
- *
- * 全走锤死通道(零新API):
- * 逐App大小 → adb.exec("du -sk /sdcard/Android/data/*") (案例32锤死)
- * 缓存大小 → du -sk 包名/cache
- * 删除 → rm -rf 包名/cache (案例32锤死)
- * 前后对比 → df /sdcard (案例32锤死)
- * 等待 → sleep.second(整秒)/ sleep.millisecond(毫秒),禁止裸 sleep()
- *
- * 写法合规:Rhino ES5,全 var、字符串拼接、函数先定义后调用、main() 末尾启动;
- * 节奏输出:关键动作前 3..2..1 倒计时,日志行间留白。
- *
- * 用法:默认 实清=false 只称重出报告;检查排行榜没冤枉的,改 true 再跑真瘦身。
- */
- // ========== 配置区 ==========
- var 实清 = false; // false=演练只列清单,true=真删各App的cache目录
- var 上榜门槛MB = 1; // 小于1MB的App不上秤
- var 大户门槛MB = 20; // 超过20MB且缓存占比高的,列为"值得清"
- var 数据根 = "/sdcard/Android/data";
- var 资源根 = "/sdcard/Android/obb";
- // 包名 → 人话名(案例31锤死映射表,自己的App自己加)
- var 应用名 = {
- "com.ss.android.ugc.aweme": "抖音",
- "com.ss.android.ugc.aweme.lite": "抖音极速版",
- "com.kuaishou.nebula": "快手极速版",
- "com.smile.gifmaker": "快手",
- "com.tencent.mm": "微信",
- "com.dragon.read": "番茄小说",
- "com.lizhi.fm": "荔枝FM",
- "com.ss.android.article.lite": "头条极速版"
- };
- // ========== adb 通道(案例30锤死) ==========
- 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 节奏倒计时(秒数, 干什么) {
- for (var i = 秒数; i > 0; i--) {
- printl("⏰ " + i + " 秒后:" + 干什么);
- sleep.second(1);
- }
- printl("🎬 " + 干什么 + " —— 开始");
- sleep.millisecond(400);
- }
- // ========== df 已用MB(案例32解析姿势) ==========
- function 查已用MB() {
- var r = adb跑("df /sdcard");
- var 行 = ("" + r.成).split("\n");
- for (var i = 1; i < 行.length; i++) {
- var 列 = ("" + 行[i]).trim().split(/\s+/);
- if (列.length >= 3) {
- var kb = parseInt(列[2], 10);
- if (kb > 0) return Math.floor(kb / 1024);
- }
- }
- return -1;
- }
- // ========== du 单目录KB ==========
- function 查KB(路径) {
- var r = adb跑("du -sk " + 路径);
- var m = ("" + r.成).match(/(\d+)/);
- return m == null ? -1 : parseInt(m[1], 10);
- }
- // ========== 逐App称重 ==========
- // du -sk 通配符输出:每行 "大小KB<TAB>/路径/包名"
- function 称重(根目录) {
- var r = adb跑("du -sk " + 根目录 + "/*");
- var 行 = ("" + r.成).split("\n");
- var 结果 = [];
- for (var i = 0; i < 行.length; i++) {
- var 列 = ("" + 行[i]).trim().split(/\s+/);
- if (列.length < 2) continue;
- var kb = parseInt(列[0], 10);
- if (!(kb > 0)) continue;
- var 路径 = 列[1];
- var 包名 = ("" + 路径).split("/");
- 结果.push({ 包: 包名[包名.length - 1], 路径: 路径, KB: kb });
- }
- return 结果;
- }
- // ========== 主流程 ==========
- function main() {
- printl("=== 案例40 应用体重秤 启动:模式=" + (实清 ? "实删缓存" : "演练(只报不删)") + " ===");
- sleep.millisecond(500);
- var 已用前 = 查已用MB();
- printl("💾 清理前已用: " + (已用前 < 0 ? "未知" : 已用前 + " MB"));
- sleep.millisecond(400);
- // ① 称重
- 节奏倒计时(3, "给 " + 数据根 + " 逐App上秤");
- var 名单 = 称重(数据根);
- var obb = 称重(资源根);
- var i;
- for (i = 0; i < obb.length; i++) 名单.push(obb[i]); // obb 游戏大包也算进来
- if (名单.length === 0) {
- printl("ℹ 两个根目录都是空的(没装第三方App或du不通),到此为止");
- return;
- }
- 名单.sort(function (a, b) { return b.KB - a.KB; });
- // ② 排行榜
- printl("========== ⚖️ 应用体重排行榜(≥" + 上榜门槛MB + "MB) ==========");
- var 上榜 = [];
- for (i = 0; i < 名单.length; i++) {
- var mb = Math.round(名单[i].KB / 1024 * 10) / 10;
- if (mb < 上榜门槛MB) continue;
- var 名 = 应用名[名单[i].包] || 名单[i].包;
- printl(" 第" + (上榜.length + 1) + "名 " + 名 + " : " + mb + " MB");
- 名单[i].MB = mb;
- 上榜.push(名单[i]);
- sleep.millisecond(300);
- }
- // ③ 缓存细分:每个大户单独量 cache
- 节奏倒计时(3, "细分缓存与数据(只量 " + 大户门槛MB + "MB 以上大户)");
- var 可清表 = [];
- for (i = 0; i < 上榜.length; i++) {
- var a = 上榜[i];
- if (a.MB < 大户门槛MB) continue;
- var cache路径 = a.路径 + "/cache";
- var ckb = 查KB(cache路径);
- if (ckb <= 0) {
- printl(" " + (应用名[a.包] || a.包) + " : 胖在数据不在缓存,不动它");
- sleep.millisecond(300);
- continue;
- }
- var cmb = Math.round(ckb / 1024 * 10) / 10;
- var 占 = Math.round(cmb / a.MB * 100);
- printl(" " + (应用名[a.包] || a.包) + " : 总 " + a.MB + " MB,其中缓存 " + cmb + " MB(" + 占 + "%)");
- 可清表.push({ 包: a.包, cache: cache路径, MB: cmb });
- sleep.millisecond(300);
- }
- // ④ 清理(或演练)
- var 合计可清 = 0;
- for (i = 0; i < 可清表.length; i++) 合计可清 += 可清表[i].MB;
- 合计可清 = Math.round(合计可清 * 10) / 10;
- if (可清表.length === 0) {
- printl("✅ 没有「缓存占大头」的App,不用清,收工");
- return;
- }
- if (!实清) {
- printl("📋 [演练] 可安全释放合计 " + 合计可清 + " MB(只删各App的cache目录,数据不碰)");
- for (i = 0; i < 可清表.length; i++)
- printl(" " + (应用名[可清表[i].包] || 可清表[i].包) + " → rm -rf " + 可清表[i].cache);
- printl("💡 检查清单没冤枉 → 把 实清 改 true 再跑,真瘦身");
- } else {
- 节奏倒计时(3, "真删 " + 可清表.length + " 个App的cache目录");
- var 删成 = 0;
- for (i = 0; i < 可清表.length; i++) {
- var r = adb跑("rm -rf " + 可清表[i].cache);
- if (r.错 === "") {
- 删成++;
- printl(" 🧹 已清 " + (应用名[可清表[i].包] || 可清表[i].包) + " 缓存 " + 可清表[i].MB + " MB");
- } else {
- printl(" ⚠ 清 " + 可清表[i].包 + " 失败: " + r.错.slice(0, 50));
- }
- sleep.millisecond(300);
- }
- var 已用后 = 查已用MB();
- var 释放 = (已用前 >= 0 && 已用后 >= 0) ? 已用前 - 已用后 : -1;
- printl("========== 📋 瘦身战报 ==========");
- printl(" 清理 " + 删成 + "/" + 可清表.length + " 个App缓存,预计释放 " + 合计可清 + " MB" +
- (释放 >= 0 ? ",df 实测释放 " + 释放 + " MB" : ""));
- }
- printl("💡 常驻玩法:每周跑一次演练清单发群里;云手机满了就 实清=true 跑一把");
- printl("=== 案例40 收工 ===");
- }
- // ========== 启动(Rhino ES5:函数全部定义完才调用) ==========
- main();
复制代码
|
|