Skip to content
Tauri 中文网

移动插件开发

插件可以运行用 Kotlin(或 Java)和 Swift 编写的原生移动代码。默认的插件模板包括一个使用 Kotlin 的 Android 库项目和一个 Swift 包,其中包含一个示例移动命令,展示如何从 Rust 代码触发其执行。

🌐 Plugins can run native mobile code written in Kotlin (or Java) and Swift. The default plugin template includes an Android library project using Kotlin and a Swift package including an example mobile command showing how to trigger its execution from Rust code.

🌐 Initialize Plugin Project

按照 插件开发指南 中的步骤来初始化一个新的插件项目。

🌐 Follow the steps in the Plugin Development guide to initialize a new plugin project.

如果你有一个现有的插件,并希望为其添加 Android 或 iOS 功能,你可以使用 plugin android initplugin ios init 来引导移动库项目,并指导你完成所需的更改。

🌐 If you have an existing plugin and would like to add Android or iOS capabilities to it, you can use plugin android init and plugin ios init to bootstrap the mobile library projects and guide you through the changes needed.

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

🌐 The default plugin template splits the plugin’s implementation into two separate modules: desktop.rs and mobile.rs.

桌面端实现使用 Rust 代码来实现功能,而移动端实现则向本地移动代码发送消息以执行函数并获取结果。如果两个实现都需要共享逻辑,可以在 lib.rs 中定义:

🌐 The desktop implementation uses Rust code to implement a functionality, while the mobile implementation sends a message to the native mobile code to execute a function and get a result back. If shared logic is needed across both implementations, it can be defined in lib.rs:

src/lib.rs
use tauri::Runtime;
impl<R: Runtime> <plugin-name><R> {
pub fn do_something(&self) {
// do something that is a shared implementation between desktop and mobile
}
}

此实现简化了共享 API 的过程,该 API 可由命令和 Rust 代码使用。

🌐 This implementation simplifies the process of sharing an API that can be used both by commands and Rust code.

🌐 Develop an Android Plugin

Android 的 Tauri 插件被定义为一个继承自 app.tauri.plugin.Plugin 并使用 app.tauri.annotation.TauriPlugin 注解的 Kotlin 类。每个使用 app.tauri.annotation.Command 注解的方法都可以被 Rust 或 JavaScript 调用。

🌐 A Tauri plugin for Android is defined as a Kotlin class that extends app.tauri.plugin.Plugin and is annotated with app.tauri.annotation.TauriPlugin. Each method annotated with app.tauri.annotation.Command can be called by Rust or JavaScript.

Tauri 默认使用 Kotlin 来实现 Android 插件,但如果你愿意,也可以切换到 Java。在生成插件后,在 Android Studio 中右键点击 Kotlin 插件类,并从菜单中选择“将 Kotlin 文件转换为 Java 文件”选项。Android Studio 将引导你完成项目向 Java 的迁移。

🌐 Tauri uses Kotlin by default for the Android plugin implementation, but you can switch to Java if you prefer. After generating a plugin, right click the Kotlin plugin class in Android Studio and select the “Convert Kotlin file to Java file” option from the menu. Android Studio will guide you through the project migration to Java.

🌐 Develop an iOS Plugin

iOS 的 Tauri 插件被定义为一个 Swift 类,该类继承自 Tauri 包中的 Plugin 类。每个带有 @objc 属性和 (_ invoke: Invoke) 参数(例如 @objc private func download(_ invoke: Invoke) { })的函数都可以被 Rust 或 JavaScript 调用。

🌐 A Tauri plugin for iOS is defined as a Swift class that extends the Plugin class from the Tauri package. Each function with the @objc attribute and the (_ invoke: Invoke) parameter (for example @objc private func download(_ invoke: Invoke) { }) can be called by Rust or JavaScript.

该插件被定义为一个 Swift 包,这样你就可以使用其包管理器来管理依赖。

🌐 The plugin is defined as a Swift package so that you can use its package manager to manage dependencies.

🌐 Plugin Configuration

有关开发插件配置的更多详细信息,请参阅《插件开发指南》的插件配置部分

🌐 Refer to the Plugin Configuration section of the Plugin Development guide for more details on developing plugin configurations.

移动设备上的插件实例有一个用于插件配置的 getter:

🌐 The plugin instance on mobile has a getter for the plugin configuration:

