Flutter消息通信插件irondash_message_channel的使用

发布于 1周前 作者 sinazl 来自 Flutter

Flutter消息通信插件irondash_message_channel的使用

irondash_message_channel

Rust-dart桥接类似于Flutter的平台通道。

此包允许通过模式调用Dart代码和反之亦然,类似于Flutter的平台通道。

  • 简单易用的API(Dart侧模仿平台通道API)。
  • 高性能
    • 调用Dart时零拷贝二进制数据
    • 调用Rust时仅拷贝一次二进制数据
  • Rust宏自动序列化和反序列化(类似Serde但优化为零拷贝)
  • 无需代码生成
  • 线程亲和性 - Rust通道对应于创建通道时绑定的线程。您可以在运行RunLoop的平台上线程或任何后台线程上创建通道,只要该线程在运行即可。
  • 最终处理程序 - Rust侧可以获取当Dart对象被垃圾回收时的通知。
  • 异步支持

使用

初始设置

因为Rust代码需要访问Dart FFI API,所以需要一些设置。

/// 初始化Native库上下文。
MessageChannelContext _initNativeContext() {
    final dylib = defaultTargetPlatform == TargetPlatform.android
        ? DynamicLibrary.open("libmyexample.so")
        : (defaultTargetPlatform == TargetPlatform.windows
            ? DynamicLibrary.open("myexample.dll")
            : DynamicLibrary.process());

    // 这个函数将由MessageChannel调用以提供FFI初始化数据。从这里你应该调用`irondash_init_message_channel_context`并进行其他初始化,例如注册Rust方法通道处理器。
    final function =
        dylib.lookup<NativeFunction<MessageChannelContextInitFunction>>(function);
    return MessageChannelContext.forInitFunction(function);
}

final nativeContext = _initNativeContext();

// 现在可以创建方法通道了

final _channel =
    NativeMethodChannel('my_method_channel', context: nativeContext);

_channel.setMethodCallHandler(...);
Rust侧
use irondash_message_channel::*;

#[no_mangle]
pub extern "C" fn my_example_init_message_channel_context(data: *mut c_void) -> FunctionResult {
    irondash_init_message_channel_context(data)
}
简单使用

在完成设置后,您可以使用Dart的NativeMethodChannel,类似于Flutter的PlatformChannel

final _channel = NativeMethodChannel('my_method_channel', context: nativeContext);

_channel.setMessageHandler((call) async {
    if (call.method == 'myMethod') {
        return 'myResult';
    }
    return null;
});

final res = await _channel.invokeMethod('someMethod', 'someArg');
在Rust侧,您可以实现MethodHandler trait用于非异步版本,或者AsyncMethodHandler如果想要使用async/await:
use irondash_message_channel::*;

struct MyHandler {}

impl MethodHandler for MyHandler {
    fn on_method_call(&self, call: MethodCall, reply: MethodCallReply) {
        match call.method.as_str() {
            "getMeaningOfUniverse" => {
                reply.send_ok(42);
            }
            _ => reply.send_error(
                "invalid_method".into(),
                Some(format!("Unknown Method: {}", call.method)),
                Value::Null,
            ),
        }
    }
}

fn init() {
    let handler = MyHandler {}.register("my_method_channel");
    // 确保handler不被释放,否则无法处理方法调用。
}
异步版本:
use irondash_message_channel::*;

struct MyHandler {}

#[async_trait(?Send)]
impl AsyncMethodHandler for MyHandler {
    async fn on_method_call(&self, call: MethodCall) -> PlatformResult {
        match call.method.as_str() {
            "getMeaningOfUniverse" => {
                Ok(42.into())
            }
            _ => Err(PlatformError {
                code: "invalid_method".into(),
                message: Some(format!("Unknown Method: {}", call.method)),
                detail: Value::Null,
            })
        }
    }
}

fn init() {
    let handler = MyHandler {}.register("my_method_channel");
    // 确保handler不被释放,否则无法处理方法调用。
}
调用Dart从Rust
use irondash_message_channel::*;

struct MyHandler {
    invoker: Late<AsyncMethodInvoker>,
}

#[async_trait(?Send)]
impl AsyncMethodHandler for MyHandler {
    // 这将在方法通道注册后立即调用。你可以使用invoker来调用Dart方法处理器。
    fn assign_invoker(&self, invoker: AsyncMethodInvoker) {
        self.invoker.set(invoker);
    }

    // ...
}

请注意,要使用Invoker,您需要知道目标isolateId。您可以在在Rust中处理方法调用时从MethodCall结构中获取它。您还可以在隔离销毁时收到通知:

impl MethodHandler for MyHandler {
    /// 当隔离即将销毁时调用。
    fn on_isolate_destroyed(&self, isolate: IsolateId) {}
    // ...
}

要查看消息通道的效果,请参阅https://github.com/irondash/irondash/message_channel/dart/example

线程考虑

MethodHandlerAsyncMethodHandler绑定到创建它们的线程。该线程必须运行一个RunLoop。这是隐含地对平台线程成立。为了在背景线程上使用通道,您需要创建一个RunLoop并自己运行它。

MethodInvokerSend。它可以跨线程传递,并且响应方法调用将在发送请求的相同线程接收。再次,该线程必须运行一个RunLoop

