Flutter调用Rust代码操作指南

这篇具有很好参考价值的文章主要介绍了Flutter调用Rust代码操作指南。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Flutter调用Rust代码操作指南

在之前的利用Rust与Flutter开发一款小工具文章中,我们使用Rust代码实现了一个简单的WebSocket发送功能。也在Rust库交叉编译以及在Android与iOS使用这篇中介绍了Rust库的打包以及双端的使用。

今天我们继续用之前WebSocket的代码举例,来介绍如何在Flutter项目中使用。

准备工作

本篇的主角就是flutter_rust_bridge,它是用于 FlutterRust 的高级内存安全绑定生成器。这个库只是一个代码生成器,帮助你的 Flutter / Dart 调用 Rust 函数。它只是生成了一些模板代码,代替了手工编写。

首先我们可以把Rust代码放到Flutter项目的根目录中,或者运行 cargo new --lib创建一个新的 Rust crate。完成后项目结构如下:

├── android
├── ios
├── lib
├── linux
├── macos
├── $crate
│   ├── Cargo.toml
│   └── src
├── test
├── web
└── windows

注意:将 crate 的根目录设为和其他项目同等级别,这样有助于简化配置过程。

稍微修改一下之前的rust代码(注意代码不要直接写在lib.rs中,不然生成文件无法获取导包):

这里我们将之前的代码放入api.rs中:

use std::collections::HashMap;
use std::sync::Mutex;
use ws::{connect, Handler, Sender, Handshake, Result, Message, CloseCode, Error};
use ws::util::Token;


lazy_static! {
    static ref DATA_MAP: Mutex<HashMap<String, Sender>> = {
        let map: HashMap<String, Sender> = HashMap::new();
        Mutex::new(map)
    };
}

struct Client {
    sender: Sender,
    host: String,
}

impl Handler for Client {
    fn on_open(&mut self, _: Handshake) -> Result<()> {
        DATA_MAP.lock().unwrap().insert(self.host.to_owned(), self.sender.to_owned());
        Ok(())
    }

    fn on_message(&mut self, msg: Message) -> Result<()> {
        println!("<receive> '{}'. ", msg);
        Ok(())
    }

    fn on_close(&mut self, _code: CloseCode, _reasonn: &str) {
        DATA_MAP.lock().unwrap().remove(&self.host);
    }

    fn on_timeout(&mut self, _event: Token) -> Result<()> {
        DATA_MAP.lock().unwrap().remove(&self.host);
        self.sender.shutdown().expect("shutdown error");
        Ok(())
    }

    fn on_error(&mut self, _err: Error) {
        DATA_MAP.lock().unwrap().remove(&self.host);
    }

    fn on_shutdown(&mut self) {
        DATA_MAP.lock().unwrap().remove(&self.host);
    }

}

pub fn websocket_connect(host: String) {
    if let Err(err) = connect(host.to_owned(), |out| {
        Client {
            sender: out,
            host: host.to_owned(),
        }
    }) {
        println!("Failed to create WebSocket due to: {:?}", err);
    }
}

pub fn send_message(host: String, message: String) {
    let binding = DATA_MAP.lock().unwrap();
    let sender = binding.get(&host.to_owned());
    
    match sender {
        Some(s) => {
            if s.send(message).is_err() {
                println!("Websocket couldn't queue an initial message.")
            };
        } ,
        None => println!("None")
    }
}

pub fn websocket_disconnect(host: String) {
    DATA_MAP.lock().unwrap().remove(&host.to_owned());
}

api.rs

mod api;
#[macro_use]
extern crate lazy_static;

Cargo.toml配置如下:

[package]
name = "rust_demo"
version = "0.1.0"
edition = "2021"
publish = false

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

[profile.release]
lto = true
opt-level = 'z'
strip = true
codegen-units = 1
# panic = 'abort'

[dependencies]
ws = "0.9.2"
lazy_static = "1.4.0"
flutter_rust_bridge = "=1.77.1"
flutter_rust_bridge_macros = "=1.77.1"

[build-dependencies]
flutter_rust_bridge_codegen = "=1.77.1"

Flutter中pubspec.yaml的配置如下:

dependencies:
  flutter_rust_bridge: 1.77.1
  ffi: ^2.0.1

dev_dependencies:
  ffigen: ^8.0.2

这里注意flutter_rust_bridge的版本需要一致。我这里目前使用的是1.77.1。然后在rust项目中执行:

cargo install flutter_rust_bridge_codegen
# 如果为iOS或MacOS应用构建
cargo install cargo-xcode
  • flutter_rust_bridge_codegen, 生成 Rust-Dart 胶水代码的核心。
  • ffigen, 从 C 头文件中生成 Dart 代码/
  • 安装 LLVM, 请看 Installing LLVM,ffigen 会使用到。
  • (可选) cargo-xcode,如果你想生成为 IOS 和 MacOS 的 Xcode 项目。

