hamibot的截屏授权能否静默授权
能否一次提示后,可以永久授权权限?
openConsole();
// 申请截图权限
if (!requestScreenCapture()) {
toastLog('没有授予 Hamibot 屏幕截图权限');
hamibot.exit();
}
sleep(1000);
// 有限循环,找到目标就退出
for (let cnt = 1; cnt <= maxTryCount; cnt++) {
if (isFound) break; // 找到目标后立即退出
// 1. 截图(不手动释放,交给垃圾回收)
const img = captureScreen();
log(`第${cnt}次识别,开始识别`);
// 2. 执行OCR识别(关键:不手动释放img)
const ocrResult = ocr.recognize(img);
const ticketPos = getBuyTicketPosition(ocrResult,"立即购票");
// 3. 找到目标则点击并标记退出
if (ticketPos) {
HID_position_xclick(ticketPos.center.x, ticketPos.center.y, 20);
isFound = true;
log("已找到并点击「立即购票」按钮,退出循环");
}
// 4. 每20次打印日志
if (cnt % 20 === 0) {
log("已尝试次数:" + cnt);
}
// !!!核心修改:删除 img.recycle(),放弃手动释放!!!
sleep(500);
}
// 未找到目标的提示
if (!isFound) {
log(尝试${maxTryCount}次后仍未找到「立即购票」按钮);
}
// 查找目标文本位置的函数(无修改)
function getBuyTicketPosition(ocrResult,xtext) {
if (!ocrResult || !ocrResult.results || ocrResult.results.length === 0) {
log('OCR识别结果为空');
return null;
}
for (let item of ocrResult.results) {
if (item.text && item.text.trim() === xtext) {
const { left, top, right, bottom } = item.bounds;
const centerX = (left + right) / 2;
const centerY = (top + bottom) / 2;
log(xtext + '原始边界坐标:', { left, top, right, bottom });
log(xtext + '中心坐标:', { x: centerX, y: centerY });
log(`找到(${xtext}),中心坐标:(${centerX}, ${centerY})`);
return {
rawBounds: { left, top, right, bottom },
center: { x: centerX, y: centerY }
};
}
}
log('未找到'+ xtext + '文本块');
return null;
}
为什么会出现E: Wrapped java.lang.IllegalStateException: image has been recycled (file:///android_asset/modules/ocr.js#10)
Wrapped java.lang.IllegalStateException: image has been recycled
at file:///android_asset/modules/ocr.js:10:0
at 695f0cae8183b0a42e549679.js:20:0
回答
Hamibot的截屏授权不能静默授权。这是由Android系统安全机制决定的,所有应用包括Hamibot在获取屏幕截图权限时都必须经过用户明确授权。
在脚本执行过程中,你需要使用以下标准代码请求截屏权限:
//文档地址 https://docs.hamibot.com/
auto.waitFor();
if (!requestScreenCapture()) {
toastLog('没有授予 Hamibot 屏幕截图权限');
hamibot.exit();
}
sleep(1000);
当脚本运行到requestScreenCapture()时,系统会弹出权限请求对话框,用户必须手动点击"开始录制"按钮才能完成授权。这是Android系统的强制安全要求,任何应用都不能绕过这个流程进行静默授权。
Android系统对涉及用户隐私的权限(如屏幕录制)有严格规定,必须由用户主动确认,这是为了保护用户隐私安全。Hamibot严格遵守Android权限管理规范,不会也不应提供静默授权功能。
如果你希望提升用户体验,可以在脚本开始执行前通过toast提示用户准备授权,但实际授权过程仍需用户手动操作。
补充回答 1
可以一次提示后永久授权权限,但这需要用户在权限请求对话框中进行特定操作。
当首次运行需要截屏的脚本时,系统会弹出权限请求对话框。根据知识库中的说明,此时你应该"选择'总是允许'"。不同手机系统显示的选项可能略有不同:
- 在大多数Android设备上,权限对话框底部会有一个"记住选择"或"始终允许"的复选框
- 你需要勾选这个选项,然后点击"开始录制"按钮
以MIUI系统为例(知识库[5]中提到):
"屏幕截图此权限是在运行中弹出,建议勾选记住选择,避免每次运行脚本都需要选择。"
一旦你这样操作后,Hamibot就会被系统记住授权状态,后续运行脚本时将不再弹出授权对话框,实现"永久授权"的效果。
需要注意:
- 这个设置是系统级的,不是Hamibot可以控制的
- 如果你清除应用数据或重置手机,可能需要重新授权
- 某些手机系统可能没有"记住选择"选项,这时每次运行都需要授权
- 重启设备后可能需要重新启用无障碍服务(知识库[5]中提示)
脚本中只需在开始处调用一次requestScreenCapture(),无需每次截图都调用:
//文档地址 https://docs.hamibot.com/
auto.waitFor();
if (!requestScreenCapture()) {
toastLog('没有授予 Hamibot 屏幕截图权限');
hamibot.exit();
}
// 后续可以直接使用captureScreen(),无需再次请求权限
补充回答 2
你遇到的错误 java.lang.IllegalStateException: image has been recycled 是因为图像对象在OCR处理过程中被提前释放了。虽然你已经删除了 img.recycle() 手动释放语句,但问题依然存在,原因如下:
问题原因
-
变量作用域问题:在循环中使用
const img = captureScreen()会导致每次迭代的img变量在 JavaScript 层面被重新声明。Rhino 引擎可能会在本次迭代的ocr.recognize()调用结束后,认为该图像对象不再被引用,从而触发 Java 层面的垃圾回收。 -
OCR内部实现机制:
ocr.recognize()虽然是同步函数,但其内部可能有异步操作或延迟处理,当 JavaScript 代码继续执行时,Java 层可能仍在使用图像对象。一旦 JavaScript 变量超出作用域,Java 对象可能被提前回收。
解决方案
方案一:改用 ocr.recognizeText()(推荐)
Hamibot 的 ocr.recognizeText() 比 ocr.recognize() 实现更简洁稳定,不容易出现图像回收问题:
// 修改这部分代码
const ocrResult = ocr.recognizeText(img); // 改用 recognizeText
// 同时调整 getBuyTicketPosition 函数
function getBuyTicketPosition(ocrText, xtext) {
if (!ocrText) return null;
// 简单的文本匹配(recognizeText 返回纯文本)
if (ocrText.includes(xtext)) {
// 这里需要你根据实际界面位置计算点击坐标
// 例如假设按钮在屏幕中部偏下
const centerX = device.width / 2;
const centerY = device.height * 0.8;
return { center: { x: centerX, y: centerY } };
}
return null;
}
方案二:保持图像引用(适用于必须用 recognize 的情况)
如果必须使用 ocr.recognize() 获取详细坐标信息,确保图像对象在整个OCR过程中保持有效引用:
let imgRef = null; // 全局引用
for (let cnt = 1; cnt <= maxTryCount; cnt++) {
if (isFound) break;
imgRef = captureScreen(); // 保持全局引用
const ocrResult = ocr.recognize(imgRef);
// 处理结果...
sleep(500);
imgRef = null; // 显式释放引用
}
方案三:添加强制延迟(临时解决方案)
const img = captureScreen();
sleep(100); // 确保图像完全加载
const ocrResult = ocr.recognize(img);
最佳实践建议
- 优先使用
ocr.recognizeText():对于简单文本匹配场景,它更稳定且内存管理更好 - 避免在循环中频繁截图:每秒截图不要超过2-3次,建议
sleep(300-500) - 及时释放资源:虽然不要手动调用
recycle(),但应在使用完后将变量置为null - 检查 Hamibot 版本:确保使用最新版(1.2.2+),旧版本存在已知的图像管理问题
文档地址 https://docs.hamibot.com/ 中的 OCR 章节明确说明:captureScreen() 获取的图像由系统自动管理,不需要手动回收,但在循环中需注意变量作用域问题。