Flutter和Rust混合编程

15 9月

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

转载自夜明的孤行灯

本文链接地址: https://www.huangyunkun.com/2021/09/15/flutter-with-rust/



Rust语言本身就是跨平台的,加上很多性能上的优势,很多代码可以在一定程度上复用。

Flutter的FFI支持可以让我们调用本地代码,也就是可以做到Flutter和Rust混合编程。

Rust改造

为了减少改动,我一般会引入一个新的项目,直接在原项目名基础上加上ffi。

这个项目的主要用途在于定义对外暴露的接口,并在构建时输出so等文件。

在Cargo.toml中新增两个依赖

[dependencies]
rust-project = { path = "../rust-project" }

[build-dependencies]
cbindgen = "0.14.3"
dart-bindgen = "0.1.7"

其中cbindgen帮助我们从Rust代码生成C/C++的头文件,dart-bindgen(现在用ffigen了)帮助我们从头文件生成dart绑定。

Flutter插件

有了上一步的so和dart文件后我们就可以调用了,但是出于隔离和复用的考虑,这里不直接在Flutter项目中使用,而是先包装成Plugin。

按照设计,Flutter插件可以用于封装与平台专用代码进行的通信,由于上一步已经生成了绑定,所以这里只做最基础的封装和注册。

import 'ffi.dart' as ffi;

class Plugin {
  int functionName(int a, int b) {
    return ffi.functionName(a, b);
  }
}

在android平台中新增注册

package com.example.adder

import androidx.annotation.NonNull;
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.PluginRegistry.Registrar

/** AdderPlugin */
public class AdderPlugin: FlutterPlugin, MethodCallHandler {
  override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
    val channel = MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "adder")
    channel.setMethodCallHandler(AdderPlugin());
  }

  // This static function is optional and equivalent to onAttachedToEngine. It supports the old
  // pre-Flutter-1.12 Android projects. You are encouraged to continue supporting
  // plugin registration via this function while apps migrate to use the new Android APIs
  // post-flutter-1.12 via https://flutter.dev/go/android-project-migration.
  //
  // It is encouraged to share logic between onAttachedToEngine and registerWith to keep
  // them functionally equivalent. Only one of onAttachedToEngine or registerWith will be called
  // depending on the user's project. onAttachedToEngine or registerWith must both be defined
  // in the same class.
  companion object {
    @JvmStatic
    fun registerWith(registrar: Registrar) {
      val channel = MethodChannel(registrar.messenger(), "adder")
      channel.setMethodCallHandler(AdderPlugin())
    }
  }

  override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
    if (call.method == "getPlatformVersion") {
      result.success("Android ${android.os.Build.VERSION.RELEASE}")
    } else {
      result.notImplemented()
    }
  }

  override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
  }
}

Flutter调用

插件的使用和普通的基本一致,只是在依赖中使用路径。

这里注意一点,x86的flutter.so有些版本只有debug版本,release后返回会报错,但是rust生成的so不涉及这个问题,如果有闪退注意看错误日志。

Cargo-make

这里是一个可选项目,由于混合编程设计的构建和依赖比较多,一般都会用脚本或者其他方式管理。如果熟悉Makefile自然可以用,如果不怎么接触的可以考虑下Cargo-make。

可以处理常见的依赖安装、操作系统判断、基础函数、任务依赖等。



本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

转载自夜明的孤行灯

本文链接地址: https://www.huangyunkun.com/2021/09/15/flutter-with-rust/

发表评论

您的电子邮箱地址不会被公开。