跳转到内容
Tauri

移动端插件开发

插件可以运行用 Kotlin(或 Java)和 Swift 编写的本地代码。 默认插件模板包括一个使用 Kotlin 的安卓库项目和一个 Swift 包,其中包括一个示例指令,展示如何从 Rust 代码调用外部插件。

初始化插件项目

按照插件开发中的步骤初始化新的插件项目。

如果你有一个现有的插件,并且想添加安卓或 iOS 的功能,你可以使用 plugin android initplugin ios init来添加构建移动端插件项目,并指导你完成所需的更改。

默认的插件模板将插件的实现分为两个独立的模块:desktop.rsmobile.rs

桌面实现使用 Rust 代码来实现功能,而移动端实现会向本地移动端代码库发送消息以执行功能并返回结果。如果两个实现存在共享逻辑,可以在 lib.rs 中定义:

lib.rs
use tauri::Runtime;
impl<R: Runtime> <plugin-name><R> {
pub fn do_something(&self) {
// 一些桌面和移动设备之间共享的实现
}
}

这一实现简化了共享 API 的过程,该 API 可被指令和 Rust 代码使用。

开发一个安卓插件

一个安卓 Tauri 插件的定义为一个扩展了 app.tauri.plugin.Plugin 并具有 app.tauri.annotation.TauriPlugin 注解的 Kotlin 类。每个有 app.tauri.annotation.Command 注解的方法都可以被 Rust 或 JavaScript 调用。

Tauri 使用 Kotlin 作为默认的安卓插件实现,但是你也可以使用 Java 作为替代。在创建一个插件后,在 Android Studio 中右键 Kotlin 插件类并点击菜单中的 ”将 Kotlin 文件转换为 Java 文件“ 按钮。Anroid Studio 会指导你完成到 Java 项目的迁移。

开发一个 iOS 插件

一个 iOS Tauri 插件由一个扩展了 Tauri 包的 Plugin 类定义。每个具有 @objc 属性和 (_ invode: Invoke) 参数的函数(例如 @objc private func download(_ invoke: Invoke) { })都可以被 Rust 或 JavaScript 调用。

插件被定义为一个 Swift 包,因此你可以使用它的包管理器以管理依赖

插件配置

前往插件开发指南的插件配置章节以获取更多关于插件开发的配置信息。

移动设备上的插件实例具有用于插件配置的获取器:

@TauriPlugin
class ExamplePlugin(private val activity: Activity): Plugin(activity) {
override fun load(webView: WebView) {
val timeout = this.config.getInt("timeout", 30)
}
}

生命周期事件

插件可以连接到几个生命周期事件中:

  • load:当插件被加载到 webview 中时触发
  • onNewIntent:(仅安卓)当活动被重启时触发

在插件开发指南中还有一些其他插件生命周期事件的介绍。

load

  • :插件被加载到 webview 中
  • 为了:加载插件初始化代码
@TauriPlugin
class ExamplePlugin(private val activity: Activity): Plugin(activity) {
override fun load(webView: WebView) {
// 在这里启动插件
}
}

onNewIntent

备注:这只在安卓系统上有效。

  • :活动项目重启。查阅 Activity#onNewIntent 以获取更多信息。
  • 为了:处理活动重启,例如通知被点击或者深链接被使用。
// import android.content.Intent
@TauriPlugin
class ExamplePlugin(private val activity: Activity): Plugin(activity) {
override fun onNewIntent(intent: Intent) {
// 处理重启事件
}
}

添加移动端命令

在相应的移动端项目中有一个插件类,可以在其中定义能够通过 Rust 代码调用的命令:

@TauriPlugin
class ExamplePlugin(private val activity: Activity): Plugin(activity) {
@Command
fun openCamera(invoke: Invoke) {
val allowEdit = invoke.getBoolean("allowEdit", false)
val quality = invoke.getInt("quality", 100)
val ret = JSObject()
ret.put("path", "/path/to/photo.jpg")
invoke.resolve(ret)
}
}

使用 tauri::plugin::PluginHandle 以在 Rust 中创建一个移动端命令:

use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use tauri::Runtime;
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CameraRequest {
quality: usize,
allow_edit: bool,
}
#[derive(Deserialize)]
pub struct Photo {
path: PathBuf,
}
impl<R: Runtime> <plugin-name;pascal-case><R> {
pub fn open_camera(&self, payload: CameraRequest) -> crate::Result<Photo> {
self
.0
.run_mobile_plugin("openCamera", payload)
.map_err(Into::into)
}
}

权限许可

如果插件需要最终用户的权限,Tauri 简化了检查和请求权限的过程。

首先定义所需权限的列表和别名,以在代码中标识每个权限组。这是在 TauriPlugin 注解中完成的:

@TauriPlugin(
permissions = [
Permission(strings = [Manifest.permission.POST_NOTIFICATIONS], alias = "postNotification")
]
)
class ExamplePlugin(private val activity: Activity): Plugin(activity) { }

Tauri 自动为插件实现了两个命令:checkPermissionsrequestPermissions。这些命令可以从 JavaScript 或 Rust 中直接调用。

import { invoke } from '@tauri-apps/api/tauri'
type PermissionState = 'granted' | 'denied' | 'prompt' | 'prompt-with-rationale'
interface Permissions {
postNotification: PermissionState
}
// 查询权限许可状态
const permission = await invoke<Permissions>('plugin:<plugin-name>|checkPermissions')
if (permission.postNotification === 'prompt-with-rationale') {
// 告诉用户为什么需要权限
}
// 请求权限
if (permission.postNotification.startsWith('prompt')) {
const state = await invoke<Permissions>('plugin:<plugin-name>|requestPermissions', { permissions: ['postNotification'] })
}

插件事件

插件可以使用 trigger 函数在任何时候触发事件:

@TauriPlugin
class ExamplePlugin(private val activity: Activity): Plugin(activity) {
override fun load(webView: WebView) {
trigger("load", JSObject())
}
override fun onNewIntent(intent: Intent) {
// 处理新意图
if (intent.action == Intent.ACTION_VIEW) {
val data = intent.data.toString()
val event = JSObject()
event.put("data", data)
trigger("newIntent", event)
}
}
@Command
fun openCamera(invoke: Invoke) {
val payload = JSObject()
payload.put("open", true)
trigger("camera", payload)
}
}

辅助函数可以通过使用 addPluginListener 函数在 NPM 包中找到并使用。

import { addPluginListener, PluginListener } from '@tauri-apps/api/tauri';
export async function onRequest(
handler: (url: string) => void
): Promise<PluginListener> {
return await addPluginListener(
'<plugin-name>',
'event-name',
handler
);
}

© 2024 Tauri Contributors. CC-BY / MIT