完整流程 Flutter 集成 Rust 多语言跨端开发基础案例

1,500 阅读1分钟

使用Rust一处编写,横跨中Android & IOS 多端场景使用,相比较 React Native 方案更加高效。并且Rust是一门系统级编程,Rust编写也可以在其它场景下复用。

本文主要介绍了Flutter 集成Rust,所以并不会在Flutter、Rust等开发环境搭建以及Dart、Rust语言上做介绍。

考虑Android & IOS 同时使用,所以本文使用MacOS系统作为演示,如仅仅需做安卓上测试可跳过相关IOS的配置等操作。

本地环境

Flutter 版本

Flutter版本

Rust 版本


rustc 1.61.0 (fe5b13d68 2022-05-18)

cargo 1.61.0 (a028ae42f 2022-04-29)

NDK 版本


可通过 Android Studio 安装 NDK 版本 22.1.7171670

测过着23版本之后cargo 构建编译会出问题

配置 NDK 环境变量

此处使用VSCode编辑器编辑

ANDROID_NDK_HOME 指定 NDK 安装目录

code ~/.zshrc

export ANDROID_NDK_HOME=/Users/username/Library/Android/sdk/ndk/22.1.7171670

source ~/.zshrc

生成交叉编译工具

mkdir ~/.NDK

python3 $ANDROID_NDK_HOME/build/tools/make_standalone_toolchain.py --api 21 --arch arm64 --install-dir ~/.NDK/arm64

python3 $ANDROID_NDK_HOME/build/tools/make_standalone_toolchain.py --api 16 --arch arm --install-dir ~/.NDK/arm

python3 $ANDROID_NDK_HOME/build/tools/make_standalone_toolchain.py --api 16 --arch x86 --install-dir ~/.NDK/x86

配置Rust交叉编译工具

code ~/.cargo/config

[target.aarch64-linux-android]
ar = ".NDK/arm64/bin/aarch64-linux-android-ar"
linker = ".NDK/arm64/bin/aarch64-linux-android-clang"

[target.armv7-linux-androideabi]
ar = ".NDK/arm/bin/arm-linux-androideabi-ar"
linker = ".NDK/arm/bin/arm-linux-androideabi-clang"

[target.i686-linux-android]
ar = ".NDK/x86/bin/i686-linux-android-ar"
linker = ".NDK/x86/bin/i686-linux-android-clang"

下载Rust 交叉编译的依赖

Android

rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android

IOS

rustup target add aarch64-apple-ios x86_64-apple-ios

安装cargo-lipo以生成iOS通用库

cargo install cargo-lipo

Rust 项目相关

创建项目

cargo init my-app-base --lib

编写功能

使用任意编辑器打开项目,编辑src中lib.rs文件,添加如下代码,其中定义了一个hello输出world字符以及count_add_self每次调用加2

use std::os::raw::c_char;
use std::ffi::CString;

static mut COUNT: u32 = 0;

#[no_mangle]
pub unsafe extern "C" fn count_add_self() -> u32 {
    COUNT += 2;
    COUNT
}

#[no_mangle]
pub unsafe extern fn hello() -> *const c_char {
    let s = CString::new("world").unwrap();
    s.into_raw()
}

修改配置

修改 Cargo.toml 文件增加如下内容

Rust 构建出来的二进制库

在 IOS 中是静态链接进最终的程序之中,需要对构建 staticlib 的支持

在 Android 是通过动态链接在运行时装在进程序运行空间的,需要对构建 cdylib 的支持

[lib]
name = "hello_lib"
crate-type = ["staticlib", "cdylib"]

编译Rust得到静态库

# IOS
cargo lipo --release
# Android
cargo build --target aarch64-linux-android --release     
cargo build --target armv7-linux-androideabi --release     
cargo build --target i686-linux-android --release

Flutter 项目相关

使用Android Studio、VSCode或者终端创建一个Flutter项目

IOS 引入Rust静态库

 1. 在Flutter项目ios目录下新建Framework文件夹

 2. 将Rust项目中target/universal/release/libhello_lib.a 复制到 Framework 文件夹

复制静态库

 3. 在 ios 目录上右键使用 xcode 打开项目

 4. 在 Build Phases 中 Link Binary With Libraries 添加 Framework文件夹中 libhello_lib.a 文件

xcode添加静态库

 5. 在 Build Settings 中 Other Linker Flags 中添加 -all_load 的参数

xcode配置参数

 6. 在 Build Settings 中 Architectures 中 Excluded Architectures 添加 arm64 的参数,默认应该有一个 i386 因为Rust编译后的是 arm64 架构

添加arm64架构

Android 引入Rust静态库

在Flutter项目android/app/src/main目录下新建jniLibs目录,并在新建 arm64-v8a 、 armeabi-v7a 、x86 三个文件夹

添加目录

将 Rust 项目编译后 target 项目下对应架构so文件复制到 对应 架构文件夹下

Rust中so静态库目录

Flutter中so静态库目录

添加FFi依赖

在flutter项目pubspec.yaml中添加FFI

ffi: ^2.0.0

FFI

测试调用

在 main.dart 中修改后

import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'dart:io';

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  String? hello;

  int count = 0;

  void _incrementCounter() {
    setState(() {
      countAdd();
    });
  }

  @override
  void initState() {
    super.initState();
    final dylib = Platform.isAndroid ? DynamicLibrary.open('libhello_lib.so') :DynamicLibrary.process();
    var result = dylib.lookupFunction<Pointer<Utf8> Function(),Pointer<Utf8> Function()>('hello');
    hello = result().toDartString();
  }

  void countAdd() {
    final dylib = Platform.isAndroid ? DynamicLibrary.open('libhello_lib.so') :DynamicLibrary.process();
    var  countAddSelf = dylib.lookupFunction<Pointer<Uint32> Function(),Pointer<Uint32> Function()>('count_add_self');
    count = countAddSelf().address;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              '以下内容为Rust侧结果',
            ),
            Text('$hello'),
            Text('$count'),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

测试结果图:

测试结果