1 个不稳定版本
0.1.1 | 2022年11月15日 |
---|
#17 在 #background-thread
4,178 每月下载次数
用于 irondash_message_channel
51KB
1K SLoC
irondash_message_channel
Rust-dart桥接器,类似于Flutter的平台通道。
此包允许使用类似Flutter平台通道的模式从Dart调用Rust代码,反之亦然。
- 易于使用的便捷API(Dart端模仿平台通道API)。
- 高性能
- 从Rust调用Dart时二进制数据无复制
- 从Dart调用Rust时二进制数据精确复制一次
- Rust宏用于自动序列化和反序列化(类似于Serde但针对零复制进行了优化)
- 无需代码生成
- 线程亲和性 - Rust通道对应项绑定到创建通道的线程。只要运行RunLoop,就可以在平台线程或任何后台线程上拥有通道。
- 最终化处理器 - Rust端可以在Dart对象被垃圾回收时收到通知。
- 异步支持
用法
初始设置
因为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,或者如果您想使用异步/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
Value
代表了可以在 Rust 和 Dart 之间传递的所有类型。为了简化 Rust 端的序列化和反序列化,irondash_message_channel
提供了 IntoValue
和 TryFromValue
进程宏,为 Value
生成 TryInto<YourStruct>
和 From<YourStruct>
特性。这是一个可选功能。
[dependencies]
irondash_message_channel = { version = "0.1.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.5MB
~35K SLoC