9个版本 (重大更新)
0.7.0 | 2024年5月10日 |
---|---|
0.6.0 | 2024年2月8日 |
0.5.0 | 2023年12月26日 |
0.4.0-dev2 | 2023年11月29日 |
0.1.0 | 2022年11月15日 |
#174 in 解析器实现
每月4,895次下载
225KB
5.5K SLoC
irondash_message_channel
Rust-dart桥梁,类似于Flutter的平台通道。
此包允许使用类似于Flutter平台通道的模式从Dart调用Rust代码,反之亦然。
- 易于使用的便捷API(Dart端模仿平台通道API)。
- 高性能
- 从Rust调用Dart时,二进制数据无需复制
- 从Dart调用Rust时,二进制数据正好复制一次
- Rust宏,用于自动序列化和反序列化(类似于Serde,但针对零复制进行了优化)
- 无需代码生成
- 线程亲和力 - Rust通道对应物绑定到创建通道的线程。只要运行RunLoop,您就可以在平台线程或任何后台线程上拥有通道。
- 终结处理程序 - 当Dart对象被垃圾回收时,Rust端可以收到通知。
- 异步支持
使用方法
初始设置
因为Rust代码需要访问Dart FFI api,所以需要进行一些设置。
/// initialize context for Native library.
MessageChannelContext _initNativeContext() {
final dylib = defaultTargetPlatform == TargetPlatform.android
? DynamicLibrary.open("libmyexample.so")
: (defaultTargetPlatform == TargetPlatform.windows
? DynamicLibrary.open("myexample.dll")
: DynamicLibrary.process());
// This function will be called by MessageChannel with opaque FFI
// initialization data. From it you should call
// `irondash_init_message_channel_context` and do any other initialization,
// i.e. register rust method channel handlers.
final function =
dylib.lookup<NativeFunction<MessageChannelContextInitFunction>>(
"my_example_init_message_channel_context");
return MessageChannelContext.forInitFunction(function);
}
final nativeContext = _initNativeContext();
// Now you can create method channels
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,或者如果您想使用async/await,则实现AsyncMethodHandler
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");
// make sure handler is not dropped, otherwise it can't handle method calls.
}
或异步版本
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");
// make sure handler is not dropped, otherwise it can't handle method calls.
}
从Rust调用Dart
use irondash_message_channel::*;
struct MyHandler {
invoker: Late<AsyncMethodInvoker>,
}
#[async_trait(?Send)]
impl AsyncMethodHandler for MyHandler {
// This will be called right after method channel registration.
// You can use invoker to call Dart methods handlers.
fn assign_invoker(&self, invoker: AsyncMethodInvoker) {
self.invoker.set(invoker);
}
// ...
}
请注意,要使用Invoker
,您需要知道目标isolateId
。您可以从Rust中处理方法调用时获取MethodCall
结构中获取它。您还可以在isolate被销毁时收到通知
impl MethodHandler for MyHandler {
/// Called when isolate is about to be destroyed.
fn on_isolate_destroyed(&self, _isolate: IsolateId) {}
// ...
要查看消息通道的实际应用,请查看示例项目。
线程考虑因素
MethodHandler
和 AsyncMethodHandler
与创建它们的线程绑定。该线程必须运行一个 RunLoop。对于平台线程,这是隐含成立的。要在后台线程使用通道,您需要创建一个 RunLoop
并自行运行它。
MethodInvoker
是 Send
。它可以在线程之间传递,方法调用的响应将在发送请求的同一线程上接收。同样,该线程必须有一个正在运行的 RunLoop
。
值之间的转换
Value
表示可以在 Rust 和 Dart 之间发送的所有类型。为了简化 Rust 端的序列化和反序列化,irondash_message_channel
提供了 IntoValue
和 TryFromValue
过程宏,它们为 Value
生成 TryInto<YourStruct>
和 From<YourStruct>
特性。这是一个可选功能
[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>,
},
}
与 serde 不同,.into()
和 try_into()
消耗原始值,这使得零拷贝序列化和反序列化成为可能。
依赖项
~1–15MB
~143K SLoC