import android.app.Activity
import android.webkit.WebView
import app.tauri.annotation.TauriPlugin
import app.tauri.annotation.InvokeArg
@InvokeArg
class Config {
var timeout: Int? = 3000
}
@TauriPlugin
class ExamplePlugin(private val activity: Activity): Plugin(activity) {
private var timeout: Int? = 3000
override fun load(webView: WebView) {
getConfig(Config::class.java).let {
this.timeout = it.timeout
}
}
}

🌐 Lifecycle Events

插件可以挂接到多个生命周期事件:

🌐 Plugins can hook into several lifecycle events:

  • 加载:当插件被加载到网页视图中时
  • onNewIntent:仅限 Android,当活动被重新启动时

在插件开发指南中,还有插件的额外lifecycle events

🌐 There are also the additional lifecycle events for plugins in the Plugin Development guide.

  • 时间:当插件被加载到网页视图中时
  • 为什么:执行插件初始化代码
import android.app.Activity
import android.webkit.WebView
import app.tauri.annotation.TauriPlugin
@TauriPlugin
class ExamplePlugin(private val activity: Activity): Plugin(activity) {
override fun load(webView: WebView) {
// perform plugin setup here
}
}

注意:此功能仅在安卓上可用。

  • 时间:当活动重新启动时。更多信息请参见 Activity#onNewIntent
  • 原因:处理应用重新启动,例如当点击通知或访问深层链接时。
import android.app.Activity
import android.content.Intent
import app.tauri.annotation.TauriPlugin
@TauriPlugin
class ExamplePlugin(private val activity: Activity): Plugin(activity) {
override fun onNewIntent(intent: Intent) {
// handle new intent event
}
}

🌐 Adding Mobile Commands

在相应的移动项目内部有一个插件类,可以在其中定义可由 Rust 代码调用的命令:

🌐 There is a plugin class inside the respective mobile projects where commands can be defined that can be called by the Rust code:

import android.app.Activity
import app.tauri.annotation.Command
import app.tauri.annotation.TauriPlugin
@TauriPlugin
class ExamplePlugin(private val activity: Activity): Plugin(activity) {
@Command
fun openCamera(invoke: Invoke) {
val ret = JSObject()
ret.put("path", "/path/to/photo.jpg")
invoke.resolve(ret)
}
}

如果你想使用 Kotlin 的 suspend 函数,你需要使用自定义协程作用域

🌐 If you want to use a Kotlin suspend function, you need to use a custom coroutine scope

import android.app.Activity
import app.tauri.annotation.Command
import app.tauri.annotation.TauriPlugin
// Change to Dispatchers.IO if it is intended for fetching data
val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
@TauriPlugin
class ExamplePlugin(private val activity: Activity): Plugin(activity) {
@Command
fun openCamera(invoke: Invoke) {
scope.launch {
openCameraInner(invoke)
}
}
private suspend fun openCameraInner(invoke: Invoke) {
val ret = JSObject()
ret.put("path", "/path/to/photo.jpg")
invoke.resolve(ret)
}
}

使用 tauri::plugin::PluginHandle 从 Rust 调用移动命令:

🌐 Use the tauri::plugin::PluginHandle to call a mobile command from 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)
}
}

🌐 Command Arguments

参数被序列化为命令,并可以在移动插件上使用 Invoke::parseArgs 函数解析,该函数接受描述参数对象的类。

🌐 Arguments are serialized to commands and can be parsed on the mobile plugin with the Invoke::parseArgs function, taking a class describing the argument object.

在 Android 上,参数被定义为一个用 @app.tauri.annotation.InvokeArg 注解的类。内部对象也必须被注解:

🌐 On Android, the arguments are defined as a class annotated with @app.tauri.annotation.InvokeArg. Inner objects must also be annotated:

import android.app.Activity
import android.webkit.WebView
import app.tauri.annotation.Command
import app.tauri.annotation.InvokeArg
import app.tauri.annotation.TauriPlugin
@InvokeArg
internal class OpenAppArgs {
lateinit var name: String
var timeout: Int? = null
}
@InvokeArg
internal class OpenArgs {
lateinit var requiredArg: String
var allowEdit: Boolean = false
var quality: Int = 100
var app: OpenAppArgs? = null
}
@TauriPlugin
class ExamplePlugin(private val activity: Activity): Plugin(activity) {
@Command
fun openCamera(invoke: Invoke) {
val args = invoke.parseArgs(OpenArgs::class.java)
}
}

Tauri 中文网 - 粤ICP备13048890号
Nodejs.cn 旗下网站