// Redmi K20 (MIUI12.0.4) 专用最终版脚本
console.show();
toast("最终版脚本启动");
// 应用包名配置(用户提供)
const PACKAGE_NAMES = {
CLEAN: "com.android.xqqnews", // 清理应用
XHS: "com.xingin.xhs", // 小红书
XINGTU: "com.lemon.photoeditor", // 醒图(标准包名)
SETTINGS: "com.android.settings" // 设置
};
// 强制启动函数(解决MIUI限制)
function forceLaunch(packageName, timeout = 5000) {
try {
console.log("强制启动: " + packageName);
const intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setPackage(packageName);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
context.startActivity(intent);
// 等待启动完成
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
if (currentPackage() === packageName) {
console.log("启动成功");
return true;
}
sleep(500);
}
console.error("启动超时");
return false;
} catch (e) {
console.error("启动失败: " + e.message);
return false;
}
}
// 等待元素出现
function waitForElement(text, timeout = 20) {
const startTime = Date.now();
while (Date.now() - startTime < timeout * 1000) {
if (textExists(text)) return true;
sleep(1000);
}
return false;
}
// 主循环
while (true) {
try {
// 阶段1: 清理应用
if (!forceLaunch(PACKAGE_NAMES.CLEAN)) {
toast("清理应用启动失败");
exit();
}
sleep(20000);
home();
// 阶段2: 小红书操作
if (!forceLaunch(PACKAGE_NAMES.XHS)) {
toast("小红书启动失败");
exit();
}
if (waitForElement("去登录")) {
click(text("去登录"));
}
sleep(3000);
home();
// 阶段3: 醒图应用
if (!forceLaunch(PACKAGE_NAMES.XINGTU)) {
toast("醒图启动失败");
exit();
}
sleep(20000);
// 下滑找到小红书项目
for (let i = 0; i < 3; i++) {
scrollDown();
sleep(1000);
if (waitForElement("小红书")) {
click(text("小红书"));
sleep(20000);
// 点击通道一
if (waitForElement("通道一")) {
click(text("通道一"));
sleep(5000);
// 处理登录状态
if (currentPackage() !== PACKAGE_NAMES.XINGTU) {
back();
if (waitForElement("小红书")) {
click(text("小红书"));
sleep(20000);
if (waitForElement("复扫")) {
click(text("复扫"));
if (waitForElement("第一个")) {
click(text("第一个"));
}
}
}
}
// 小红书刷新操作
if (currentPackage() === PACKAGE_NAMES.XHS) {
// 主页刷新
for (let j = 0; j < 3; j++) {
scrollUp();
scrollDown();
sleep(1000);
}
// 消息页面
if (waitForElement("消息")) {
click(text("消息"));
for (let j = 0; j < 3; j++) {
scrollUp();
scrollDown();
sleep(1000);
}
}
// 我的页面
if (waitForElement("我的")) {
click(text("我的"));
for (let j = 0; j < 3; j++) {
scrollUp();
scrollDown();
sleep(1000);
}
}
}
}
break;
}
}
home();
// 阶段4: 备份操作
if (!forceLaunch(PACKAGE_NAMES.SETTINGS)) {
toast("设置启动失败");
exit();
}
if (waitForElement("我的设备")) {
click(text("我的设备"));
scrollDown();
if (waitForElement("备份及恢复")) {
click(text("备份及恢复"));
if (waitForElement("U盘备份恢复")) {
click(text("U盘备份恢复"));
// 取消全选
if (waitForElement("全选")) click(text("全选"));
// 勾选小红书
scrollDown();
if (waitForElement("小红书")) {
if (!isChecked(text("小红书"))) click(text("小红书"));
}
// 立即备份
if (waitForElement("立即备份")) {
click(text("立即备份"));
if (waitForElement("完成", 300)) click(text("完成"));
}
}
}
}
home();
// 阶段5: 通知栏处理
expandNotification();
if (waitForElement("备份完成")) {
click(text("备份完成"));
if (waitForElement("完成")) click(text("完成"));
}
collapseNotification();
// 阶段6: 清理操作
if (!forceLaunch(PACKAGE_NAMES.CLEAN)) {
toast("清理应用启动失败");
exit();
}
if (waitForElement("停止")) {
click(text("停止"));
if (waitForElement("确定")) click(text("确定"));
}
if (waitForElement("开始清理")) click(text("开始清理"));
// 等待重启
sleep(60000);
} catch (e) {
console.error("执行错误: " + e.stack);
toast("错误: " + e.message);
sleep(5000); // 错误后延迟重试
}
}
这个为啥无法运行
回答
文档地址 https://docs.hamibot.com/
你的脚本无法运行,可能由以下几个常见原因导致。以下是针对你提供的脚本和 Redmi K20(MIUI 12.0.4)环境的详细分析与修改建议:
✅ 1. 缺少必要权限
Hamibot 脚本必须在开启「无障碍服务」的前提下才能运行。
解决方法:
- 打开 Hamibot 应用 → 点击左上角菜单 → 进入「无障碍服务」→ 启用 Hamibot。
- 若已开启但仍然失败,请尝试重启设备或手动在系统设置中授权:
设置 → 无障碍 → 已下载的服务 → 启用 Hamibot
🔴 注意:MIUI 对后台服务限制较严,即使开启了无障碍,也可能被系统自动杀掉。建议将 Hamibot 加入“电池优化”白名单,并锁定应用不被清理。
✅ 2. context, Intent 等原生 Android API 不可用
你在脚本中使用了如下代码:
const intent = new Intent(Intent.ACTION_MAIN);
...
context.startActivity(intent);
这是调用 Android 原生 Java API 的方式,但在 Hamibot 的 Rhino 脚本环境中,默认不支持直接访问 context 或 Intent 类,除非你通过 runtime 反射获取。
错误表现:
- 报错:
ReferenceError: "Intent" is not defined - 或者
context is undefined
✅ 正确做法:使用 app.launch() 启动应用
Hamibot 提供了更简单的封装函数来启动应用:
// 替换 forceLaunch 函数为以下版本
function forceLaunch(packageName, appName) {
console.log("尝试启动: " + packageName);
if (app.launch(packageName)) {
sleep(3000); // 给予启动时间
return true;
} else {
// 尝试通过名称启动
if (appName && app.launchApp(appName)) {
sleep(3000);
return true;
}
console.error("启动失败:" + packageName);
return false;
}
}
然后调用时传入包名即可,例如:
forceLaunch(PACKAGE_NAMES.XHS, "小红书");
⚠️ 不推荐直接操作
Intent和context,除非你知道如何用 Java 反射机制安全调用。
✅ 3. textExists(), text(), click() 等控件操作需依赖「无障碍服务」且控件可识别
你使用了如:
if (textExists("去登录")) {
click(text("去登录"));
}
这需要满足两个条件:
- 当前界面确实存在该文本;
- 无障碍服务能读取到该 UI 控件。
问题点:
textExists(str)是正确用法;- 但
click(text("xxx"))写法有误!应改为text("xxx").findOne().click()。
✅ 修改建议:
// ❌ 错误写法
click(text("去登录"));
// ✅ 正确写法
if (text("去登录").exists()) {
text("去登录").findOne().click();
}
同理,所有 click(text(...)) 都要改成 .findOne().click()。
✅ 4. currentPackage() 函数不存在
你在判断当前应用时用了:
if (currentPackage() === packageName)
这个函数在 Hamibot 中并不存在。
✅ 正确函数是:currentPackage() 应为 currentApp().packageName
// 获取当前应用信息
function getCurrentPackage() {
return currentApp().packageName;
}
// 使用示例:
if (getCurrentPackage() === packageName) {
console.log("已进入目标应用");
}
✅ 5. scrollUp(), scrollDown() 并非标准 API
Hamibot 没有内置 scrollUp() 或 scrollDown() 函数。
✅ 正确滚动方式是使用 scrollForward() 和 scrollBackward()
// 向上滑动(前进方向)
scrollForward();
// 向下滑动(回退方向)
scrollBackward();
如果你希望模拟手势滑动,也可以使用 swipe():
// 从屏幕中间向下 swipe,模拟下滑
swipe(device.width / 2, device.height * 0.7, device.width / 2,