Tauri 监听器开关按钮后端逻辑

设计思路

  • 启动任务: 当用户请求启动任务时,你需要:
    1. 获取 AppState 的锁。
    2. 检查 watch_task 是否已经是 Some
    3. 如果是 Some,可能需要先取消旧的任务(old_handle.abort()),然后等待它真正停止(old_handle.await,或者在 Tauri 中可能通过其他机制处理)。
    4. 使用 tokio::spawn() 启动新任务,得到一个 JoinHandle
    5. 将这个 JoinHandle 存入 watch_task 中(Some(new_handle))。
    6. 释放锁。
  • 停止任务: 当用户请求停止任务时,你需要:
    1. 获取 AppState 的锁。
    2. 检查 watch_task 是否是 Some
    3. 如果是 Some,获取 JoinHandle,调用 handle.abort() 来尝试取消它。
    4. 将 watch_task 设置回 None
    5. 释放锁。
  • 检查任务状态: 你可能还需要一个命令来检查是否有任务在运行。
    1. 获取 AppState 的锁。
    2. 检查 watch_task 是否是 Some
    3. 返回 true 或 false
    4. 释放锁。

代码实现

AppState结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use std::{sync::Arc, thread::JoinHandle};
use tokio::sync::Mutex;

pub struct AppState {
    pub watch_task: Arc<Mutex<Option<JoinHandle<()>>>>,
}

impl AppState {
    pub fn new() -> Self {
        AppState {
            watch_task: Arc::new(Mutex::new(None)),
        }
    }
}

impl Default for AppState {
    fn default() -> Self {
        Self::new()
    }
}

Tauri部分

引用AppState

1
2
    tauri::Builder::default()
.manage(AppState::new())

tauri::Builder::manage() 方法的作用:

  • manage() 方法是 Tauri 提供的用于管理应用程序全局状态的机制。
  • 当调用 builder.manage(some_value) 时,Tauri 会将 some_value 的一个克隆(如果 some_value 是 Clone 的)或者一个共享引用(如果 some_value 是 Arc 的) 存储在应用程序的内部状态管理器中。
  • 在例子中,AppState::new() 创建了一个 AppState 实例。由于 AppState 内部使用了 Arc,Tauri 实际上会将这个 Arc<Mutex<Option<JoinHandle<()>>>> 的共享引用存储起来。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#[tauri::command]
pub async fn start_watching(state: State<'_, AppState>) -> Result<(), String> {
    // 检查是否已经在监听
    let mut task_guard = state.watch_task.lock().await;
    if task_guard.is_some() {
        return Err("Already watching".to_string());
    }

    let on_start = move || {
// 开启时运行的函数
    };

    let on_stop = move || {
// 关闭时运行的函数
    };

    let task = tokio::spawn(async move {
        watch_task(on_start, on_stop).await;
    });

    *task_guard = Some(task);
    println!("监听已启动");
    Ok(())
}

#[tauri::command]
pub async fn stop_watching(state: State<'_, AppState>) -> Result<(), String> {
    let mut task_guard = state.watch_task.lock().await;
    if let Some(task) = task_guard.take() {
        task.abort();
        println!("监听已停止");
        Ok(())
    } else {
        Err("Not watching".to_string())
    }
}


pub async fn watch_valorant<S, F>(on_start: S, on_stop: F)
where
    S: Fn() + Send + 'static,
    F: Fn() + Send + 'static,
{
    let mut sys = System::new_all();
    let mut interval = interval(time::Duration::from_secs(3));
    let mut valorant_running = false;

    loop {
        interval.tick().await;
        sys.refresh_all();

        let is_running = // 具体实现
        if is_running {
            on_start();
        } else {
            on_stop();
        }
    }
}

** tauri::State<'_, AppState>:**

  • tauri::State: 这是 Tauri 提供的一个用于在命令、事件等处理函数中访问已注册状态的类型。
  • <'_, AppState>: 这是一个生命周期和类型参数。
    • '_: 表示生命周期是匿名的,Tauri 会自动管理它的生命周期,确保它不会比应用程序本身活得更久。
    • AppState: 指定了我们要访问的状态类型是 AppState
  • 当你将 app_state: State<'_, AppState> 作为命令参数时,Tauri 会自动查找通过 builder.manage(AppState::new()) 注册的 AppState 实例,并安全地将其传递给你的函数。

** state.watch_task.lock().await:**

  • 返回的是一个 tokio::sync::MutexGuard<'_, Option<JoinHandle<()>>>
  • MutexGuard 是一个智能指针。它实现了 Deref 和 DerefMut trait,允许你像访问 Option<JoinHandle<()>> 本身一样访问它。

interval.tick().await;:

  • interval.tick(): 这个方法返回一个 Future(一个未来的值)。这个 Future 会在下一次间隔时间到达时完成。
  • .await: 这是异步 Rust 的核心关键字。当程序执行到 .await 时,如果 Future 还没有准备好(即 3 秒还没到),当前的任务(watch_valorant 函数的执行)就会被暂停,Tokio 运行时会去调度执行其他可以运行的任务。一旦 3 秒到了,interval.tick() 的 Future 就完成了,暂停的任务会被唤醒,继续执行 interval.tick().await; 后面的代码。