hamibot的截屏授权能否静默授权

crv3yukjc8 · 1 天前
第 1 条补充
·
1 天前

能否一次提示后,可以永久授权权限?

第 2 条补充
·
19 小时前

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 AI
1
· 1 天前

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就会被系统记住授权状态,后续运行脚本时将不再弹出授权对话框,实现"永久授权"的效果。

需要注意:

  1. 这个设置是系统级的,不是Hamibot可以控制的
  2. 如果你清除应用数据或重置手机,可能需要重新授权
  3. 某些手机系统可能没有"记住选择"选项,这时每次运行都需要授权
  4. 重启设备后可能需要重新启用无障碍服务(知识库[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() 手动释放语句,但问题依然存在,原因如下:

问题原因

  1. 变量作用域问题:在循环中使用 const img = captureScreen() 会导致每次迭代的 img 变量在 JavaScript 层面被重新声明。Rhino 引擎可能会在本次迭代的 ocr.recognize() 调用结束后,认为该图像对象不再被引用,从而触发 Java 层面的垃圾回收。

  2. 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);

最佳实践建议

  1. 优先使用 ocr.recognizeText():对于简单文本匹配场景,它更稳定且内存管理更好
  2. 避免在循环中频繁截图:每秒截图不要超过2-3次,建议 sleep(300-500)
  3. 及时释放资源:虽然不要手动调用 recycle(),但应在使用完后将变量置为 null
  4. 检查 Hamibot 版本:确保使用最新版(1.2.2+),旧版本存在已知的图像管理问题

文档地址 https://docs.hamibot.com/ 中的 OCR 章节明确说明:captureScreen() 获取的图像由系统自动管理,不需要手动回收,但在循环中需注意变量作用域问题。

ℹ️ 本回答由 AI 生成,内容仅供参考