Tauri(九)—— 实现桌面应用的开机自启动

1,095 阅读5分钟

前言

在许多桌面应用程序中,实现开机自启动功能是一项常见需求,特别是当应用需要在用户启动操作系统时自动启动并保持运行时。这篇文章将介绍如何在 Tauri 中实现桌面应用的开机自启动功能。

实现原理

Tauri 提供了 tauri-plugin-autostart 插件,允许开发者方便地将应用设置为开机自启动。该插件支持多种操作系统(Windows、macOS、Linux),并提供简单的 API 来控制自启动功能。

安装插件

  1. 安装插件: 要启用开机自启动功能,首先需要在 Tauri 项目中安装 tauri-plugin-autostart 插件。

    在项目根目录下打开终端,运行以下命令:

    cargo tauri plugin add tauri-plugin-autostart
    
  2. 增加权限设置: 安装完插件后,需要在 src-tauri/capabilities/default.json 配置文件中进行相关设置。

    src-tauri/capabilities/default.json**** 文件中的 permissions 部分添加 autostart 配置。

    {
      "$schema": "../gen/schemas/desktop-schema.json",
      "identifier": "default",
      "description": "Capability for the main window",
      "windows": ["main", "chat", "settings"],
      "permissions": [
    
        "autostart:allow-enable",
        "autostart:allow-disable",
        "autostart:allow-is-enabled",
      ]
    }
    

Rust 代码实现

src-tauri/src/autostart.rs 中实现核心功能:

use std::{fs::create_dir, io::Read};
use tauri::{Manager, Runtime};
use tauri_plugin_autostart::ManagerExt;

// 根据配置启用或关闭开机自启动功能
pub fn enable_autostart(app: &mut tauri::App) {
    use tauri_plugin_autostart::MacosLauncher;

    // 初始化 tauri-plugin-autostart 插件,使用 macOS 的 AppleScript 启动器
    app.handle()
        .plugin(tauri_plugin_autostart::init(
            MacosLauncher::AppleScript,
            None, // None 表示不指定额外的配置路径
        ))
        .unwrap();

    let autostart_manager = app.autolaunch(); // 获取自动启动管理器实例

    // 如果需要直接关闭自启动,可以在这里调用 disable 并提前返回
    // autostart_manager.disable().unwrap();
    // return;

    // 根据当前的自启动状态和本地配置文件的记录,启用或禁用自启动
    match (
        autostart_manager.is_enabled(),        // 检查当前自启动是否已启用
        current_autostart(app.app_handle()),   // 检查本地配置中是否应该启用自启动
    ) {
        (Ok(false), Ok(true)) => match autostart_manager.enable() {
            Ok(_) => println!("Autostart enabled successfully."), // 启用成功
            Err(err) => eprintln!("Failed to enable autostart: {}", err), // 启用失败
        },
        (Ok(true), Ok(false)) => match autostart_manager.disable() {
            Ok(_) => println!("Autostart disabled successfully."), // 禁用成功
            Err(err) => eprintln!("Failed to disable autostart: {}", err), // 禁用失败
        },
        _ => (), // 状态一致,无需操作
    }
}

// 检查当前的本地自启动配置状态
fn current_autostart<R: Runtime>(app: &tauri::AppHandle<R>) -> Result<bool, String> {
    use std::fs::File;

    let path = app.path().app_config_dir().unwrap(); // 获取应用的配置目录
    let mut old_value = true; // 默认值为 true

    // 如果配置目录存在
    if path.exists() {
        let file_path = path.join("autostart.txt"); // 配置文件路径
        if file_path.exists() {
            let mut file = File::open(file_path).unwrap(); // 打开配置文件
            let mut data = String::new();
            if let Ok(_) = file.read_to_string(&mut data) {
                if !data.is_empty() {
                    old_value = data.parse().unwrap_or(true); // 解析文件内容为布尔值
                }
            }
        }
    };

    Ok(old_value) // 返回结果
}

