一、背景与需求
手机使用时间长了,总会积累大量长期不用的App。它们占据存储空间、在后台消耗资源、推送无用的通知,拖累手机的整体运行效率。手动逐个检查“哪些App很久没用过”是一项繁琐且耗时的工作,尤其当手机里装了上百个应用时。
小米手机的安全中心内置了“应用卸载”功能,能够按照“使用频率”对已安装应用进行排序,并标注“未使用”状态。这个功能为自动化清理提供了基础——我们只需要用脚本自动识别这些“未使用”的应用,批量勾选并一键卸载即可。
冰狐智能辅助平台提供了完善的无障碍自动化API,允许开发者通过JavaScript脚本操控Android应用的UI控件。本文将基于冰狐平台的launchApp、findView、click、switchPage等核心API,深入解析如何编写一个自动清理小米手机中长期未使用App的脚本。
二、整体设计思路
脚本的核心逻辑分为以下几个步骤:
-
启动小米安全中心——通过
launchApp函数启动指定包名的应用。 -
导航到应用卸载页面——依次点击“应用管理”→“应用卸载”。
-
切换到“使用频率”排序——默认可能是按“存储占用”排序,需要切换到“使用频率”,让“未使用”的应用排在前面。
-
遍历应用列表——获取RecyclerView容器,逐个检查每个应用项是否标记为“未使用”,同时检查是否在白名单中。
-
批量卸载——勾选所有符合条件的应用,点击“一键卸载”,等待卸载完成。
白名单机制用于保护重要应用不被误删,例如微信、支付宝等日常必备工具。
三、核心API解析
在深入源码之前,先简要介绍脚本中用到的几个核心冰狐API:
launchApp(packageName) :通过应用包名启动指定App。返回true表示启动成功,false表示失败。
findView(tag, options) :通过控件的text、id、className等属性查找UI控件。返回值为JSON对象,包含tag和views两个属性,views为找到的控件节点数组。支持txt:(文本匹配)、cn:(类名匹配)、id:(ID匹配)等多种匹配方式。可通过maxStep参数控制最大查找步数。
click(tag, options) :点击控件。支持传入控件对象或tag字符串,可配置click(是否使用原生点击)、afterWait(点击后等待时间)等参数。
switchPage(findTag, checkTag, options) :切换页面。先查找findTag对应的控件并点击,然后检查checkTag对应的控件是否出现来判断页面切换是否成功。返回对象的length大于0表示成功。
console.log() 和 console.error() :输出日志,用于调试和记录执行状态。
四、源码实现
// 删除长时间没有使用的app,系统app和白名单中的app不删除
function main(whiteList=[]) {
// 1. 启动小米安全中心
if (!launchApp('com.miui.securitycenter')) {
return;
}
var ret = findView('txt:安全中心', {maxStep: 30});
if (ret.length <= 0) {
console.log('启动安全中心失败');
return;
}
// 2. 添加默认白名单应用
whiteList.push('微信');
whiteList.push('支付宝');
// 3. 进入应用管理 -> 应用卸载
ret = switchPage('txt:应用管理', 'txt:应用卸载', {click: true});
if (ret.length > 0) {
ret = switchPage('txt:应用卸载', 'txt^:卸载选中应用|txt^:一键卸载', {click: true});
if (ret.length > 0) {
// 4. 切换到“使用频率”排序
ret = findView('txt:存储占用|txt:使用频率|txt:安装时间');
if (ret.length > 0) {
if (ret.tag != '使用频率') {
click(ret.views[0], {click: true, afterWait: 1000});
ret = findView('txt:使用频率');
if (ret.length > 0) {
click(ret.views[0], {click: true, afterWait: 1000});
}
}
// 5. 遍历应用列表,勾选“未使用”且不在白名单中的应用
var selectedCount = 0, index = 0;
var container = findView('cn:androidx.recyclerview.widget.RecyclerView');
while (container.length > 0 && index < container.views[0].size) {
var item = container.views[0][index];
// 适配不同的UI布局结构
if (item[0].size == 4) {
// 布局结构1:item有4个子控件
if (item[0][1][1].text.includes('未使用')) {
if (!item[0][3][0].checked) {
if (whiteList.indexOf(item[0][1][0].text) < 0) {
click(item, {click: true});
selectedCount++;
}
}
} else {
break; // 遇到非“未使用”的应用则停止遍历
}
} else if (item[0].size == 3 && item[0][1].size == 3) {
// 布局结构2:item有3个子控件,其中第二个子控件有3个子控件
console.log('item[0][1][2].text:' + item[0][1][2].text);
if (item[0][1][2].text.includes('未使用')) {
if (!item[0][2].checked) {
console.log('txt:' + item[0][1][0].text);
if (whiteList.indexOf(item[0][1][0].text) < 0) {
click(item[0], {click:true});
selectedCount++;
}
}
} else {
break;
}
} else {
console.log('忽略一个item');
}
++index;
container = findView('cn:androidx.recyclerview.widget.RecyclerView');
}
// 6. 如果有勾选的应用,执行批量卸载
if (selectedCount > 0) {
ret = switchPage('txt^:卸载选中应用|txt^:一键卸载', 'txt:确定|txt:卸载选中应用', {click: true});
if (ret.length > 0) {
click(ret.views[0], {click: true, afterWait: 3000});
var startTime = new Date().getTime();
ret = findView('txt:应用卸载');
while (ret.length <= 0 && (new Date().getTime() - startTime) < 120000) {
ret = findView('txt:应用卸载', {afterWait: 1000});
}
console.log('卸载成功');
}
} else {
console.log('没有需要卸载的');
}
}
}
} else {
console.error('进入优化加速失败');
}
}
五、源码逐段解析
5.1 启动安全中心
if (!launchApp('com.miui.securitycenter')) {
return;
}
com.miui.securitycenter是小米安全中心的应用包名。launchApp如果返回false,说明应用不存在或无法启动,脚本直接退出。
5.2 确认安全中心已启动
var ret = findView('txt:安全中心', {maxStep: 30});
if (ret.length <= 0) {
console.log('启动安全中心失败');
return;
}
通过查找“安全中心”文本控件来确认页面已加载完成。maxStep: 30表示最多尝试30步(每步约几百毫秒),给页面加载留出足够时间。
5.3 页面导航
ret = switchPage('txt:应用管理', 'txt:应用卸载', {click: true});
switchPage是冰狐提供的高阶页面切换函数。它会先查找“应用管理”控件并点击,然后等待“应用卸载”控件出现。如果两个条件都满足,说明页面切换成功。{click: true}表示使用控件原生的点击功能。
5.4 切换到“使用频率”排序
ret = findView('txt:存储占用|txt:使用频率|txt:安装时间');
这里使用|分隔多个匹配条件,查找排序标签。如果当前选中的不是“使用频率”,则点击当前标签弹出排序选项,再点击“使用频率”。
5.5 遍历应用列表——核心逻辑
这是脚本最复杂的部分。小米应用卸载页面的应用列表是一个RecyclerView容器。每个应用项是一个控件节点,我们需要遍历所有子项:
var container = findView('cn:androidx.recyclerview.widget.RecyclerView');
while (container.length > 0 && index < container.views[0].size) {
var item = container.views[0][index];
// 处理每个item...
++index;
container = findView('cn:androidx.recyclerview.widget.RecyclerView');
}
每次循环结束后重新获取container,是因为UI可能在滚动或刷新后发生变化。
为什么有两种布局结构判断?
不同版本的小米MIUI系统,应用卸载页面的控件布局结构可能不同。脚本通过判断item[0].size来适配两种常见布局:
-
布局结构1 (
item[0].size == 4):应用名称在item[0][1][0].text,状态标签在item[0][1][1].text,复选框在item[0][3][0].checked -
布局结构2 (
item[0].size == 3 && item[0][1].size == 3):状态标签在item[0][1][2].text,复选框在item[0][2].checked
判断“未使用” :通过检查text.includes('未使用')来识别长期未使用的应用。
白名单过滤:whiteList.indexOf(item[0][1][0].text) < 0确保白名单中的应用不被选中。
提前终止:由于列表按使用频率排序,“未使用”的应用排在最前面。一旦遇到非“未使用”的应用,说明后面的应用都是最近使用过的,直接break退出循环,提高效率。
5.6 执行卸载
ret = switchPage('txt^:卸载选中应用|txt^:一键卸载', 'txt:确定|txt:卸载选中应用', {click: true});
txt^:表示前缀匹配。不同MIUI版本中按钮文本可能是“卸载选中应用”或“一键卸载”,用前缀匹配可以兼容两种写法。
点击卸载按钮后,脚本等待卸载完成:
var startTime = new Date().getTime();
ret = findView('txt:应用卸载');
while (ret.length <= 0 && (new Date().getTime() - startTime) < 120000) {
ret = findView('txt:应用卸载', {afterWait: 1000});
}
这里设置120秒超时(120000毫秒),每1秒检查一次“应用卸载”页面是否重新出现,以此判断卸载过程是否结束。
六、使用与扩展
调用方式:
// 使用默认白名单(微信、支付宝) main(); // 自定义白名单 main(['微信', '支付宝', '抖音', '淘宝']);
扩展建议:
-
动态获取白名单:可以从配置文件或网络接口读取白名单,避免每次修改脚本。
-
增加日志记录:记录卸载了哪些应用,便于后续审计。
-
定时执行:结合冰狐平台的定时任务功能,每周自动执行一次清理。
-
异常处理增强:增加更多
try-catch和重试机制,提高脚本鲁棒性。
七、注意事项
-
无障碍权限:冰狐智能辅助需要开启无障碍服务权限才能正常运行。
-
MIUI版本差异:不同MIUI版本的应用卸载页面布局可能有差异,建议在目标设备上先通过“UI树”工具查看控件结构。
-
系统应用保护:小米安全中心的应用卸载功能本身不会列出不可卸载的系统应用,因此无需额外过滤。
-
执行环境:脚本需要在冰狐智能辅助移动端执行,确保设备在线且脚本已设为默认脚本。
八、总结
本文基于冰狐智能辅助平台,实现了一个自动清理小米手机中长期未使用App的脚本。通过launchApp启动应用、findView定位控件、switchPage导航页面、click模拟点击,配合RecyclerView遍历和布局适配,实现了从“打开安全中心”到“批量卸载完成”的全流程自动化。
冰狐智能辅助以JavaScript作为统一脚本语言,封装了完善的无障碍操作API,让开发者无需处理Android底层无障碍服务的复杂逻辑,即可快速实现各类自动化场景。本文的脚本可直接在小米手机上运行,有效帮助用户清理存储空间、提升手机运行效率。
4673

被折叠的 条评论
为什么被折叠?



