はじめに
Flutter (Dart) とプラットフォーム (Android/iOSなど) 間の通信/呼び出しAPIについてまとめています。
- MethodChannel
- EventChannel
- MessageChannel ← 今回
1. MessageChannelとは
プラットフォーム (Android/iOSなど) とFlutter (Dart) 間で双方向でメッセージ送受信するためのAPIであるMessageChannel (正確にはBasicMessageChannel) です。片方からもう片方にデータを送信し、受信したらリプライを返すというごく普通のメッセージパッシング用のAPIです。
Flutterの仕組み的には、ここで解説しているPlatform Channelsを利用しています。
2. 基本フロー
実際のコードの説明の前にAPIの基本的なフローについて解説します。
ただし、プラットフォーム毎に若干API名が異なる場合があるため (iOS) 、注意してください。
送信側
- BasicMessageChannelでチャンネル生成
- BasicMessageChannel#sendでデータ送信
受信側
- BasicMessageChannel#MessageHandlerでイベントリスナーを登録
- データ受信時に登録したイベントリスナーのBasicMessageChannel#MessageHandler#onMessageがコールバックされるため、その中でリプライデータを送信
Dart → プラットフォームへの送信
プラットフォーム → Dartへの送信
3. サンプルプロジェクト
実際のAPIの使い方は後述しますが、サンプルプロジェクトを以下に用意しています。そのまま動作確認可能ですので、参考にしてください。
ただし、正式に何かの機能を実装する場合にはプラグインの形で組み込んだ方が良いです。
https://github.com/Kurun-pan/flutter-messagechannel-example
4. Flutter + Android (Kotlin) のコード解説
4.1 Dart側の実装
通信チャンネル作成
MethodChannel同様、BasicMessageChannelの通信チャンネルを作成します。
BasicMessageChannel(String name, MessageCodec<T> codec, {BinaryMessenger binaryMessenger})
第1引数には、指定する文字列は他のアプリケーションと被らず、一意に決まるようにするために慣例で"アプリパッケージ名/チャンネル名"とするのが一般的な様子です。
第2引数は送信データのコーデックタイプを指定します。Frameworkの内部的にはバイナリデータとして扱われるため、その情報を指定する必要があります。
タイプ | 内容 |
---|---|
BinaryCodec | ByteDataのデータ送信時 |
JSONMessageCodec | JSON形式のデータ送信時 |
StandardMessageCodec | 通常はこちらを利用(プリミティブ型) |
StringCodec | Stringデータを送信時 |
なお、サンプルプロジェクト↓ではJSONMessageCodecを利用したデータ送信の例をコメントアウトした形で残していますので、参考にしてください。
https://github.com/Kurun-pan/flutter-messagechannel-example/blob/master/lib/main.dart
データ送信
BasicMessageChannel#sendでデータ送信
Future<T> send(T message) async {
return codec.decodeMessage(await binaryMessenger.send(name, codec.encodeMessage(message)));
}
サンプルコード
サンプルコードは、Dart側からプラットフォーム側に文字列 (String型) を送信&リプライ受信と、プラットフォーム側からのメッセージ受信&リプライ送信の双方向の内容を盛り込んでいます。
class _MyHomePageState extends State<MyHomePage> {
static const _channel = BasicMessageChannel('com.example.messagechannel/interop', StringCodec());
String _platformMessage;
void _sendMessage() async {
final String reply = await _channel.send('Hello World form Dart');
print(reply);
}
@override
initState() {
super.initState();
// Receive messages from platform
_channel.setMessageHandler((String message) async {
print('Received message = $message');
setState(() => _platformMessage = message);
return 'Reply from Dart';
});
// Send message to platform
_sendMessage();
}
4.2 プラットフォーム側の実装
通信チャンネル作成
BasicMessageChannelの通信チャンネルを作成します。
BasicMessageChannel(BinaryMessenger messenger, String name, MessageCodec<T> codec)
第3引数にはString型のデータ送信時にはStringCodec.INSTANCEを指定します。StringCodecのstaticインスタンスになり、ソースコードを見てもらった方が早いです。
受信待機
BasicMessageChannel#setMessageHandlerでイベントリスナーを登録します。
リプライ送信
データ受信時に登録したイベントリスナーのBasicMessageChannel#MessageHandler#onMessageがコールバックされるため、その中でリプライデータを送信します。
サンプルコード
サンプルコードは、プラットフォーム側からプラットフォーム側に文字列 (String型) を送信&リプライ受信と、Dart側からのメッセージ受信&リプライ送信の双方向の内容を盛り込んでいます。
class MainActivity: FlutterActivity() {
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine)
val channel = BasicMessageChannel(
flutterEngine.dartExecutor.binaryMessenger,
"com.example.messagechannel/interop",
StringCodec.INSTANCE)
// Receive messages from Dart
channel.setMessageHandler { message, reply ->
Log.d("Android", "Received message = $message")
reply.reply("Reply from Android")
}
// Send message to Dart
Handler().postDelayed({
channel.send("Hello World from Android") { reply ->
Log.d("Android", "$reply")
}
}, 500)
}
}