var window = null
var taskThreads = null
function init() {
window = floaty.rawWindow(
<vertical w='140dp' h='240dp' padding='8dp' bg='#80000000' id='container'>
<button w='50dp' h='40dp' bg='#03E06D' text='测试' textSize='14sp' textColor='#000000' id='btn1' />
</vertical>
)
// 设置悬浮窗属性
window.setPosition(0, 100)
window.setSize(200, 800)
}
function ceshi(callback) {
console.log('ceshi 执行中...');
setTimeout(() => {
callback(111)
}, 1000)
}
window.btn1.click(() => {
// 按钮1的点击事件
taskThreads = threads.start(function () {
console.log('点击我了');
ceshi()
})
// console.log('点击我了');
// function handle(num){
// console.log('handle 执行中...',num);
// }
// ceshi(handle)
})
main();
setInterval(() => {
if(taskThreads){
if(taskThreads.isAlive()){
console.log(1111);
}
}
}, 1000);
这样的脚本为什么 会出现 hamibot被退出的情况呢
回答
// 文档地址 https://docs.hamibot.com/
你的脚本可能导致 Hamibot 被退出(崩溃或强制关闭)的主要原因是 在子线程中调用了 ceshi() 函数,而该函数内部使用了 setTimeout,但没有正确处理线程上下文与 JavaScript 引擎的兼容性问题。
具体分析如下:
🔴 问题定位
-
threads.start()创建了一个新线程taskThreads = threads.start(function () { console.log('点击我了'); ceshi() })- 这个线程是 Rhino 引擎下的独立执行环境。
- 所有在这个线程中运行的代码不会自动绑定到主脚本环境的安全机制中。
-
ceshi()中使用了setTimeout(callback, delay)function ceshi(callback) { console.log('ceshi 执行中...'); setTimeout(() => { callback(111) }, 1000) }setTimeout是 Hamibot 提供的全局函数,用于在 主线程(UI 线程) 延迟执行任务。- 它本质上将回调加入主事件循环队列。
- 但你从一个
threads.start的子线程中调用了它,并且传入了一个来自子线程的callback函数。
-
❌ 跨线程传递函数引用是不安全的操作
- JavaScript 引擎(Rhino 1.7.15)并不支持将函数对象跨线程自由传递。
- 当
setTimeout在主线程尝试调用那个已属于子线程的callback(111)时,可能会导致:- 上下文丢失
- 函数作用域异常
- Native 层报错(如
JSException,DeadObjectException)
- 最终触发 Hamibot 崩溃并退出。
-
✅ 正确做法:所有涉及 UI 操作、定时器、回调注册的行为应尽量在主线程进行,或通过安全通信方式交互。
✅ 解决方案
✅ 方案一:避免在子线程调用 setTimeout 和回调函数
将异步逻辑移到主线程处理,子线程只负责耗时任务,通过标志位或事件通知主线程去执行后续操作。
// 文档地址 https://docs.hamibot.com/
var window = null
var taskThreads = null
var isRunning = false
function init() {
window = floaty.rawWindow(
<vertical w='140dp' h='240dp' padding='8dp' bg='#80000000' id='container'>
<button w='50dp' h='40dp' bg='#03E06D' text='测试' textSize='14sp' textColor='#000000' id='btn1' />
</vertical>
)
window.setPosition(0, 100)
window.setSize(-2, -2) // 自动适配内容大小
}
init()
window.btn1.click(() => {
if (isRunning) return
isRunning = true
console.log('点击我了')
// 启动子线程执行任务(仅做工作,不触发回调)
taskThreads = threads.start(function () {
console.log('子线程开始执行...')
// 模拟耗时任务
sleep(1000)
// 任务完成,通知主线程处理结果
ui.run(function () {
// 回到主线程再调用 handle
handle(111)
isRunning = false
})
})
})
function handle(num) {
console.log('handle 执行中...', num)
}
setInterval(() => {
if (taskThreads && taskThreads.isAlive()) {
console.log('任务线程正在运行')
}
}, 1000)
// 主循环保持脚本存活
function main() {
hamibot.keepRunning()
}
main()
✅ 方案二:使用 events 模块进行线程间通信(高级)
如果你需要更复杂的线程通信,可以使用 events.post 和 events.on 来解耦线程间的依赖。
⚠️ 其他建议
- 不要在任何非 UI 线程中直接调用
setTimeout,setInterval,ui.run, 或对悬浮窗元素进行修改。 - 子线程中如需更新 UI,请使用 `ui