完成上面的准备工作,我们就可以在Flutter项目下执行命令,成功胶水代码了。

flutter_rust_bridge_codegen -r native/src/api.rs -d lib/ffi/rust_ffi.dart -c ios/Runner/bridge_generated.h
  • native/src/api.rs rust代码路径。
  • lib/ffi/rust_ffi.dart生成dart代码路径。
  • ios/Runner/bridge_generated.h创建的一个 C 头文件,里面列出了 Rust 库导出的所有符号,我们需要使用它确保 Xcode 不会将符号去除。

Android配置

首先安装cargo-ndk,它能够将代码编译到适合的 JNI 而不需要额外的配置。我们之前的文章中,就通过手动的方式在.cargo/config中配置clang链接器的路径。就比较繁琐,这个插件就是简化这一操作的。

安装命令:

// ndk低于22
cargo install cargo-ndk --version 2.6.0
// ndk高于22
cargo install cargo-ndk

交叉编译到安卓需要一些额外的组件,这个我们之前的文章也有说明:

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

接着,在 android/app/build.gradle 的最后添加下面几行:

[
        Debug: null,
        Profile: '--release',
        Release: '--release'
].each {
    def taskPostfix = it.key
    def profileMode = it.value
    tasks.whenTaskAdded { task ->
        if (task.name == "javaPreCompile$taskPostfix") {
            task.dependsOn "cargoBuild$taskPostfix"
        }
    }
    tasks.register("cargoBuild$taskPostfix", Exec) {
        workingDir "../../native"
        environment ANDROID_NDK_HOME: "$ANDROID_NDK"
        commandLine 'cargo', 'ndk',
                // the 2 ABIs below are used by real Android devices
                '-t', 'armeabi-v7a',
                '-t', 'arm64-v8a',
                '-o', '../android/app/src/main/jniLibs', 'build'
        if (profileMode != null) {
            args profileMode
        }
    }
}
  • ../../native就是rust代码路径。
  • ANDROID_NDK就是在android/gradle.properties配置的NDK路径。
  • Android每次运行都会打包rust代码并将so文件放入android/app/src/main/jniLibs下。所以如果Rust代码没有变化修改,可以在生成release文件后注释掉此处代码。
ANDROID_NDK=/Users/weilu/android/android-sdk-macosx/ndk/21.4.7075529

iOS配置

安装交叉编译组件:

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

接着在rust项目目录下执行cargo xcode。执行后,会生成一个xcodeproj后缀文件夹。它可以用于导入到其他 Xcode 项目中。
Flutter调用Rust代码操作指南
在 Xcode 中打开 ios/Runner.xcodeproj, 点击菜单File ---> Add Files to "Runner"接着把 xxx.xcodeproj 添加为子项目。

  • 点击 Runner 根项目,在Build PhasesTab下的Target Dependencies点击加号添加 $crate-staticlib文件。
    Flutter调用Rust代码操作指南
  • 接着,展开下面的 Link Binary With Libraries点击加号, 为 IOS 添加 lib$crate_static.a文件。
    Flutter调用Rust代码操作指南
    完成后如下图:
    Flutter调用Rust代码操作指南

绑定一开始生成的头文件bridge_generated.h

ios/Runner/Runner-Bridging-Header.h 中添加:bridge_generated.h

#import "GeneratedPluginRegistrant.h"
#import "bridge_generated.h"

ios/Runner/AppDelegate.swift 中添加dummy_method_to_enforce_bundling()

import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    let dummy = dummy_method_to_enforce_bundling()
    print(dummy)
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

Flutter调用

至此,配置工作已全部结束。下面我们看下如何调用,首先我们简单封装一个方法调用类。

import 'dart:ffi';
import 'dart:io';

import 'package:flutter_ffi/ffi/rust_ffi.dart';

class NativeFFI {
  NativeFFI._();

  static DynamicLibrary? _dyLib;

  static DynamicLibrary get dyLib {
    if (_dyLib != null) return _dyLib!;

    if (Platform.isIOS) {
      _dyLib = DynamicLibrary.process();
    } else if (Platform.isAndroid) {
      _dyLib = DynamicLibrary.open('librust_demo.so');
    } else {
      throw Exception('DynamicLibrary初始化失败');
    }

    return _dyLib!;
  }
}

class NativeFun {
  static final _ffi = RustDemoImpl(NativeFFI.dyLib);

  static Future<void> websocketConnect(String host) async {
    return await _ffi.websocketConnect(host: host);
  }

  static Future<void> sendMessage(String host, String message) async {
    return await _ffi.sendMessage(host: host, message: message);
  }

  static Future<void> websocketDisconnect(String host) async {
    return await _ffi.websocketDisconnect(host: host);
  }
}

