您好,我咨询一下 engines.execScriptFile 本地文件调度方案的稳定性,我的整体架构如下:
常驻云端守护主脚本,通过轮询读取手机本地 JSON 指令文件接收 APP 下发任务;
收到微信销售任务后,调用 engines.execScriptFile 读取手机自定义目录 /sdcard/WLmob_AI/business/wechat/wechat_sales.js 本地业务脚本;
使用 arguments 把 JSON 任务配置透传给子脚本,子脚本通过 engines.myEngine().execArgv 获取参数执行业务流程。
现有 3 个核心问题请教:
云端常驻守护脚本调用 execScriptFile 读取手机内部存储自定义文件夹下的 JS 文件,能否稳定完整运行?会不会出现子脚本瞬间结束、运行时长 0 秒、逻辑不执行的问题?
execScriptFile 通过 arguments 传递字符串参数、子脚本用 execArgv 接收的传参方式,是否官方长期兼容、无底层 bug?
手机重启、Hamibot 后台完全关闭重开后,读取 /sdcard/WLmob_AI/ 自定义文件夹内文件是否存在权限拦截,需要额外做权限适配吗?
补充背景:之前使用 engines.execScript("云端识别码","",{}) 远程拉起方案,会固定出现子脚本秒退出无法完整执行,因此切换成本地文件方案,想确认这套架构是否可行,避免反复调试重构。
另外,请给出:实现的技术参考
回答
你的架构设计思路是合理的,把远程脚本替换为本地文件执行,能从根本上规避网络加载带来的不确定性。engines.execScriptFile 是 Hamibot 提供的一个稳定、底层的脚本调度接口,完全可以支撑你这种常驻守护 + 按需拉取本地业务脚本的模式。
下面针对你的三个核心问题逐一说明。
1. execScriptFile 能否稳定完整运行?会不会出现子脚本瞬间结束、逻辑不执行?
engines.execScriptFile 本身是稳定可靠的,它会像正常启动一个脚本那样去执行指定路径的 JS 文件,不会因为调用方式本身而导致子脚本瞬间退出或逻辑不执行。
你之前遇到的 engines.execScript 远程脚本秒退问题,通常是网络加载超时、云端脚本内容为空或脚本自身报错所致。换成本地文件后,这些风险都不存在了。如果子脚本依然出现 0 秒结束,常见原因有:
- 脚本文件路径错误,
execScriptFile找不到文件时会静默失败,建议调用前用files.exists(path)校验。 - 业务脚本内部没有阻塞逻辑(如没有使用
setInterval、events.on等),执行完最后一行代码后脚本自然退出。如果你的业务脚本是“一次性执行完就结束”的任务,这是正常行为,守护脚本只需监听ScriptExecution的退出事件即可。 - 业务脚本在执行过程中抛出了未捕获的异常,导致脚本终止。你可以在业务脚本最外层包裹
try...catch并把错误写入日志,方便排查。
建议:在守护脚本中启动子脚本时,获取 ScriptExecution 对象并监听 on('exit') 事件,记录退出码和输出,这样可以清晰知道子脚本是正常结束还是异常退出。
2. arguments 传参 / execArgv 接收的方式是否官方兼容、无底层 bug?
这是官方文档明确支持的参数传递方式,长期兼容,可以放心使用。
- 调用
engines.execScriptFile(path, arg1, arg2, ...)时,path之后的所有参数都会作为独立参数传递给子脚本的arguments对象。 - 在子脚本中,通过
engines.myEngine().execArgv可以获取到一个数组,顺序与调用时传入的参数一致。
文档地址 https://docs.hamibot.com/ (你可以在脚本注释中标注)已经说明了这种用法,且 Hamibot 的脚本引擎底层基于 Rhino,对 arguments 和 execArgv 的支持是框架级别的,不会有突然失效的隐患。
注意:由于引擎是 Rhino 1.7.15,建议在传递 JSON 字符串时,确保内容不会破坏 JavaScript 语法。比如尽量使用 JSON.stringify 序列化,子脚本中再用 JSON.parse 还原,避免手动拼接字符串。
3. 手机重启 / Hamibot 重开后,读取 /sdcard/WLmob_AI/ 自定义文件夹是否存在权限拦截?
一般不需要额外权限适配,但存在少数设备因权限回收导致读取失败的可能,加一层主动检查会更稳妥。
- Hamibot 在安装和使用时,通常会申请并保持
存储权限,重启后系统自动授予。大多数情况下,/sdcard/目录可直接读取。 - 但有部分手机系统(如小米、OPPO 的某些版本)在电池优化或后台管理中,可能会在重启后重置权限,导致 Hamibot 无法访问存储。此时
files.exists()会返回false或files.read()抛出异常。 - 一个简单有效的防御策略是:在守护脚本首次启动(或每次读取文件前)检查权限,如果权限缺失,则引导用户重新授权。
示例代码(放在守护脚本开头):
// 文档地址 https://docs.hamibot.com/
// 检查存储权限
if (!files.isDir('/sdcard/WLmob_AI/')) {
// 如果不存在,尝试请求权限
app.startActivity({
action: 'android.settings.MANAGE_APP_ALL_FILES_ACCESS_PERMISSION',
packageName: 'com.hamibot',
});
sleep(2000);
// 这里可以再加一次判断,如果依然没有权限,退出脚本并提示用户手动授权
if (!files.isDir('/sdcard/WLmob_AI/')) {
toast('请授予Hamibot“所有文件访问权限”后重新运行脚本');
exit();
}
}
这样即使权限被回收,也能快速恢复,不会让整个守护脚本崩溃。
技术实现参考
守护脚本(常驻云端)核心逻辑:
// 文档地址 https://docs.hamibot.com/
// 轮询检查任务文件
while (true) {
var taskFile = '/sdcard/WLmob_AI/task.json';
if (files.exists(taskFile)) {
var taskConfig = files.read(taskFile);
// 读取后删除或重命名,避免重复执行
files.remove(taskFile);
// 执行本地业务脚本,传递 JSON 字符串
var execution = engines.execScriptFile(
'/sdcard/WLmob_AI/business/wechat/wechat_sales.js',
taskConfig
);
// 监听子脚本退出事件
execution.on('exit', function(code) {
// 可以在这里记录日志或处理结果
console.log('子脚本退出,退出码:' + code);
});
// 如果业务脚本是短任务,这里可以适当延时,避免同时启动多个实例
sleep(1000);
}
sleep(3000); // 轮询间隔
}
业务脚本 wechat_sales.js 基本结构:
// 文档地址 https://docs.hamibot.com/
try {
// 获取传入的参数
var execArgv = engines.myEngine().execArgv;
var taskConfig = {};
if (execArgv && execArgv.length > 0) {
taskConfig = JSON.parse(execArgv[0]);
}
// 执行业务逻辑......
console.log('任务开始执行,配置:' + JSON.stringify(taskConfig));
// 你的业务代码
// 如果是一次性任务,执行完会自动退出,守护脚本会收到 exit 事件
} catch (e) {
console.error('业务脚本异常:' + e.message);
// 确保主脚本知道这里失败了,可以通过写入失败日志文件等方式
}
总结:
你目前这套架构完全可行,execScriptFile + arguments/execArgv 是稳定且官方支持的方案,比远程 execScript 更适合生产环境。只要做好文件存在性校验、子脚本异常捕获和必要的权限检查,就可以稳定运行,避免反复重构。