Android版のFlutterからRustの関数を呼び出す最低限のメモ。 それらしい記事はいくつかあるものの、理解が出来てないのかバージョンの違いからか中々上手く行かなかったので。 Fluttter, Android, Rust等々の作法として正しいかどうかは正直分からないし、少し前まではDartから直接FFIが扱えなかったのでJNI経由だったようだ。
以下参考にしたページ。
- Rust once and share it with Android, iOS and Flutter | Roberto Huertas
- Using FFI on Flutter Plugins to run native Rust code
- Binding to native code using dart:ffi - Flutter
- Flutterプラグインでdart:ffiを使ってみる - Speaker Deck
環境
ツールバージョン
インストール
Flutter
普通にインストール。
Rust
rustupをインストールしrustのandroid関連パッケージをインストールする。
curl https://sh.rustup.rs -sSf | sh rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android cargo install cargo-ndk
新規Flutterプロジェクト作成
- Start a New Flutter project選択
- Flutter Application選択
- New Flutter Application
- Project name :
flutter_dart_ffi_rust
- Flutter SDK path :
/home/username/.local/flutter
- Project location :
/home/username/AndroidStudioProjects/flutter_dart_ffi_rust
- Description :
Flutter Dart FFI Rust
- Package name :
com.example.flutterdartffirust
- AndroidX :
on
- Platform channel language
- Project name :
新規Rustライブラリ作成
Flutterプロジェクトの中ににRustのライブラリを作成する。
cd flutter_dart_ffi_rust && mkdir rust && cd rust cargo init --name rust_sample --lib
src/lib.rsを書き換えてC ABIの関数echoを作る。
use std::ffi::{CStr, CString}; use std::os::raw::c_char; #[no_mangle] pub unsafe extern "C" fn echo(to: *const c_char) -> *mut c_char { let c_str = CStr::from_ptr(to); let s = match c_str.to_str() { Ok(s) => s, Err(_) => "Err", }; CString::new(format!("From Rust {}", s)).unwrap().into_raw() }
rust/build.sh
ビルドスクリプト。
ローカルのシミュレータやAndroid実機等で動かすため複数のプロセッサ向けにコンパイルする。
export ANDROID_NDK_HOME=$HOME/Android/Sdk/ndk/21.0.6113669 for target in aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android do cargo ndk --target ${target} --android-platform 22 -- build --release done
rust/install.sh
インストールスクリプト。
最終的な実行時に、共有ライブラリが参照できる場所は具体的にどこなのか作法としてよく分からないが、JNIと同様Flutterの android/app/src/main/jniLibs/
以下に置いて動いた。
INSTALL_PATH=../android/app/src/main/jniLibs INSTALL_FILE=librust_sample.so mkdir -p ${INSTALL_PATH}/arm64-v8a mkdir -p ${INSTALL_PATH}/armeabi-v7a mkdir -p ${INSTALL_PATH}/x86 mkdir -p ${INSTALL_PATH}/x86_64 cp target/aarch64-linux-android/release/${INSTALL_FILE} ${INSTALL_PATH}/arm64-v8a/ cp target/armv7-linux-androideabi/release/${INSTALL_FILE} ${INSTALL_PATH}/armeabi-v7a/ cp target/i686-linux-android/release/${INSTALL_FILE} ${INSTALL_PATH}/x86/ cp target/x86_64-linux-android/release/${INSTALL_FILE} ${INSTALL_PATH}/x86_64/
Flutter側修正
pubspec.yaml
の dependencies:
にffiを追加する。
ffi: ^0.1.3
lib/main.dart
からRustのecho関数を呼び出す。
ffiのインポートを追加し、dll読み込み、呼び出す関数の型定義、echo関数の生成(?)を書く。
import 'dart:ffi'; import 'package:flutter/material.dart'; import 'package:ffi/ffi.dart'; final dll = DynamicLibrary.open('librust_sample.so'); typedef Echo = Pointer<Utf8> Function(Pointer<Utf8> to); final echo = dll.lookup<NativeFunction<Echo>>('echo').asFunction<Echo>(); void main() => runApp(MyApp()); // 略
lib/main.dart
の 'Flutter Demo Home Page'
を関数呼び出しで置き換える。
// 略 home: MyHomePage(title: Utf8.fromUtf8(echo(Utf8.toUtf8('dummy')))), // 略
実行
Zenfone6上で実行出来た。