跳到主要内容
版本:3.0.1

进程调度

XXTouchNG 的每个脚本都是独立运行的进程,即 脚本进程。为避免多个脚本进程同时运行造成的冲突和管理不便,通过以下方式派发的脚本进程,同一时间 只能有一个实例

  • 通过音量键启动的脚本
  • 通过 OpenAPI 调用启动的脚本
  • 通过 Activator 事件触发启动的脚本
  • 通过 X.X.T. 应用程序界面启动的脚本
  • 通过 VSCode 插件、XXTStudio 等 IDE 直接调试或运行的脚本
  • 通过群控软件、云控软件等 “远程运行” 的脚本
  • 因脚本异常终止,触发 “守护模式” 重新启动的脚本
  • 由 “开机启动”、“计划任务” 等功能启动的脚本
信息
  • 你可以通过 utils.launch_args 获取当前脚本的启动参数,以便根据不同的启动方式做出不同的处理。
  • 由命令行、云控守护服务 daemon.lua 等方式启动的脚本,不受以上限制。

结束脚本进程

脚本进程可以通过以下方式结束:

  • os.exit
  • 脚本运行结束
  • 通过音量键停止脚本
  • 通过 OpenAPI 调用停止脚本
  • 通过 Activator 事件触发停止脚本
  • 通过 VSCode 插件、XXTStudio 等 IDE 停止脚本
  • 通过群控软件、云控软件等 “远程停止” 脚本
  • 通过命令行 kill 向脚本进程发送 SIGINTSIGTERMSIGKILL 等信号

脚本进程结束时,会立即停止事件监听,关闭 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