// 提供一个命令供前端调用,用于动态更改开机自启动状态
#[tauri::command]
pub fn change_autostart<R: Runtime>(app: tauri::AppHandle<R>, open: bool) -> Result<(), String> {
    use std::fs::File;
    use std::io::Write;

    let autostart_manager = app.autolaunch(); // 获取自动启动管理器实例

    // 内部的状态修改函数,根据 open 参数启用或禁用自启动
    let change = |open: bool| -> Result<(), String> {
        #[allow(unused_assignments)]
        let mut open_str = String::from("");
        if open {
            // 启用自启动
            autostart_manager
                .enable()
                .map_err(|_| "enable autostart failed".to_owned())?;

            open_str = "true".to_owned(); // 记录为启用状态
        } else {
            // 禁用自启动
            autostart_manager
                .disable()
                .map_err(|_| "disable autostart failed".to_owned())?;

            open_str = "false".to_owned(); // 记录为禁用状态
        }

        // 获取应用的配置目录
        let path = app
            .path()
            .app_config_dir()
            .map_err(|_| "not found app config directory".to_owned())?;
        if !path.exists() {
            // 如果配置目录不存在,创建它
            create_dir(&path).map_err(|_| "creating app config directory failed".to_owned())?;
        }

        // 将新的自启动状态写入配置文件
        let file_path = path.join("autostart.txt");
        let mut file = File::create(file_path).unwrap();
        file.write_all(open_str.as_bytes()).unwrap();

        Ok(())
    };

    // 根据当前状态和传入参数决定是否需要更改状态
    match (autostart_manager.is_enabled().unwrap(), open) {
        (false, true) => change(true),  // 从禁用改为启用
        (true, false) => change(false), // 从启用改为禁用
        _ => Err("no change".to_owned()), // 状态未改变
    }
}

注册进 src-tauri/src/lib.rs 文件中:

mod autostart;

use autostart::{change_autostart, enable_autostart};
use tauri_plugin_autostart::MacosLauncher;

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    let mut ctx = tauri::generate_context!();

    tauri::Builder::default()
        
        .plugin(tauri_plugin_autostart::init(
            MacosLauncher::AppleScript,
            None,
        ))
        
        .invoke_handler(tauri::generate_handler![

            change_autostart,
      
        ])
        .setup(|app| {
            init(app.app_handle());

            enable_autostart(app);

            Ok(())
        })
        .run(ctx)
        .expect("error while running tauri application");
}

前端设置自启动

在前端,调用后端命令实现状态切换:

import { isTauri, invoke } from "@tauri-apps/api/core";
import {
  isEnabled,
  // enable, disable
} from "@tauri-apps/plugin-autostart";


const [launchAtLogin, setLaunchAtLogin] = useState(true);

useEffect(() => {
    const fetchAutoStartStatus = async () => {
      if (isTauri()) {
        try {
          const status = await isEnabled();
          setLaunchAtLogin(status);
        } catch (error) {
          console.error("Failed to fetch autostart status:", error);
        }
      }
    };

    fetchAutoStartStatus();
  }, []);

  const enableAutoStart = async () => {
    if (isTauri()) {
      try {
        // await enable();
        invoke("change_autostart", { open: true });
      } catch (error) {
        console.error("Failed to enable autostart:", error);
      }
    }
    setLaunchAtLogin(true);
  };

  const disableAutoStart = async () => {
    if (isTauri()) {
      try {
        // await disable();
        invoke("change_autostart", { open: false });
      } catch (error) {
        console.error("Failed to disable autostart:", error);
      }
    }
    setLaunchAtLogin(false);
  };

小结

通过 tauri-plugin-autostart 插件,可以轻松为 Tauri 应用实现开机自启动功能。该插件支持多个平台,简化了跨平台自启动功能的开发。通过配置和调用 Tauri API,你可以方便地管理应用的自启动状态。

希望这篇文章能帮助你实现 Tauri 桌面应用的开机自启动功能。如果有其他问题,欢迎随时讨论!

开源

最近,我正在基于 Tauri 开发一款项目,名为 Coco。目前已开源,项目仍在不断完善中,欢迎大家前往支持并为项目点亮免费的 star 🌟!

作为个人的第一个 Tauri 项目,开发过程中也是边探索边学习。希望能与志同道合的朋友一起交流经验、共同成长!

代码中如有问题或不足之处,期待小伙伴们的宝贵建议和指导!

非常感谢您的支持与关注!