进程调度
XXTouchNG 的每个脚本都是独立运行的进程,即 脚本进程。为避免多个脚本进程同时运行造成的冲突和管理不便,通过以下方式派发的脚本进程,同一时间 只能有一个实例:
- 通过音量键启动的脚本
- 通过 OpenAPI 调用启动的脚本
- 通过 Activator 事件触发启动的脚本
- 通过 X.X.T. 应用程序界面启动的脚本
- 通过 VSCode 插件、XXTStudio 等 IDE 直接调试或运行的脚本
- 通过群控软件、云控软件等 “远程运行” 的脚本
- 因脚本异常终止,触发 “守护模式” 重新启动的脚本
- 由 “开机启动”、“计划任务” 等功能启动的脚本
信息
- 你可以通过
utils.launch_args获取当前脚本的启动参数,以便根据不同的启动方式做出不同的处理。 - 由命令行、云控守护服务
daemon.lua等方式启动的脚本,不受以上限制。
结束脚本进程
脚本进程可以通过以下方式结束:
os.exit- 脚本运行结束
- 通过音量键停止脚本
- 通过 OpenAPI 调用停止脚本
- 通过 Activator 事件触发停止脚本
- 通过 VSCode 插件、XXTStudio 等 IDE 停止脚本
- 通过群控软件、云控软件等 “远程停止” 脚本
- 通过命令行
kill向脚本进程发送SIGINT、SIGTERM或SIGKILL等信号
脚本进程结束时,会立即停止事件监听,关闭 UI,终止其派发的所有线程和子进程(忽略 SIGHUP 信号的除外),并释放该进程组占用的所有 CPU 和内存资源。
重启脚本进程
重启脚本 (os.restart)
声明
操作成败, 失败原因 = os.restart([ 脚本名称 ])
参数及返回值
- 脚本名称
- 文本型,可选,一个有效的脚本名称。默认为
""
- 文本型,可选,一个有效的脚本名称。默认为
- 操作成败
- 布尔型,只有传递了 脚本名称 的情况下才可能操作失败,操作成功则这个函数不会返回
- 失败原因 文本型
说明
- 结束当前脚本进程,并计划在 2 秒后重新加载 当前脚本文件。
- 如果传入了 脚本文件,则会重新加载此路径下的脚本文件。
- 如果操作失败,此函数返回
false并附带失败原因。常见原因是传入的脚本文件不存在。
警告
- 当前脚本 指的是启动入口脚本。
- 若 当前脚本 发生改动,
os.restart会从文件系统 重新读取 并运行更改之后的脚本文件。 - 请 不要 在多线程环境使用此函数,短延迟重启会导致的其他逻辑问题也需要你规避。
示例 1
os.restart
os.restart() -- 重启到 “当前脚本文件”
示例 2
os.restart.path
os.restart("main.lua") -- 重启到 “/var/mobile/Media/1ferver/lua/scripts/main.lua”
脚本终止回调
这不是一个函数,而是利用 Lua 的垃圾回收机制实现的,用于在脚本结束(或被结束)时执行一些代码的方法。
简易示例
-- 关键词 脚本终止回调 脚本结束回调
随便取个变量名 = {}
setmetatable(随便取个变量名, {
__gc = function(...)
sys.toast('被终止了!')
sys.msleep(500)
end
})
--
while true do
sys.toast("现在可尝试手动结束脚本\n\n"..os.date("%Y-%m-%d %H:%M:%S"))
sys.msleep(1000)
end
备注
定义一个全局对象(表型值),将其 析构函数 设为一个函数,当 Lua 虚拟机结束之时,所有 Lua 对象(也包括你定义的这个)的 析构函数 会被调用。Lua 中的 析构函数 是指对象的 __gc 元方法。
完整封装示例
atexit.lua
function atexit(callback) -- 参数为一个函数,使用 atexit(一个函数) 注册一个函数在脚本结束时执行,建议不要耗时太长
____atexit_guard____ = ____atexit_guard____ or {}
if type(____atexit_guard____) == 'table' then
if not getmetatable(____atexit_guard____) then
setmetatable(____atexit_guard____, {
__gc = function(self)
if type(self.callback) == 'function' then
pcall(self.callback)
end
end
})
end
____atexit_guard____.callback = callback
else
error('别用 `____atexit_guard____` 命名你的变量。')
end
end
-- 以上代码可拷贝到你的脚本的开头,以下为使用示例
--
-- 使用 atexit 注册一个终止回调函数
atexit(function()
sys.toast('被终止了!')
sys.msleep(500)
end)
--
while true do
sys.toast("现在可尝试手动结束脚本\n\n"..os.date("%Y-%m-%d %H:%M:%S"))
sys.msleep(1000)
end
发送全局通知 (notify_post)
声明
notify_post(通知名称)
参数及返回值
- 通知名称 文本型
ch.xxtou.notification.remote-access.on:打开远程访问ch.xxtou.notification.remote-access.off:关闭远程访问ch.xxtou.notification.restart:立即结束脚本,并重启 XXTouchNG 守护进程ch.xxtou.notification.boom:立即结束脚本,并卸载 XXTouchNG
说明
向 Darwin 操作系统发送一个最高级别的全局通知,与原生 notify_post 函数的功能相同。
锁定进程号文件 (lockfile)
声明
操作成败 = lockfile(文件路径)
参数及返回值
- 文件路径 文本型
- 操作成败 布尔型
说明
创建或锁定 文件路径,并写入脚本进程的进程号文本。即 pidfile,防止多个单例脚本同时运行。
示例
lockfile
if not lockfile("/tmp/daemon.lua.singleton") then
return -- 如果文件已经被别的进程锁定,那么说明不需要再次运行
end