从Value到Value和从Value到Value的转换

Value表示所有可以在Rust和D Dart之间发送的数据类型。为了简化Rust侧的序列化和反序列化,irondash_message_channel提供了IntoValueTryFromValue proc macros,这些宏生成TryInto&lt;YourStruct&gt;From&lt;YourStruct&gt; traits。这是可选功能:

[dependencies]
irondash_message_channel = { version = "0.6.0", features = ["derive"] }
#[derive(TryFromValue, IntoValue)]
struct AdditionRequest {
    a: f64,
    b: f64,
}

#[derive(IntoValue)]
struct AdditionResponse {
    result: f64,
    request: AdditionRequest,
}

let value: Value = get_value_from_somewhere();
let request: AdditionRequest = value.try_into()?;
let response: Value = AdditionResponse {
    result: request.a + request.b,
    request,
}.into();

更高级的映射选项也受到支持,例如:

#[derive(IntoValue, TryFromValue)]
#[irondash(tag = "t", content = "c")]
#[irondash(rename_all = "UPPERCASE")]
enum Enum3CustomTagContent {
    Abc,
    #[irondash(rename = "_Def")]
    Def,
    SingleValue(i64),
    #[irondash(rename = "_DoubleValue")]
    DoubleValue(f64, f64),
    Xyz {
        x: i64,
        s: String,
        z1: Option<i64>,
        #[irondash(skip_if_empty)]
        z2: Option<i64>,
        z3: Option<f64>,
    },
}

更多关于Flutter消息通信插件irondash_message_channel的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter消息通信插件irondash_message_channel的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何使用Flutter消息通信插件irondash_message_channel的代码示例。这个示例将展示如何在Flutter与原生平台(如Android和iOS)之间进行消息传递。

1. 添加依赖

首先,在你的pubspec.yaml文件中添加irondash_message_channel依赖:

dependencies:
  flutter:
    sdk: flutter
  irondash_message_channel: ^latest_version  # 请替换为最新的版本号

然后运行flutter pub get来安装依赖。

2. 配置原生代码

Android

android/app/src/main/AndroidManifest.xml中,确保你有必要的权限(如果需要)。

然后,在MainActivity.kt(或MainActivity.java)中,你可以设置消息处理逻辑。但通常,irondash_message_channel已经为你处理了大部分配置,你只需要在Flutter端调用即可。

iOS

在iOS上,你通常不需要做额外的配置,除非你有特定的需求。irondash_message_channel已经处理了基本的桥接逻辑。

3. Flutter端代码

导入插件

在你的Dart文件中导入插件:

import 'package:flutter/material.dart';
import 'package:irondash_message_channel/irondash_message_channel.dart';

设置消息通道

接下来,设置消息通道并进行消息传递。

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('irondash_message_channel Example'),
        ),
        body: Center(
          child: MessageChannelExample(),
        ),
      ),
    );
  }
}

class MessageChannelExample extends StatefulWidget {
  @override
  _MessageChannelExampleState createState() => _MessageChannelExampleState();
}

class _MessageChannelExampleState extends State<MessageChannelExample> {
  static const MethodChannel _channel = MethodChannel('com.example.my_app/message_channel');

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        ElevatedButton(
          onPressed: () async {
            // 发送消息到原生平台
            String result = await _channel.invokeMethod('sendMessage', {'message': 'Hello from Flutter!'});
            ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Received from native: $result')));
          },
          child: Text('Send Message to Native'),
        ),
      ],
    );
  }
}

原生端处理消息

Android端(在MainActivity.kt中,如果你需要自定义处理逻辑):

import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MainActivity: FlutterActivity() {
    private val CHANNEL = "com.example.my_app/message_channel"

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
            if (call.method == "sendMessage") {
                val message = call.argument<HashMap<String, String>>()?.get("message")
                // 处理接收到的消息
                result.success("Message received: $message")
            } else {
                result.notImplemented()
            }
        }
    }
}

iOS端(在AppDelegate.swift中,如果你需要自定义处理逻辑):

import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)
    
    let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
    let channel = FlutterMethodChannel(name: "com.example.my_app/message_channel", binaryMessenger: controller.binaryMessenger)
    channel.setMethodCallHandler({
      (call: FlutterMethodCall, result: @escaping FlutterResult) in
      if call.method == "sendMessage" {
        guard let message = call.arguments as? [String: String],
              let msg = message["message"] else {
          result(FlutterError(code: "NO_MESSAGE", message: "No message provided", details: nil))
          return
        }
        // 处理接收到的消息
        result("Message received: \(msg)")
      } else {
        result(FlutterMethodNotImplementedError(methodName: call.method))
      }
    })
    
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

注意:在大多数情况下,irondash_message_channel插件可能已经有了内置的桥接逻辑,因此你可能不需要在原生端手动设置MethodChannel。上述原生代码示例主要是为了展示如何手动处理消息,如果你只需要基本的消息传递功能,可以直接在Flutter端使用插件提供的API。

结论

这个示例展示了如何使用irondash_message_channel插件在Flutter与原生平台之间进行消息传递。在实际项目中,你可能需要根据具体需求调整代码。

回到顶部