1. 基本使用
Basic Message Channel
final BasicMessageChannel _bmc = const BasicMessageChannel('basic_message', StringCodec());
String _string = 'null';
_requestMesssage() async {
// 這種方式會通過原生端調(diào)用 `callback` 返回消息, 不會觸發(fā) `setMessageHandler`
_string = await _bmc.send('i need a string');
setState(() {});
}
_handleMessage() {
// 原生端通過`bmc.send()`觸發(fā)
_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);
}];
// 主動發(fā)送消息 -- 需要等待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調(diào)用flutter端 - 這里的調(diào)用不觸發(fā), 因為沒有任何對象持有`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調(diào)用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的數(shù)據(jù)傳遞都是通過BinaryMessenger
+Codec
,在運行時BinaryMessenger
的實現(xiàn)是_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
的主要區(qū)別是編解碼器不同,同時后者會調(diào)用_handleAsMethodCall
晦炊,其目的是將二進制數(shù)據(jù)轉(zhuǎn)化為MethodCall
類型。
在_DefaultBinaryMessenger
中,可以看到ui.PlatformDispatcher.instance.sendPlatformMessage
唉俗,是Basic Message Channel
的send
和Method Channel
的invokeMethod
的后續(xù)調(diào)用,最終調(diào)用的是C++
配椭。
而ui.channelBuffers.setListener
則是setMessageHandler
(Basic Message Channel)和setMethodCallHandler
(Method Channel)的后續(xù)調(diào)用虫溜。這里將我們傳入的handler
包裝在ChannelCallback
內(nèi),然后推入ui.channelBuffers
股缸,當有消息從原生端傳過來衡楞,ui.PlatformDispatcher
會去ui.channelBuffers
查詢對應的Channel
,從中觸發(fā)回調(diào)敦姻。
·2. Event Channel
Event Channel
其實就是對Method Channel
的封裝瘾境。在使用Event Channel
的使用調(diào)用了receiveBroadcastStream
方法,內(nèi)部代碼如下
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
回調(diào)會在Stream
首次被訂閱的時候回調(diào)寄雀。所以當我們在外部運行_ec.receiveBroadcastStream().listen(_onEvent);
對Stream
產(chǎn)生的訂閱,于是onListen
被調(diào)用陨献。然后在onListen
中調(diào)用:
await methodChannel.invokeMethod<void>('listen', arguments)
并在Method Channel
的回調(diào)中將原生端返回的數(shù)據(jù)注入Stream
:
controller.add(codec.decodeEnvelope(reply))
onCancel
則在Stream
沒有訂閱者時被調(diào)用盒犹,在這里停止Method Channel
:
binaryMessenger.setMessageHandler(name, null);
try {
await methodChannel.invokeMethod<void>('cancel', arguments);
} catch (exception, stack) {
......
}
end
精力有限,有誤請指正眨业。