0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Tauri Android】Kotlinで実装したネイティブ機能と連携する

Last updated at Posted at 2024-11-21

はじめに

TauriでAndroidアプリを開発する際、フロントエンドはWebViewベースで構築されています。そのため、OS固有のネイティブ機能にアクセスするには、まずKotlinでその機能を実装し、TypeScript、Rust、Kotlin間の橋渡し(ブリッジ)を構築する必要があります。
ただし、このブリッジの仕組みはTauriフレームワークによって提供されているため、開発者は用意された手順に従って実装するだけで、言語間の連携を実現できます。

この記事では、フロントエンドからKotlinで書かれたネイティブ機能との連携方法を紹介します。

プラグインの作成

今のところ、ネイティブ機能と連携するためには、プラグインを作成する必要があります1

プラグインプロジェクトの生成

Tauriアプリのプロジェクトのルートディレクトリで次のコマンドを打ちます。 [name] は任意のプラグイン名です。

npx @tauri-apps/cli plugin new [name] --android --ios

--ios は、iOSをサポートしない場合は不要です。

コマンドに成功すると、ルートディレクトリ直下にプラグイン用のプロジェクトが生成されます。プラグイン名には自動的に tauri-plugin- というプレフィックスが付きます。

これ以降、説明のためにプラグイン名をtauri-plugin-exampleとします。

アプリのディレクトリ構成は以下のようになります。

[tauri-app]/  <- アプリのルートディレクトリ
└ index.html
└ public/
└ src/
└ src-tauri/
└ package.json
└ ...
└ tauri-plugin-example/  <- プラグイン用のプロジェクトが生成される

プラグインの追加

上記で生成したプラグインをアプリの依存関係に追加します。

Cargo.toml

src-tauri/Cargo.toml
[dependencies]
+ tauri-plugin-example = { path = "../tauri-plugin-example/" }

package.json

package.json
{
  "dependencies": {
+   "tauri-plugin-example-api": "file:./tauri-plugin-example"
  }
}

lib.rs

src-tauri/src/lib.rs にプラグインの初期化コードを追加します。

src-tauri/src/lib.rs
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
+       .plugin(tauri_plugin_example::init())
        ...
}

パーミッションの設定

アプリのパーミッションに追加したプラグインを設定します。パーミッションは default にします。なお、プラグイン名に tauri-plugin- プレフィックスは不要です。

src-tauri/capabilities/default.json
{
  "permissions": [
    "core:default",
    "shell:allow-open"
+   "example:default"
  ]
}

プラグインの実装

この記事では、例として、フロントエンドからメッセージを受け取ってToastを表示するコマンドを実装します。

ExamplePlugin.kt

tauri-plugin-example/android/src/main/java/ExamplePlugin.kt に下記のような showToast メソッドを実装します。メソッドには @Command アノテーションを付けます。 ShowToastArgs クラスはフロントエンドから渡されるデータの型を表します。

tauri-plugin-example/android/src/main/java/ExamplePlugin.kt
@InvokeArg
class ShowToastArgs {
    var message: String? = null

    /**
     * "short" or "long"
     */
    var length: String? = null
}

@TauriPlugin
class ExamplePlugin(private val activity: Activity) : Plugin(activity) {
    @Command
    fun showToast(invoke: Invoke) {
        val args = invoke.parseArgs(ShowToastArgs::class.java)
        args.message?.let {
            val length = when (args.length) {
                "short" -> Toast.LENGTH_SHORT
                "long" -> Toast.LENGTH_LONG
                else -> Toast.LENGTH_SHORT
            }
            Toast.makeText(activity, it, length).show()
        }

        invoke.resolve()
    }
}

コマンドが正常に終了したときは invoke.resolve() メソッドを呼びます。

  • フロントエンドにデータを返したいときは resolve メソッドの引数に JSObject を渡します
  • コマンドが失敗したときは、 resolve メソッドの代わりに reject メソッドを呼ぶことでエラーを返せます

