Dart和原生通信
一次典型的方法调用过程类似网络调用,由作为客户端的 Flutter,通过方法通道向作为服务端的原生代码宿主发送方法调用请求,原生代码宿主在监听到方法调用的消息后,调用平台相关的 API 来处理 Flutter 发起的请求,最后将处理完毕的结果通过方法通道回发至 Flutter。调用过程如下图所示:
1. 基本用法
以跳转应用市场为例
//声明MethodChannel
const platform = MethodChannel('channelName');
//处理按钮点击
handleButtonClick() async{
int result;
//异常捕获
try {
//异步等待方法通道的调用结果
result = await platform.invokeMethod('openAppMarket');
}
catch (e) {
result = -1;
}
print("Result:$result");
}
1.1 Android端实现
MainActivity.java
public class MainActivity extends FlutterActivity {
MethodChannel methodChannel;
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
methodChannel = new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), "channelName");
methodChannel.setMethodCallHandler((call, result) -> {
//判断方法名是否支持
if(call.method.equals("openAppMarket")) {
try {
//应用市场URI
Uri uri = Uri.parse("market://details?id=com.hangchen.example.flutter_module_page.host");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//打开应用市场
startActivity(intent);
//返回处理结果
result.success(0);
} catch (Exception e) {
//打开应用市场出现异常
result.error("UNAVAILABLE", "没有安装应用市场", null);
}
}else {
//方法名暂不支持
result.notImplemented();
}
});
}
}
1.2 iOS端实现
AppleDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//创建命名方法通道
FlutterMethodChannel* channel = [FlutterMethodChannel methodChannelWithName:@"samples.chenhang/utils" binaryMessenger:(FlutterViewController *)self.window.rootViewController];
//往方法通道注册方法调用处理回调
[channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
//方法名称一致
if ([@"openAppMarket" isEqualToString:call.method]) {
//打开App Store(本例打开微信的URL)
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"itms-apps://itunes.apple.com/xy/app/foo/id414478124"]];
//返回方法处理结果
result(@0);
} else {
//找不到被调用的方法
result(FlutterMethodNotImplemented);
}
}];
...
}
2. 原理分析
Dart这边的MethodChannel类,有几个重要的成员变量:
/// 方法通道的名字 not null
final String name;
/// 信息的编解码器 not null
final MethodCodec codec;
/// 用来发送消息的messenger
/// 未设定的话就用默认的
BinaryMessenger get binaryMessenger => _binaryMessenger ?? defaultBinaryMessenger;
final BinaryMessenger? _binaryMessenger;
2.1 调用invokeMethod来获取通信结果
Future<T?> _invokeMethod<T>(String method, {
required bool missingOk, dynamic arguments }) async {
final ByteData? result = await binaryMessenger.send(
name,
codec.encodeMethodCall(MethodCall(method, arguments)),
);
if (result == null) {
if (missingOk) {
return null;
}
throw MissingPluginException('No implementation found for method $method on channel $name');
}
return codec.decodeEnvelope(result) as T?;
}
先是将我们要调用的方法名构造了一个MethodCall,然后用codec编码,之后通过binaryMessenger发送了一条消息,调用结果会被封装成一个ByteData,然后用codec解码。
2.2 StandartMethodCodec
codec默认是一个StandardMethodCodec,看下它的encodeMethodCall方法做了什么
@override
ByteData encodeMethodCall(MethodCall call) {
final WriteBuffer buffer = WriteBuffer();
messageCodec.writeValue(buffer, call.method);
messageCodec.writeValue(buffer, call.arguments);
return buffer.done();
}
构造了一个WriteBuffer,然后将方法名和buffer传进messageCodec
void writeValue(