使用时,直接调用NativeFun.xxx()方法将可以了。


以上示例代码我已经提交到Github,有需要的可以运行查看。对你有帮助的话,点赞收藏起来~我们下个月再见!

参考

  • Flutter和Rust如何优雅的交互

  • flutter_rust_bridge中文版文档文章来源地址https://www.toymoban.com/news/detail-486517.html

到了这里,关于Flutter调用Rust代码操作指南的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 端口操作指南

    知行之桥 EDI 系统中的端口是用于创建数据工作流的功能模块。每个端口可执行以下的一个任务: 使用标准网络协议 (AS2,AS4,FTP,SFTP,OFTP 等)与外部目标之间传输文件 与后端系统集成,例如数据库或云端应用程序 使用或公开 RESTful web API 将数据从一种格式转换为另一种格式

    2024年02月16日
    浏览(54)
  • git常用操作指南

    先可以使用git tag命令查看下当前有哪些tag,然后在原有tag的基础上增加版本号,并提交tag信息,例如: 即可 如果有个项目是用git的lfs管理的,那么首先需要安装git-lfs 然后执行: 最后,git clone 如何删除本地某个分支: 在删除你想删除的那个分支前,需要先切换到其他分支,

    2024年02月07日
    浏览(55)
  • Git操作指南

    Git是目前最流行的版本控制系统之一,它为开发者提供了便捷的代码管理和协作工具。对于初学者来说,熟悉Git的操作和基本概念是非常重要的。本文将带你从入门到进阶,逐步掌握Git的常用操作和技巧。 1、Git简介: Git是什么?为什么需要使用Git? Git是一个分布式版本控制

    2024年01月19日
    浏览(53)
  • Linux常规操作指南

    (1)查看当前目录内容 或查看详细信息: (2)切换工作目录 (3)创建新目录 (4)删除空目录 (5)删除文件或目录(递归删除) (1)复制文件或目录 (2)移动或重命名文件/目录 (3)查看文件内容 (4)编辑文件(使用vi/vim编辑器) 在vi中,输入 i 进入插入模式,编辑

    2024年01月19日
    浏览(55)
  • rabbitMQ入门指南:管理页面全面指南及实战操作

      在前一篇文章在centos stream 9环境中部署和使用rabbitMQ,我们已经详细介绍了如何在CentOS下安装和配置RabbitMQ,我们不仅启动了RabbitMQ服务,还通过插件安装了管理后台,并且登陆到管理页面。   RabbitMQ管理后台提供了一个直观的用户界面,允许我们查看和管理RabbitMQ服务器

    2024年02月12日
    浏览(50)
  • Python 列表操作指南1

    Python 列表 列表用于在单个变量中存储多个项目。列表是 Python 中的 4 种内置数据类型之一,用于存储数据集合,其他 3 种分别是元组(Tuple)、集合(Set)和字典(Dictionary),它们具有不同的特性和用途。 使用方括号创建列表: 列表项是有序的、可变的,并且允许重复值。

    2024年02月08日
    浏览(69)
  • Python 列表操作指南3

    示例,将新列表中的所有值设置为 \\\'hello\\\': 表达式还可以包含条件,不像筛选器那样,而是作为操纵结果的一种方式: 示例,返回 \\\"orange\\\" 而不是 \\\"banana\\\": 列表对象具有 sort() 方法,默认情况下将对列表进行字母数字排序,升序排列: 示例,对列表按字母顺序排序: 示例,对

    2024年02月08日
    浏览(41)
  • Rocky Linux操作指南

    rocky Linux 相信还有一些同学会比较陌生。好像平常只听说过Ubuntu和centos。rocky Linux 是个什么东西呢。它其实就是centos8的更稳定版本:centos8 测试版 -- rhel8 -- rocky8 它现在已经更新到了第九代的一个版本,我们暂时先不用那么高的。先用rocky8.6版本熟悉一下基础的操作。 CentOS

    2024年02月03日
    浏览(49)
  • 银河麒麟服务器操作系统【进入救援模式】操作指南

     银河麒麟服务器操作系统使用光驱或者U盘启动盘引导进入救援模式的操作方法类似,这里不再阐述。 以银河麒麟服务器操作系统V10使用光驱或者U盘启动盘进入救援模式为例,具体操作步骤如下: 1.插入光驱或者U盘启动盘后,重启系统,再根据屏幕下方的提示,按相应键进

    2024年01月21日
    浏览(146)
  • Docker 命令和组合操作指南

    Docker 是一种流行的容器化平台,允许开发人员在隔离的环境中构建、打包、发布和运行应用程序。下面是一些 Docker 的基础命令: docker version :检查 Docker 版本信息。 docker info :显示 Docker 系统信息,如运行的容器数量和镜像数量等。 docker search image :搜索 Docker Hub 上的镜像

    2024年02月12日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包