Rust in Everything
基于 tauri 2.12.5

应用与管理器

这一部分讲 crates/tauri/src 里把各层串起来的那几个文件:app.rs(2.8k 行)、manager/mod.rs、lib.rs 里的 Manager trait、state.rs、resources/mod.rs、async_runtime.rs、plugin.rs。03 运行时讲到 Wry 怎么建事件循环、怎么把跨线程操作送回主线程为止;这一部分从用户写的 tauri::Builder::default()...run(generate_context!()) 讲起:build 里依次发生了什么,窗口为什么在 setup 之前就已经存在,命令、事件监听器、插件钩子各在哪个线程上跑,退出怎么被拦下来。

  1. Builder、App 与 AppHandle:Builder 只是攒配置,build 建 AppManager、调 R::new 建事件循环、注册并初始化插件,但不建窗口;窗口在事件循环第一次回调 Ready 时建,紧接着跑 setup。App 拿着运行时、不能跨线程,run 把它吃掉;AppHandle 可以克隆、可以送到任何线程。exit 先发 ExitRequested,prevent_exit 只在回调里同步调用才有效。
  2. AppManager 与 Manager trait:一个 Arc<AppManager> 装着窗口表、webview 表、事件监听器、插件、状态、ACL、invoke key 和内嵌资源,App、AppHandle、Window、Webview、WebviewWindow 都拿着它;Manager 是一个封口的 trait,靠 ManagerBase 让五种类型共享同一套方法。
  3. 状态:manage 把值按 TypeId 装箱存进一张表,State<T> 是指向箱子的引用;同一类型第二次 manage,Manager 上返回 false,Builder 上直接 panic;命令要一个没 manage 的 State<T> 时得到的错误原文;给 JS 一个 rid 的 ResourceTable。
  4. 异步运行时:async_runtime 是一个懒初始化的全局 tokio 多线程运行时,可以在第一次使用前用 set 换成自己的;实测同步命令、async 命令、事件监听器、插件钩子各在哪个线程上跑。
  5. 插件:Plugin trait 和 plugin::Builder 的每个钩子在什么时候、哪个线程、持着什么锁被调用;plugin:<name>|<cmd> 怎么分流;插件命令一律要过 ACL;内置的 core:* 插件。
flowchart TB
  bld["Builder 攒配置"] --> build["Builder::build"]
  build --> mgr["Arc AppManager"]
  build --> rt["R::new 建事件循环"]
  build --> plg["插件 initialize"]
  build --> app["App"]
  app --> run["App::run 进事件循环"]
  run --> ready["Ready: 建窗口, setup"]
  mgr --> handle["AppHandle / Window / Webview"]
  handle --> state["State 与 ResourceTable"]
  ready --> cmds["命令与事件"]
  cmds --> art["async_runtime"]

前两页讲骨架:对象怎么建出来、谁拿着谁;后三页讲挂在骨架上的三样东西。命令宏本身(参数怎么取、State 参数怎么生成)在 #[command] 与 generate_handler!,一次 invoke 进来以后怎么分到命令上在 on_message 与分发,插件命令要的权限怎么声明、怎么解析在 07 安全。

本部分的实验都在 M2 Max、macOS 27、rustc 1.99.0 上用真实的 Wry 运行时跑(窗口 "visible": false,WKWebView 照常加载页面、执行 JS),不是 MockRuntime;Windows 和 Linux 上的行为只读了源码。

💬 关联讨论

发起讨论
加载中...