日常发病
前面写过一两篇使用水文, 用 Rust 写点东西, 编译成 shared/static lib 给 Android 或者 iOS 端调用.
这篇水文需要用到之前写得 MD5 的那个 Rust 项目.
Android 使用 Rust 生成的动态库
用 Rust 开发 iOS 应用(粗糙版)
今天发病的主题是 Flutter FFI 相关.
创建 Flutter 项目
这里直接通过下面这条命令创建
flutter create --platforms=android,ios --template=plugin ffi_demo
复制代码
这里使用的是 plugin 的模板, 如果直接使用 project 模板也是可以的, 现在直接用 Android Studio 打开项目. 我们先来看看项目结构
tree -L 1
.
├── CHANGELOG.md
├── LICENSE
├── README.md
├── analysis_options.yaml
├── android
├── example
├── ffi_demo.iml
├── ios
├── lib
├── pubspec.lock
├── pubspec.yaml
└── test
5 directories, 7 files
复制代码
处理 Android 端
Android 端的比较好处理, 把开头 Android 的那篇文章的 md5 项目生成的动态库拷贝出来, 放到 android/src/main/jniLibs/arm64-v8a 下面, 事实上这里不需要用到 CMakeLists, 所以直接放到 jniLibs 下面, 不过有个问题, Android 项目并不知道你放这边了, 所以还得配置一下 build.gradle 的 sourceSets
android {
// ...
sourceSets {
// ...
main.jniLibs.srcDirs = ['src/main/jniLibs']
}
// ...
}
复制代码
这一步完成后, 我们先把 Android 这边的事放一放
处理 iOS 端
由于之前的 md5 那个项目, 没编译 iOS 的静态库, 所以我们先得把 iOS 静态库生成一下. 喜闻乐见的添加对应 target 环节
rustup target add aarch64-apple-ios
复制代码
如果你想跑在 Intel 设备的 iOS 仿真器上, 需要添加 x86 的 target, 这里只添加了手机 aarch64 的, 如果你想跑在 M1 设备的 iOS 仿真器上, 你需要添加
rustup target add aarch64-apple-ios-sim
复制代码
接着跑到 md5 的项目中运行构造命令
cargo build --target aarch64-apple-ios --release
复制代码
构建成功后把 target 目录对应平台的静态库拷贝出来放到 ios/Frameworks 下, 没有 Frameworks 文件夹就自己创建, 文件夹名字随便起, 然后把 ffi_demo.podspec 文件改一下, 总之找到对应的 .podspec 文件, 添加指定静态库的路径
Pod::Spec.new do |s|
# ...
s.vendored_libraries = 'Frameworks/libmd5.a'
# ...
end
复制代码
处理 Flutter 端
上面的准备工作完成后, 我们可以来暴露接口给 Dart 使用啦! 找到 lib/ffi_demo.dart 文件, 修改里面的代码
import 'dart:io';
import 'dart:ffi';
// third part package
import 'package:ffi/ffi.dart';
typedef LLMD5 = Pointer<Int8> Function(Pointer<Int8>);
class FfiDemo {
String md5(String str) {
final DynamicLibrary nativeLib = Platform.isAndroid
? DynamicLibrary.open("libmd5.so")
: DynamicLibrary.process();
LLMD5 md5 = nativeLib.lookupFunction<LLMD5, LLMD5>("ll_md5");
var result = md5(str.toNativeUtf8().cast<Int8>());
return result.cast<Utf8>().toDartString();
}
}
复制代码
其实就是把 dart 的字符串转成 C 的字符串传给 md5 函数使用, 返回的结果再转成 dart 的字符串, 我们代码中用到一个第三方包来处理 dart 字符串跟 C 字符串的转换, 直接执行添加第三方包的命令
flutter pub add ffi
复制代码
执行 example
Android
为了验证我们的插件是否正常工作, 我们把 example 里面的 lib/main.dart 修改一下
import 'package:flutter/material.dart';
import 'package:ffi_demo/ffi_demo.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _md5Str = '';
final _ffiDemoPlugin = FfiDemo();
@override
void initState() {
super.initState();
initMD5();
}
void initMD5() {
String str = '';
try {
str = _ffiDemoPlugin.md5("foo");
} on Exception {
str = 'MD5 failed';
}
if (!mounted) return;
setState(() {
_md5Str = str;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: Text('$_md5Str\n'),
),
),
);
}
}
复制代码
然后连接对应 ABI 的 Android 手机, 用 Android Studio 执行一下, 可以看到屏幕中间显示了一串经过 md5 运算后的字符串.
iOS
iOS 这边, 因为我们修改了 podspec 文件, 所以我们需要在 example/ios 目录下执行
pod install
复制代码
然后就是更新 xcworkspace, 完成后, 就可以在 Pod 项目中找到我们的静态库了 然后用 Android Studio 打开插件项目(直接用 Xcode 执行 Runner 可能会出现找不到符号的问题), 执行到对应的设备(或者仿真器), 就能看到对应的效果
Flutter FFI 使用起来比较简单, 某些情况也很有用, 譬如要使用 FFmpeg 理论上可以通过 FFI 来达成我们的目标.