Platform Channel (iOS)
1. 基本使用
Basic Message Channel
final BasicMessageChannel _bmc = const BasicMessageChannel('basic_message', StringCodec());
String _string = 'null';
_requestMesssage() async {
// 这种方式会通过原生端调用 `callback` 返回消息, 不会触发 `setMessageHandler`
_string = await _bmc.send('i need a string');
setState(() {});
}
_handleMessage() {
// 原生端通过`bmc.send()`触发
_bmc.setMessageHandler((message) async {
setState(() {
if (message is String) {
_string = message;
}
});
return 'i\' dart side, i receive a message: $message';
});
}
@override
void initState() {
super.initState();
_handleMessage();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Basic Message Channel'),
),
body: child: Text(_string),
floatingActionButton: ElevatedButton(
onPressed: () {
_requestMesssage();
},
child: const Text('request message'),
),
);
}
Method Channel
final MethodChannel _mc = const MethodChannel('method_channel');
bool _changed = false;
String _message = 'null';
_invokeMethod() async {
_message = await _mc.invokeMethod('getString', 1321);
setState(() {});
}
_handleMethod() {
_mc.setMethodCallHandler((call) async {
switch (call.method) {
case 'changeColor':
setState(() {
_changed = !_changed;
print(call.arguments);
});
break;
default:
}
});
}
@override
void initState() {
super.initState();
_handleMethod();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Method Channle'),
),
body: Container(
width: 200,
height: 100,
color: _changed ? Colors.red : Colors.blue,
child: Text(_message),
),
floatingActionButton: ElevatedButton(
onPressed: () {
_invokeMethod();
},
child: const Text('invoke method')),
);
}
Event Channel
final EventChannel _ec = const EventChannel('event_channel');
int _num = 0;
_onEvent(event) {
if (event is int) {
print(event);
setState(() {
_num = event;
});
}
}
late Stream _stream;
@override
void initState() {
super.initState();
_ec.receiveBroadcastStream().listen(_onEvent);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Method Channle'),
),
body: Container(
width: 200,
height: 100,
color: Colors.blue,
child: Text('$_num'),
),
floatingActionButton: ElevatedButton(
onPressed: () {
_stream.listen(_onEvent);
},
child: const Text('invoke method')),
);
}
iOS代码
#import "AppDelegate.h"
#import "GeneratedPluginRegistrant.h"
@interface EventHander : NSObject<FlutterStreamHandler>
@property (nonatomic, strong, nullable)FlutterEventSink events;
@property (nonatomic, assign)int count;
@end
@implementation EventHander
- (instancetype)init {
NSLog(@"event hander init");
if (self = [super init]) {
[self startTimer];
}
return self;
}
- (void)startTimer {
[NSTimer scheduledTimerWithTimeInterval:2 repeats:true block:^(NSTimer * _Nonnull timer) {
self.count++;
if (self.events != nil) {
self.events([[NSNumber alloc] initWithInt:self.count]);
}
}];
}
- (FlutterError * _Nullable)onCancelWithArguments:(id _Nullable)arguments {
NSLog(@"%@", arguments);
self.events = nil;
return nil;
}
- (FlutterError * _Nullable)onListenWithArguments:(id _Nullable)arguments eventSink:(nonnull FlutterEventSink)events {
NSLog(@"%@", arguments);
self.events = events;
return nil;
}
- (void)dealloc {
NSLog(@"dealloc");
}
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
FlutterViewController *rootVC = (FlutterViewController *)self.window.rootViewController;
// MARK: Basic Message Channel
FlutterBasicMessageChannel *bmc = [[FlutterBasicMessageChannel alloc] initWithName:@"basic_message" binaryMessenger:rootVC.binaryMessenger codec:[FlutterStringCodec sharedInstance]];
// 收到消息后回复
[bmc setMessageHandler:^(id _Nullable message, FlutterReply _Nonnull callback) {
NSString *callbackStr = [NSString stringWithFormat:@"i revice a message: %@", message];
callback(callbackStr);
}];
// 主动发送消息 -- 需要等待flutter端初始化platform Channel
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3*NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[bmc sendMessage:@"i'm ios side, i send this message"];
});
// MARK: Method Channel
FlutterMethodChannel *mc = [[FlutterMethodChannel alloc] initWithName:@"method_channel" binaryMessenger:rootVC.binaryMessenger codec:[FlutterStandardMethodCodec sharedInstance]];
__weak typeof(mc) w_mc = mc;
[mc setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {
__strong typeof(w_mc) s_mc = w_mc;
NSLog(@"w_mc:%@", w_mc);
NSLog(@"s_mc:%@", s_mc);
if ([call.method isEqualToString:@"getString"]) {
result([NSString stringWithFormat:@"%@", call.arguments]);
}
// ios调用flutter端 - 这里的调用不触发, 因为没有任何对象持有`mc`, 在`didFinishLaunchingWithOptions` 运行完毕后 `mc`会被释放
[s_mc invokeMethod:@"changeColor" arguments:nil];
}];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5*NSEC_PER_SEC), dispatch_get_main_queue(), ^{
// ios调用flutter端
[mc invokeMethod:@"changeColor" arguments:nil];
});
// MARK: Event Channel
/*
EventChannel 是对 MethodChannel和Stream的封装,
*/
FlutterEventChannel *ec = [FlutterEventChannel eventChannelWithName:@"event_channel" binaryMessenger:rootVC.binaryMessenger];
[ec setStreamHandler:[[EventHander alloc] init]];
[GeneratedPluginRegistrant registerWithRegistry:self];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end
2. 原理
·1. Basic Message Channel & Method Channel
Platform Channel的数据传递都是通过BinaryMessenger
+Codec
,在运行时BinaryMessenger
的实现是_DefaultBinaryMessenger
,源码位于binding.dart文件
import 'dart:ui' as ui;
// ......
class _DefaultBinaryMessenger extends BinaryMessenger {
const _DefaultBinaryMessenger._();
@override
Future<void> handlePlatformMessage(
String channel,
ByteData? message,
ui.PlatformMessageResponseCallback? callback,
) async {
ui.channelBuffers.push(channel, message, (ByteData? data) {
if (callback != null)
callback(data);
});
}
@override
Future<ByteData?> send(String channel, ByteData? message) {
final Completer<ByteData?> completer = Completer<ByteData?>();
ui.PlatformDispatcher.instance.sendPlatformMessage(channel, message, (ByteData? reply) {
try {
completer.complete(reply);
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'services library',
context: ErrorDescription('during a platform message response callback'),
));
}
});
return completer.future;
}
@override
void setMessageHandler(String channel, MessageHandler? handler) {
if (handler == null) {
ui.channelBuffers.clearListener(channel);
} else {
ui.channelBuffers.setListener(channel, (ByteData? data, ui.PlatformMessageResponseCallback callback) async {
ByteData? response;
try {
response = await handler(data);
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'services library',
context: ErrorDescription('during a platform message callback'),
));
} finally {
callback(response);
}
});
}
}
}
可通过
import 'package:flutter/services.dart';
找到export 'src/services/binding.dart';
阅读上面的代码。
Basic Message Channel
和Method Channel
的主要区别是编解码器不同,同时后者会调用_handleAsMethodCall
,其目的是将二进制数据转化为MethodCall
类型。
在_DefaultBinaryMessenger
中,可以看到ui.PlatformDispatcher.instance.sendPlatformMessage
,是Basic Message Channel
的send
和Method Channel
的invokeMethod
的后续调用,最终调用的是C++
。
而ui.channelBuffers.setListener
则是setMessageHandler
(Basic Message Channel)和setMethodCallHandler
(Method Channel)的后续调用。这里将我们传入的handler
包装在ChannelCallback
内,然后推入ui.channelBuffers
,当有消息从原生端传过来,ui.PlatformDispatcher
会去ui.channelBuffers
查询对应的Channel
,从中触发回调。
·2. Event Channel
Event Channel
其实就是对Method Channel
的封装。在使用Event Channel
的使用调用了receiveBroadcastStream
方法,内部代码如下
Stream<dynamic> receiveBroadcastStream([dynamic arguments]) {
String name = 'event_channel';
MethodCodec codec = const StandardMethodCodec();
MethodChannel methodChannel = MethodChannel(name, codec);
BinaryMessenger binaryMessenger = ServicesBinding.instance.defaultBinaryMessenger;
late StreamController<dynamic> controller;
controller = StreamController<dynamic>.broadcast(onListen: () async {
binaryMessenger.setMessageHandler(name, (ByteData? reply) async {
if (reply == null) {
controller.close();
} else {
try {
controller.add(codec.decodeEnvelope(reply));
} on PlatformException catch (e) {
controller.addError(e);
}
}
return null;
});
try {
await methodChannel.invokeMethod<void>('listen', arguments);
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'services library',
context: ErrorDescription('while activating platform stream on channel $name'),
));
}
}, onCancel: () async {
binaryMessenger.setMessageHandler(name, null);
try {
await methodChannel.invokeMethod<void>('cancel', arguments);
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'services library',
context: ErrorDescription('while de-activating platform stream on channel $name'),
));
}
});
return controller.stream;
}
先将代码简化如下:
late StreamController<dynamic> controller;
controller = StreamController<dynamic>.broadcast(onListen: () async {
print('on listen');
}, onCancel: () async {
print('on cancel');
});
return controller.stream;
这里的重点就是broadcast
方法,其onListen
回调会在Stream
首次被订阅的时候回调。所以当我们在外部运行_ec.receiveBroadcastStream().listen(_onEvent);
对Stream
产生的订阅,于是onListen
被调用。然后在onListen
中调用:
await methodChannel.invokeMethod<void>('listen', arguments)
并在Method Channel
的回调中将原生端返回的数据注入Stream
:
controller.add(codec.decodeEnvelope(reply))
onCancel
则在Stream
没有订阅者时被调用,在这里停止Method Channel
:
binaryMessenger.setMessageHandler(name, null);
try {
await methodChannel.invokeMethod<void>('cancel', arguments);
} catch (exception, stack) {
......
}
end
精力有限,有误请指正。