ExamplePlugin コンストラクタの引数には Activity が渡されるため、大抵のことはできそうです。

models.rs

Kotlinで書いた ShowToastArgs クラスに対応する形でモデルを定義します。変数名はスネークケースで書きます。

tauri-plugin-example/src/models.rs
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ShowToastRequest {
    pub message: Option<String>,
    pub length: Option<String>,
}

フロントエンドにデータを返す場合、Responseのモデルもこのファイルに定義します。

mobile.rs

モバイルプラットフォーム向けに show_toast メソッドを実装します。 run_mobile_plugin の第1引数は、Kotlinで実装したコマンドのメソッド名です。

tauri-plugin-example/src/mobile.rs
impl<R: Runtime> Example<R> {
    pub fn show_toast(&self, payload: ShowToastRequest) -> crate::Result<()> {
        self.0
            .run_mobile_plugin("showToast", payload)
            .map_err(Into::into)
    }
}

desktop.rs

デスクトップ向けの show_toast メソッドですが、今回、デスクトップアプリは対象外のため、メソッドだけ用意し中身は何も実装しません。

tauri-plugin-example/src/desktop.rs
impl<R: Runtime> Example<R> {
    pub fn show_toast(&self, payload: ShowToastRequest) -> crate::Result<()> {
        // Nothing to do
        Ok(())
    }
}

commands.rs

tauri-plugin-example/src/commands.rsshow_toast 関数を書きます。

tauri-plugin-example/src/commands.rs
#[command]
pub(crate) async fn show_toast<R: Runtime>(
    app: AppHandle<R>,
    payload: ShowToastRequest,
) -> Result<()> {
    app.example().show_toast(payload)
}

lib.rs

tauri-plugin-example/src/lib.rsBuilder において、上記commands.rsで書いた show_toast を追加します。

tauri-plugin-example/src/lib.rs
Builder::new("example")
    .invoke_handler(tauri::generate_handler![
        commands::ping,
+       commands::show_toast
    ])

build.rs

tauri-plugin-example/build.rsCOMMANDSshow_toast を追加します。

tauri-plugin-example/build.rs
const COMMANDS: &[&str] = &[
    "ping",
+   "show_toast",
];

default.toml

tauri-plugin-example/permissions/default.tomlpermissionsallow-show-toast を追加します。

tauri-plugin-example/permissions/default.toml
[default]
description = "Default permissions for the plugin"
permissions = ["allow-ping", "allow-show-toast"]

index.ts

下記のような showToast 関数を書きます。 payload にセットするオブジェクトは、上記models.rsで書いた ShowToastRequest に対応します。キー名はキャメルケースで書きます。

tauri-plugin-example/guest-js/index.ts
export async function showToast(
  message: string,
  length?: string,
): Promise<void> {
  return await invoke("plugin:example|show_toast", {
    payload: {
      message,
      length: length || "short",
    },
  });
}

tauri-plugin-example/guest-js/index.ts に書いたexport文は、フロントエンドでimportすることができます。

import { showToast } from "tauri-plugin-example-api";

プラグインのビルド

最後にビルドすることを忘れないようにしてください。

cd tauri-plugin-example
npm run build

プラグインを使う

上記のindex.tsでエクスポートした showToast 関数をインポートして使えます。下記は使用例です。

App.tsx
import { showToast } from "tauri-plugin-example-api";

...

function App() {
  ...

  // ボタンが押されたら、Toastで"Hello!"と表示する
  function handleClick() {
    showToast("Hello!").then(() => {
      // コマンドの実行に成功したとき(resolve)
    }).catch(error => {
      // コマンドの実行に失敗したとき(reject)
    });
  }

  ...
}

export default App;

おわりに

フロントエンドからKotlinで書かれたネイティブ機能との連携方法を紹介しました。
実装するにあたり手数が多い印象ですが、Tauriの今後のバージョンアップで改善されることを期待しています。

参考

Develop a Tauri plugin for Android

  1. https://v2.tauri.app/blog/tauri-20/#mobile-support

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?