Flutter 调用 Rust 生成的 共享/静态 库
标签搜索

Flutter 调用 Rust 生成的 共享/静态 库

limit
2022-05-30 / 0 评论 / 15 阅读 / 正在检测是否收录...

日常发病

前面写过一两篇使用水文, 用 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 可能会出现找不到符号的问题), 执行到对应的设备(或者仿真器), 就能看到对应的效果

iOS 上的效果

Flutter FFI 使用起来比较简单, 某些情况也很有用, 譬如要使用 FFmpeg 理论上可以通过 FFI 来达成我们的目标.

0

评论 (0)

取消