Flutter消息通信插件irondash_message_channel的使用
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。
线程考虑
MethodHandler
和AsyncMethodHandler
绑定到创建它们的线程。该线程必须运行一个RunLoop
。这是隐含地对平台线程成立。为了在背景线程上使用通道,您需要创建一个RunLoop
并自己运行它。
MethodInvoker
是Send
。它可以跨线程传递,并且响应方法调用将在发送请求的相同线程接收。再次,该线程必须运行一个RunLoop
。
从Value到Value和从Value到Value的转换
Value
表示所有可以在Rust和D Dart之间发送的数据类型。为了简化Rust侧的序列化和反序列化,irondash_message_channel
提供了IntoValue
和TryFromValue
proc macros,这些宏生成TryInto<YourStruct>
和From<YourStruct>
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
更多关于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与原生平台之间进行消息传递。在实际项目中,你可能需要根据具体需求调整代码。