前言
本文主要讲解,使用不同的 Channel 让 Flutter 和 Android原生 进行通信,由于只是讲解两端通信,所以可视化效果不好;
不过我写了一篇专门讲解 Flutter 嵌入 Android原生View的文章
Flutter 页面嵌入 Android原生 View-CSDN博客
可以先看完,再结合案例自己改动一下,做到可视化,不过我还是建议优先看当前文章。
我采用在Android原生延时发送方式,来模拟Android原生向Flutter 发送消息。
1、BasicMessageChannel
主要应用于传输数据的通道,目前提供了以下几种消息类型:
JSONMessageCodec、StandardMessageCodec、StringCodec、BinaryCodec;
默认使用的消息类型是 StandardMessageCodec。
下面图片是 Flutter和Android原生,分别支持的数据类型 和 对应关系;
1.1 JSONMessageCodec
Json字符串。
1.2 StandardMessageCodec
标准类型,可以看作默认类型,支持的类型也是最多的。
1.3 StringCodec
字符串。
1.4 BinaryCodec
二进制,比如传输文件。
我挑了两个最常用的类型进行演示,比如Flutter向Android原生发送请求,Android原生收到请求后将数据响应给Flutter;
BasicMessageChannel的监听回调,没有提供具体调用了哪个函数的标识,所以在演示PUT时,我会先将GET相关代码注释,反之也一样;
如果和其他Channel配合使用可以解决这个注释问题, 比如BasicMessageChannel 和 MethodChannel 一起使用,这个案例我放在最后讲解,这一阶段先麻烦一下;
传输 BinaryCodec,我使用的是音频文件,传输完成默认开始播放,友情提示,戴耳机或者声音小点,避免社S。
在Flutter添加音频播放库,库的具体地址
just_audio | Flutter Package
just_audio: ^0.9.36
Android原生 需要的配置,app/build.gradle
defaultConfig {
... ...
multiDexEnabled true
}
dependencies {
def exoplayer_version = "2.18.5"
implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version"
implementation "com.google.android.exoplayer:exoplayer-dash:$exoplayer_version"
implementation "com.google.android.exoplayer:exoplayer-hls:$exoplayer_version"
implementation "com.google.android.exoplayer:exoplayer-smoothstreaming:$exoplayer_version"
implementation 'androidx.multidex:multidex:2.0.1'
}
传输JSONMessageCodec:
传输BinaryCodec
1.5 传输JSONMessageCodec
Flutter:main_json_basic_message_channel.dart
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
/// BasicMessageChannel
/// 使用Map类型,对应 Android端的 JSONObject类型
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late BasicMessageChannel channel;
// Android原生View 在Flutter引擎上注册的唯一标识,在Flutter端使用时必须一样
static const String CHANNEL_NAME = 'flutter.mix.android/json_basic_message_channel';
String msgState = "默认"; // 消息传递状态
@override
initState() {
super.initState();
initChannel();
}
/// 初始化消息通道
initChannel() {
channel = const BasicMessageChannel(CHANNEL_NAME,JSONMessageCodec()); // 创建 Flutter端和Android端的,相互通信的通道
// 监听来自 Android端 的消息通道
// Android端调用了函数,这个handler函数就会被触发
channel.setMessageHandler(handler);
}
/// 监听来自 Android端 的消息通道
/// Android端调用了函数,这个handler函数就会被触发
Future<dynamic> handler(dynamic message) async {
// PUT
var androidCount = message['androidNum'];
msgState = 'Flutter端接收Android端PUT请求成功,数据:$androidCount';
setState(() {});
return 0; // 返回给Android端
// GET,这里模拟在Android端显示
// var randomV = getRandomV();
// Map<String, int> map = {'flutterNum': randomV};
// msgState = 'Flutter端接收Android端GET请求成功:$randomV';
// setState(() {});
// return map; // 返回给Android端
}
/// Flutter端 向 Android端 发送数据,PUT 操作
flutterSendAndroidData() {
var randomV = getRandomV();
Map<String, int> map = {'flutterNum': randomV};
// Android端调用Reply相关回调函数后,then、catchError 会接收到
channel.send(map).then((value) {
var flutterNum = value['flutterNum'];
msgState = 'Android端接收Flutter端PUT请求成功,数据:$flutterNum ----> 5秒后,Android端会向Flutter端发送PUT请求';
setState(() {});
}).catchError((e) {
if (e is MissingPluginException) {
debugPrint('flutterSendAndroidDataNotice --- Error:notImplemented --- 未找到Android端具体实现函数');
} else {
debugPrint('flutterSendAndroidDataNotice --- Error:$e');
}
});
}
/// Flutter端 获取 Android端 数据,GET 操作
flutterGetAndroidData() {
// Android端调用Reply相关回调函数后,then、catchError 会接收到
channel.send(null).then((value) {
var androidCount = value['androidNum'];
msgState = 'Android端接收Flutter端GET请求成功,数据:$androidCount ----> 5秒后,Android端会向Flutter端发送GET请求';
setState(() {});
}).catchError((e) {
if (e is MissingPluginException) {
debugPrint('flutterGetAndroidDataNotice --- Error:notImplemented --- 未找到Android端具体实现函数');
} else {
debugPrint('flutterGetAndroidDataNotice --- Error:$e');
}
});
}
/// 获取随机数
int getRandomV() {
return Random().nextInt(100); // 随机数范围(0-99)
}
@override
Widget build(BuildContext context) {
const defaultStyle = TextStyle(
fontSize: 16,
color: Colors.orangeAccent,
fontWeight: FontWeight.bold,
);
return Scaffold(
backgroundColor: Colors.blueGrey,
body: SafeArea(
top: true,
child: SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: 300,
child: Text(
msgState,
textAlign: TextAlign.center,
style: defaultStyle)
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: ElevatedButton(
onPressed: flutterSendAndroidData,
child: const Text('Flutter端向Android端发送数据'),
),
),
ElevatedButton(
onPressed: flutterGetAndroidData,
child: const Text('Flutter端获取Android端数据'),
),
],
),
),
));
}
}
Android原生:TestJsonBasicMessageChannel.kt
package com.example.flutter_android_channel.channel
import android.os.Handler
import android.os.Looper
import android.util.Log
import io.flutter.plugin.common.BasicMessageChannel
import io.flutter.plugin.common.BinaryCodec
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.JSONMessageCodec
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.StandardMessageCodec
import io.flutter.plugin.common.StringCodec
import org.json.JSONException
import org.json.JSONObject
/**
* BasicMessageChannel
*
* 使用 JSONObject类型,对应 Flutter端的 Map类型
*/
class TestJsonBasicMessageChannel(messenger: BinaryMessenger) :
BasicMessageChannel.MessageHandler<Any> {
private lateinit var mChannel: BasicMessageChannel<Any>
companion object {
// Android原生View 在Flutter引擎上注册的唯一标识,在Flutter端使用时必须一样
private const val CHANNEL_NAME = "flutter.mix.android/json_basic_message_channel"
}
init {
initChannel(messenger)
}
/**
* 初始化消息通道
*/
private fun initChannel(messenger: BinaryMessenger) {
// 创建 Android端和Flutter端的,相互通信的通道
// 通道名称,两端必须一致
mChannel = BasicMessageChannel(messenger, CHANNEL_NAME, JSONMessageCodec.INSTANCE)
// 监听来自 Flutter端 的消息通道
// Flutter端调用了函数,这个handler函数就会被触发
mChannel.setMessageHandler(this)
}
// ========================== PUT 操作 ==========================
/**
* 监听来自 Flutter端 的消息通道
*
* message: Android端 接收到 Flutter端 发来的 数据对象
* reply:Android端 给 Flutter端 执行回调的接口对象
*/
override fun onMessage(message: Any?, reply: BasicMessageChannel.Reply<Any>) {
// 回调结果对象
// 获取Flutter端传过来的数据
val flutterCount = getMap(message.toString())?.get("flutterNum")
Log.d("TAG", "flutterCount:$flutterCount")
// 回调状态接口对象,里面只有一个回调方法
// reply.reply(@Nullable T reply)
reply.reply(message) // 返回给Flutter端
Handler(Looper.getMainLooper()).postDelayed({
androidSendFlutterData()
}, 5000)
}
/**
* Android端 向 Flutter端 发送数据
*/
private fun androidSendFlutterData() {
val map: MutableMap<String, Int> = mutableMapOf<String, Int>()
map["androidNum"] = getRandomV() // 随机数范围(0-99)
mChannel.send(map, object : BasicMessageChannel.Reply<Any> {
override fun reply(reply: Any?) {
// 获取Flutter端传过来的数据
Log.d("TAG", "reply:$reply")
}
})
}
// ========================== GET 操作 ==========================
// /**
// * 监听来自 Flutter端 的消息通道
// *
// * message: Android端 接收到 Flutter端 发来的 数据对象
// * reply:Android端 给 Flutter端 执行回调的接口对象
// */
// override fun onMessage(message: Any?, reply: BasicMessageChannel.Reply<Any>) {
// val map: MutableMap<String, Int> = mutableMapOf<String, Int>()
// map["androidNum"] = getRandomV() // 随机数范围(0-99)
// reply.reply(map) // 返回给Flutter端
//
// Handler(Looper.getMainLooper()).postDelayed({
// androidGetFlutterData()
// }, 5000)
// }
//
// /**
// * Android端 获取 Flutter端 数据
// */
// private fun androidGetFlutterData() {
// mChannel.send(null, object : BasicMessageChannel.Reply<Any> {
//
// override fun reply(reply: Any?) {
// // 获取Flutter端传过来的数据
// val flutterCount = getMap(reply.toString())?.get("flutterNum")
// Log.d("TAG", "flutterCount:$flutterCount")
// }
//
// })
// }
/**
* 获取随机数
*/
private fun getRandomV() = (0..100).random()
/**
* 解除绑定
*/
fun closeChannel() {
mChannel.setMessageHandler(null)
}
/**
* Json 转 Map
*/
private fun getMap(jsonString: String?): HashMap<String, Any>? {
val jsonObject: JSONObject
try {
jsonObject = JSONObject(jsonString)
val keyIter: Iterator<String> = jsonObject.keys()
var key: String
var value: Any
var valueMap = HashMap<String, Any>()
while (keyIter.hasNext()) {
key = keyIter.next()
value = jsonObject[key] as Any
valueMap[key] = value
}
return valueMap
} catch (e: JSONException) {
e.printStackTrace()
}
return null
}
}
Android原生:MainActivity.kt
package com.example.flutter_android_channel
import com.example.flutter_android_channel.channel.TestJsonBasicMessageChannel
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
class MainActivity : FlutterActivity() {
private lateinit var testJsonBasicMessageChannel: TestJsonBasicMessageChannel
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
testJsonBasicMessageChannel = TestJsonBasicMessageChannel(flutterEngine.dartExecutor.binaryMessenger)
}
override fun onDestroy() {
super.onDestroy()
testJsonBasicMessageChannel.closeChannel()
}
}
1.6 传输BinaryCodec
Flutter:main_byte_basic_message_channel.dart
import 'package:just_audio/just_audio.dart';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
/// BasicMessageChannel
/// 使用ByteData类型,对应 Android端的 ByteBuffer类型
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late BasicMessageChannel channel;
// Android原生View 在Flutter引擎上注册的唯一标识,在Flutter端使用时必须一样
static const String CHANNEL_NAME = 'flutter.mix.android/byte_basic_message_channel';
String msgState = "默认"; // 消息传递状态
@override
initState() {
super.initState();
initChannel();
}
/// 初始化消息通道
initChannel() {
channel = const BasicMessageChannel(CHANNEL_NAME,BinaryCodec()); // 创建 Flutter端和Android端的,相互通信的通道
// 监听来自 Android端 的消息通道
// Android端调用了函数,这个handler函数就会被触发
channel.setMessageHandler(handler);
}
/// 监听来自 Android端 的消息通道
/// Android端调用了函数,这个handler函数就会被触发
Future<dynamic> handler(dynamic message) async {
// PUT
var data = message as ByteData;
loadMusic(data);
msgState = 'Flutter端接收Android端PUT请求成功,音频加载完毕,开始播放';
setState(() {});
return ByteData.view(Uint8List(0).buffer); // 返回给Android端
// GET,这里模拟在Android端播放音乐
// final data = await rootBundle.load('assets/music/di_jia_a.mp3');
// loadMusic(data);
// msgState = 'Flutter端接收Android端GET请求成功,音频加载完毕,开始播放';
// setState(() {});
// return data; // 返回给Android端
}
/// Flutter端 向 Android端 发送数据,PUT 操作
flutterSendAndroidData() async {
final byteData = await rootBundle.load('assets/music/di_jia_a.mp3');
// Android端调用Reply相关回调函数后,then、catchError 会接收到
channel.send(byteData).then((value) {
loadMusic(value);
msgState = 'Android端接收Flutter端PUT请求成功,音频加载完毕,开始播放 --- 5秒钟后 Android端会向Flutter端发送PUT请求';
setState(() {});
}).catchError((e) {
if (e is MissingPluginException) {
debugPrint('flutterSendAndroidDataNotice --- Error:notImplemented --- 未找到Android端具体实现函数');
} else {
debugPrint('flutterSendAndroidDataNotice --- Error:$e');
}
});
}
/// Flutter端 获取 Android端 数据,GET 操作
flutterGetAndroidData() {
// Android端调用Reply相关回调函数后,then、catchError 会接收到
channel.send(null).then((value) {
loadMusic(value);
msgState = 'Android端接收Flutter端GET请求成功,音频加载完毕,开始播放 --- 5秒钟后 Android端会向Flutter端发送GET请求';
setState(() {});
}).catchError((e) {
if (e is MissingPluginException) {
debugPrint('flutterGetAndroidDataNotice --- Error:notImplemented --- 未找到Android端具体实现函数');
} else {
debugPrint('flutterGetAndroidDataNotice --- Error:$e');
}
});
}
final player = AudioPlayer();
/// 加载音频
loadMusic(ByteData data) async {
var buffer = data.buffer;
var uint8list = buffer.asUint8List(data.offsetInBytes,data.lengthInBytes);
var audioSource = AudioSource.uri(Uri.dataFromBytes(uint8list));
await player.setAudioSource(audioSource);
player.play(); // 播放音乐
}
/// 播放或暂停
palsyOrPause() {
if(player.playing) {
player.pause();
}
}
@override
Widget build(BuildContext context) {
const defaultStyle = TextStyle(
fontSize: 16,
color: Colors.orangeAccent,
fontWeight: FontWeight.bold,
);
return Scaffold(
backgroundColor: Colors.blueGrey,
body: SafeArea(
top: true,
child: SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: 300,
child: Text(
msgState,
textAlign: TextAlign.center,
style: defaultStyle)
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: ElevatedButton(
onPressed: flutterSendAndroidData,
child: const Text('Flutter端向Android端发送数据'),
),
),
Padding(
padding: const EdgeInsets.only(bottom: 16),
child: ElevatedButton(
onPressed: flutterGetAndroidData,
child: const Text('Flutter端获取Android端数据'),
),
),
ElevatedButton(
onPressed: palsyOrPause,
child: Text('暂停'),
),
],
),
),
));
}
}
Android原生:TestByteBasicMessageChannel.kt
package com.example.flutter_android_channel.channel
import android.content.Context
import android.os.Handler
import android.os.Looper
import android.util.Log
import com.google.common.io.ByteStreams
import io.flutter.plugin.common.BasicMessageChannel
import io.flutter.plugin.common.BinaryCodec
import io.flutter.plugin.common.BinaryMessenger
import java.nio.ByteBuffer
import java.nio.ByteOrder
/**
* BasicMessageChannel
*
* 使用 ByteBuffer类型,对应 Flutter端的 ByteData类型
*/
class TestByteBasicMessageChannel(messenger: BinaryMessenger, private val context: Context) :
BasicMessageChannel.MessageHandler<ByteBuffer> {
private lateinit var mChannel: BasicMessageChannel<ByteBuffer>
companion object {
// Android原生View 在Flutter引擎上注册的唯一标识,在Flutter端使用时必须一样
private const val CHANNEL_NAME = "flutter.mix.android/byte_basic_message_channel"
}
init {
initChannel(messenger)
}
/**
* 初始化消息通道
*/
private fun initChannel(messenger: BinaryMessenger) {
// 创建 Android端和Flutter端的,相互通信的通道
// 通道名称,两端必须一致
mChannel = BasicMessageChannel(messenger, CHANNEL_NAME, BinaryCodec.INSTANCE)
// 监听来自 Flutter端 的消息通道
// Flutter端调用了函数,这个handler函数就会被触发
mChannel.setMessageHandler(this)
}
// ========================== PUT 操作 ==========================
/**
* 监听来自 Flutter端 的消息通道
*
* byteBuffer: Android端 接收到 Flutter端 发来的 数据对象
* reply:Android端 给 Flutter端 执行回调的接口对象
*/
override fun onMessage(byteBuffer: ByteBuffer?, reply: BasicMessageChannel.Reply<ByteBuffer>) {
// 回调结果对象
// 获取Flutter端传过来的数据
Log.d("TAG", "byteBuffer:$byteBuffer")
// 回调接口对象,里面只有一个回调方法
// reply.reply(@Nullable T reply)
byteBuffer?.order(ByteOrder.nativeOrder())
val direct = ByteBuffer.allocateDirect(byteBuffer!!.capacity())
direct.put(byteBuffer)
reply.reply(direct) // 返回给Flutter端
Handler(Looper.getMainLooper()).postDelayed({
androidSendFlutterData()
}, 5000)
}
/**
* Android端 向 Flutter端 发送数据
*/
private fun androidSendFlutterData() {
// 读取assert目录下的音频文件
val fileInputStream = context.assets.open("music/di_jia_b.mp3")
val targetArray = ByteStreams.toByteArray(fileInputStream)
val byteBuffer = ByteBuffer.wrap(targetArray)
byteBuffer.order(ByteOrder.nativeOrder())
val direct = ByteBuffer.allocateDirect(byteBuffer.capacity())
direct.put(byteBuffer)
mChannel.send(direct,object : BasicMessageChannel.Reply<ByteBuffer> {
override fun reply(reply: ByteBuffer?) {
Log.d("TAG", "reply:$reply")
}
})
}
// ========================== GET 操作 ==========================
// /**
// * 监听来自 Flutter端 的消息通道
// *
// * byteBuffer: Android端 接收到 Flutter端 发来的 数据对象
// * reply:Android端 给 Flutter端 执行回调的接口对象
// */
// override fun onMessage(byteBuffer: ByteBuffer?, reply: BasicMessageChannel.Reply<ByteBuffer>) {
// // 读取assert目录下的音频文件
// val fileInputStream = context.assets.open("music/di_jia_b.mp3")
// val targetArray = ByteStreams.toByteArray(fileInputStream)
// val byteBuffer = ByteBuffer.wrap(targetArray)
//
// byteBuffer.order(ByteOrder.nativeOrder())
// val direct = ByteBuffer.allocateDirect(byteBuffer.capacity())
// direct.put(byteBuffer)
// reply.reply(direct) // 返回给Flutter端
//
// Handler(Looper.getMainLooper()).postDelayed({
// androidGetFlutterData()
// }, 5000)
// }
//
// /**
// * Android端 获取 Flutter端 数据
// */
// private fun androidGetFlutterData() {
// mChannel.send(null,object : BasicMessageChannel.Reply<ByteBuffer> {
//
// override fun reply(reply: ByteBuffer?) {
// // 获取Flutter端传过来的数据
// Log.d("TAG", "reply:$reply")
// }
//
// })
// }
/**
* 解除绑定
*/
fun closeChannel() {
mChannel.setMessageHandler(null)
}
}
Android原生:MainActivity.kt
package com.example.flutter_android_channel
import com.example.flutter_android_channel.channel.TestByteBasicMessageChannel
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
class MainActivity : FlutterActivity() {
private lateinit var testByteBasicMessageChannel: TestByteBasicMessageChannel
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
testByteBasicMessageChannel = TestByteBasicMessageChannel(flutterEngine.dartExecutor.binaryMessenger,this)
}
override fun onDestroy() {
super.onDestroy()
testByteBasicMessageChannel.closeChannel()
}
}
2、MethodChannel
主要应用于Flutter和Android原生之间函数相互调用,所以它提供methodName作为具体调用函数的标识,就不用像演示 BasicMessageChannel那样,需要注释代码。
默认使用的消息类型是 StandardMessageCodec。
Flutter:main_method_channel.dart
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
/// MethodChannel
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late MethodChannel channel;
// Android原生View 在Flutter引擎上注册的唯一标识,在Flutter端使用时必须一样
static const String CHANNEL_NAME = 'flutter.mix.android/method_channel';
static const String FLUTTER_SEND_ANDROID_DATA_NOTICE = 'flutterSendAndroidDataNotice'; // Flutter端 向 Android端 发送数据
static const String FLUTTER_GET_ANDROID_DATA_NOTICE = 'flutterGetAndroidDataNotice'; // Flutter端 获取 Android端 数据
static const String ANDROID_SEND_FLUTTER_DATA_NOTICE = 'androidSendFlutterDataNotice'; // Android端 向 Flutter端 发送数据
static const String ANDROID_GET_FLUTTER_DATA_NOTICE = 'androidGetFlutterDataNotice'; // Android端 获取 Flutter端 数据
String msgState = "默认"; // 消息传递状态
@override
initState() {
super.initState();
initChannel();
}
/// 初始化消息通道
initChannel() {
channel = const MethodChannel(CHANNEL_NAME); // 创建 Flutter端和Android端的,相互通信的通道
// 监听来自 Android端 的消息通道
// Android端调用了函数,这个handler函数就会被触发
channel.setMethodCallHandler(handler);
}
/// 监听来自 Android端 的消息通道
/// Android端调用了函数,这个handler函数就会被触发
Future<dynamic> handler(MethodCall call) async {
// 获取调用函数的名称
final String methodName = call.method;
switch (methodName) {
case ANDROID_SEND_FLUTTER_DATA_NOTICE:
{
int androidCount = call.arguments['androidNum'];
msgState = 'Flutter端接收Android端PUT请求成功,数据:$androidCount';
setState(() {});
return '$ANDROID_SEND_FLUTTER_DATA_NOTICE ---> success'; // 返回给Android端
}
case ANDROID_GET_FLUTTER_DATA_NOTICE:
{
msgState = 'Flutter端接收Android端GET请求成功,返回数据:${getRandomV()}';
setState(() {});
return '$ANDROID_GET_FLUTTER_DATA_NOTICE ---> success:${getRandomV()}'; // 返回给Android端
}
default:
{
return PlatformException(code: '-1', message: '未找到Flutter端具体实现函数', details: '具体描述'); // 返回给Android端
}
}
}
/// Flutter端 向 Android端 发送数据,PUT 操作
flutterSendAndroidData() {
var randomV = getRandomV();
Map<String, int> map = {'flutterNum': randomV};
// Android端调用Result相关回调函数后,then、catchError 会接收到
channel.invokeMethod(FLUTTER_SEND_ANDROID_DATA_NOTICE, map).then((value) {
msgState = value;
setState(() {});
}).catchError((e) {
if (e is MissingPluginException) {
debugPrint('$FLUTTER_SEND_ANDROID_DATA_NOTICE --- Error:notImplemented --- 未找到Android端具体实现函数');
} else {
debugPrint('$FLUTTER_SEND_ANDROID_DATA_NOTICE --- Error:$e');
}
});
}
/// Flutter端 获取 Android端 数据,GET 操作
flutterGetAndroidData() {
// Android端调用Result相关回调函数后,then、catchError 会接收到
channel.invokeMethod(FLUTTER_GET_ANDROID_DATA_NOTICE).then((value) {
msgState = value;
setState(() {});
}).catchError((e) {
if (e is MissingPluginException) {
debugPrint('$FLUTTER_GET_ANDROID_DATA_NOTICE --- Error:notImplemented --- 未找到Android端具体实现函数');
} else {
debugPrint('$FLUTTER_GET_ANDROID_DATA_NOTICE --- Error:$e');
}
});
}
/// 获取随机数
int getRandomV() {
return Random().nextInt(100); // 随机数范围(0-99)
}
@override
Widget build(BuildContext context) {
const defaultStyle = TextStyle(
fontSize: 16,
color: Colors.orangeAccent,
fontWeight: FontWeight.bold,
);
return Scaffold(
backgroundColor: Colors.blueGrey,
body: SafeArea(
top: true,
child: SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: 300,
child: Text(
msgState,
textAlign: TextAlign.center,
style: defaultStyle)
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: ElevatedButton(
onPressed: flutterSendAndroidData,
child: const Text('Flutter端向Android端发送数据'),
),
),
ElevatedButton(
onPressed: flutterGetAndroidData,
child: const Text('Flutter端获取Android端数据'),
),
],
),
),
));
}
}
Android原生:TestMethodChannel.kt
package com.example.flutter_android_channel.channel
import android.os.Handler
import android.os.Looper
import android.util.Log
import androidx.annotation.NonNull
import androidx.annotation.Nullable
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import kotlin.random.Random
/**
* MethodChannel
*/
class TestMethodChannel(messenger: BinaryMessenger) : MethodChannel.MethodCallHandler {
private lateinit var mChannel: MethodChannel
companion object {
// Android原生View 在Flutter引擎上注册的唯一标识,在Flutter端使用时必须一样
private const val CHANNEL_NAME = "flutter.mix.android/method_channel"
private const val ANDROID_SEND_FLUTTER_DATA_NOTICE: String = "androidSendFlutterDataNotice" // Android端 向 Flutter端 发送数据
private const val ANDROID_GET_FLUTTER_DATA_NOTICE: String = "androidGetFlutterDataNotice" // Android端 获取 Flutter端 数据
private const val FLUTTER_SEND_ANDROID_DATA_NOTICE: String = "flutterSendAndroidDataNotice" // Flutter端 向 Android端 发送数据
private const val FLUTTER_GET_ANDROID_DATA_NOTICE: String = "flutterGetAndroidDataNotice" // Flutter端 获取 Android端 数据
}
init {
initChannel(messenger)
}
/**
* 初始化消息通道
*/
private fun initChannel(messenger: BinaryMessenger) {
// 创建 Android端和Flutter端的,相互通信的通道
// 通道名称,两端必须一致
mChannel = MethodChannel(messenger, CHANNEL_NAME)
// 监听来自 Flutter端 的消息通道
// Flutter端调用了函数,这个handler函数就会被触发
mChannel.setMethodCallHandler(this)
}
/**
* 监听来自 Flutter端 的消息通道
*
* call: Android端 接收到 Flutter端 发来的 数据对象
* result:Android端 给 Flutter端 执行回调的接口对象
*/
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
// 获取调用函数的名称
val methodName: String = call.method
when (methodName) {
FLUTTER_SEND_ANDROID_DATA_NOTICE -> {
// 回调结果对象
// 获取Flutter端传过来的数据
val flutterCount: Int? = call.argument<Int>("flutterNum")
result.success("Android端接收Flutter端PUT请求成功,数据:$flutterCount ----> 5秒后,Android端会向Flutter端发送PUT请求")
Handler(Looper.getMainLooper()).postDelayed({
androidSendFlutterData()
}, 5000)
// 回调状态接口对象,里面有三个回调方法,都可以给Flutter端返回消息
// result.success(result: Any?)
// result.error(errorCode: String, errorMessage: String?, errorDetails: Any?)
// result.notImplemented()
}
FLUTTER_GET_ANDROID_DATA_NOTICE -> {
result.success("Android端接收Flutter端GET请求成功,返回数据:${getRandomV()} ----> 5秒后,Android端会向Flutter端发送GET请求")
Handler(Looper.getMainLooper()).postDelayed({
androidGetFlutterData()
}, 5000)
}
else -> {
result.notImplemented()
}
}
}
/**
* Android端 向 Flutter端 发送数据,相当于 PUT 操作
*/
private fun androidSendFlutterData() {
val map: MutableMap<String, Int> = mutableMapOf<String, Int>()
map["androidNum"] = getRandomV() // 随机数范围(0-99)
mChannel.invokeMethod(ANDROID_SEND_FLUTTER_DATA_NOTICE, map, object : MethodChannel.Result {
override fun success(result: Any?) {
Log.d("TAG", "$result")
}
override fun error(
errorCode: String,
errorMessage: String?,
errorDetails: Any?
) {
Log.d(
"TAG", "errorCode:$errorCode --- errorMessage:$errorMessage --- errorDetails:$errorDetails"
)
}
/**
* Flutter端 未实现 Android端 定义的接口方法
*/
override fun notImplemented() {
Log.d("TAG", "notImplemented")
}
})
}
/**
* Android端 获取 Flutter端 数据,相当于 GET 操作
*/
private fun androidGetFlutterData() {
// 说一个坑,不传参数可以写null,
// 但不能这样写,目前它没有这个重载方法,invokeMethod第二个参数是Object类型,所以编译器不会提示错误
// mChannel.invokeMethod(ANDROID_GET_FLUTTER_DATA_NOTICE, object : MethodChannel.Result {
// public void invokeMethod(@NonNull String method, @Nullable Object arguments)
mChannel.invokeMethod(ANDROID_GET_FLUTTER_DATA_NOTICE, null, object : MethodChannel.Result {
override fun success(result: Any?) {
Log.d("TAG", "$result")
}
override fun error(
errorCode: String,
errorMessage: String?,
errorDetails: Any?
) {
Log.d(
"TAG", "errorCode:$errorCode --- errorMessage:$errorMessage --- errorDetails:$errorDetails"
)
}
/**
* Flutter端 未实现 Android端 定义的接口方法
*/
override fun notImplemented() {
Log.d("TAG", "notImplemented")
}
})
}
/**
* 获取随机数
*/
private fun getRandomV() = (0..100).random()
/**
* 解除绑定
*/
fun closeChannel() {
mChannel.setMethodCallHandler(null)
}
}
Android原生:MainActivity.kt
package com.example.flutter_android_channel
import com.example.flutter_android_channel.channel.TestMethodChannel
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
class MainActivity : FlutterActivity() {
TestByteBasicMessageChannel
private lateinit var testMethodChannel: TestMethodChannel
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
testMethodChannel = TestMethodChannel(flutterEngine.dartExecutor.binaryMessenger)
}
override fun onDestroy() {
super.onDestroy()
testMethodChannel.closeChannel()
}
}
3、EventChannel
主要应用于 原生端 向 Flutter 单向通信;
默认使用的消息类型是 StandardMessageCodec。
Flutter:main_single_event_channel.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
/// EventChannel
/// 使用方式:单向通信
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late EventChannel channel;
// Android原生View 在Flutter引擎上注册的唯一标识,在Flutter端使用时必须一样
static const String CHANNEL_NAME = 'flutter.mix.android/single_event_channel';
StreamSubscription? streamSubscription;
String msgState = "默认"; // 消息传递状态
@override
initState() {
super.initState();
initChannel();
}
/// 初始化消息通道
initChannel() {
channel = const EventChannel(CHANNEL_NAME); // 创建 Flutter端和Android端的,相互通信的通道
// 监听来自 Android端 的消息通道
// Android端调用了函数,这个handler函数就会被触发
streamSubscription = channel
.receiveBroadcastStream()
.listen(onData, onError: onError, onDone: onDone);
}
/// 监听来自 Android端 的消息通道
/// 这几个函数就会根据情况被触发
/// 响应数据
onData(dynamic data) {
msgState = data;
setState(() {});
}
/// 发生异常
onError(dynamic error) {
msgState = error;
setState(() {});
}
/// 流被关闭
onDone() {
msgState = "流被关闭";
setState(() {});
}
@override
void dispose() {
super.dispose();
streamSubscription?.cancel();
}
@override
Widget build(BuildContext context) {
const defaultStyle = TextStyle(
fontSize: 50,
color: Colors.orangeAccent,
fontWeight: FontWeight.bold,
);
return Scaffold(
backgroundColor: Colors.blueGrey,
body: SafeArea(
top: true,
child: SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: 300,
child: Text(msgState,
textAlign: TextAlign.center, style: defaultStyle)),
],
),
),
));
}
}
Android原生:TestSingleEventChannel.kt
package com.example.flutter_android_channel.channel
import android.os.CountDownTimer
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.EventChannel
/**
* EventChannel
*
* 使用方式:单向通信
*/
class TestSingleEventChannel(messenger: BinaryMessenger) : EventChannel.StreamHandler {
private lateinit var mChannel: EventChannel
companion object {
// Android原生View 在Flutter引擎上注册的唯一标识,在Flutter端使用时必须一样
private const val CHANNEL_NAME = "flutter.mix.android/single_event_channel"
}
init {
initChannel(messenger)
}
/**
* 初始化消息通道
*/
private fun initChannel(messenger: BinaryMessenger) {
// 创建 Android端和Flutter端的,相互通信的通道
// 通道名称,两端必须一致
mChannel = EventChannel(messenger, CHANNEL_NAME)
// 监听来自 Flutter端 的消息通道
// Flutter端调用了函数,这个handler函数就会被触发
mChannel.setStreamHandler(this)
}
private var count: Int = 10
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
// 一共10秒,每隔1秒执行一次
object : CountDownTimer(10000, 1000) {
override fun onTick(millisUntilFinished: Long) {
// 还剩下多少秒,依次为2000、1000、0
if (millisUntilFinished == 0L) {
cancel()
}
events?.success("${count--}")
}
override fun onFinish() { // 结束后的操作
events?.success("${count--}")
}
}.start()
// 给Flutter端返回消息
// events?.endOfStream()
// events?.success(event: Any?)
// events?.error(errorCode: String?, errorMessage: String?, errorDetails: Any?)
// events?.endOfStream() // 流结束
}
override fun onCancel(arguments: Any?) {
}
/**
* 解除绑定
*/
fun closeChannel() {
mChannel.setStreamHandler(null)
}
}
Android原生:MainActivity.kt
package com.example.flutter_android_channel
import com.example.flutter_android_channel.channel.TestSingleEventChannel
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
class MainActivity : FlutterActivity() {
private lateinit var testSingleEventChannel : TestSingleEventChannel
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
testSingleEventChannel = TestSingleEventChannel(flutterEngine.dartExecutor.binaryMessenger)
}
override fun onDestroy() {
super.onDestroy()
testSingleEventChannel.closeChannel()
}
}
怀疑
- 它用到Stream,流在开发中,代表一连串数据,主要应用在传输文件,比如IO流;
- EventChannel.EventSink,它提供的那些函数名,一看就是返回操作,既然是返回,就需要前置条件,比如接收到请求;
- BinaryMessenger.send(String channel, ByteData? message),我找到了方法,传输的数据类型是ByteData,结合前两项,逻辑通了,那是否意味着Flutter可以使用EventChannel主动向原生端发送请求?
1.0 撸起袖子,开始干
Flutter一切正常,成功发送数据,且Android原生也成功接收到数据,但遇到了解析异常;
17:53:44.737 E Uncaught exception in binary message listener
java.lang.IllegalArgumentException: Message corrupted
at io.flutter.plugin.common.StandardMessageCodec.readValueOfType(StandardMessageCodec.java:450)
at io.flutter.plugin.common.StandardMessageCodec.readValue(StandardMessageCodec.java:340)
at io.flutter.plugin.common.StandardMethodCodec.decodeMethodCall(StandardMethodCodec.java:48)
at io.flutter.plugin.common.EventChannel$IncomingStreamRequestHandler.onMessage(EventChannel.java:195)
at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:295)
at io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0$io-flutter-embedding-engine-dart-DartMessenger(DartMessenger.java:322)
at io.flutter.embedding.engine.dart.DartMessenger$$ExternalSyntheticLambda0.run(Unknown Source:12)
at android.os.Handler.handleCallback(Handler.java:942)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7872)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
1.1 异常分析
这个异常是在原生端触发的,默认使用的消息类型是 StandardMessageCodec,它的父类是MethodCodec抽象类,目前没有可以解析 ByteBuffer的子类,这就很矛盾,发送成功,接收成功,但不支持解析,于是我抱着侥幸的心态,跑去问官方,看看是不是我使用的方式不对。
StandardMessageCodec.java 中的数据解析方法;
@Nullable
protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) {
final Object result;
switch (type) {
case NULL:
result = null;
break;
case TRUE:
result = true;
break;
case FALSE:
result = false;
break;
case INT:
result = buffer.getInt();
break;
case LONG:
result = buffer.getLong();
break;
case BIGINT:
{
final byte[] hex = readBytes(buffer);
result = new BigInteger(new String(hex, UTF8), 16);
break;
}
case DOUBLE:
readAlignment(buffer, 8);
result = buffer.getDouble();
break;
case STRING:
{
final byte[] bytes = readBytes(buffer);
result = new String(bytes, UTF8);
break;
}
case BYTE_ARRAY:
{
result = readBytes(buffer);
break;
}
case INT_ARRAY:
{
final int length = readSize(buffer);
final int[] array = new int[length];
readAlignment(buffer, 4);
buffer.asIntBuffer().get(array);
result = array;
buffer.position(buffer.position() + 4 * length);
break;
}
case LONG_ARRAY:
{
final int length = readSize(buffer);
final long[] array = new long[length];
readAlignment(buffer, 8);
buffer.asLongBuffer().get(array);
result = array;
buffer.position(buffer.position() + 8 * length);
break;
}
case DOUBLE_ARRAY:
{
final int length = readSize(buffer);
final double[] array = new double[length];
readAlignment(buffer, 8);
buffer.asDoubleBuffer().get(array);
result = array;
buffer.position(buffer.position() + 8 * length);
break;
}
case LIST:
{
final int size = readSize(buffer);
final List<Object> list = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
list.add(readValue(buffer));
}
result = list;
break;
}
case MAP:
{
final int size = readSize(buffer);
final Map<Object, Object> map = new HashMap<>();
for (int i = 0; i < size; i++) {
map.put(readValue(buffer), readValue(buffer));
}
result = map;
break;
}
case FLOAT_ARRAY:
{
final int length = readSize(buffer);
final float[] array = new float[length];
readAlignment(buffer, 4);
buffer.asFloatBuffer().get(array);
result = array;
buffer.position(buffer.position() + 4 * length);
break;
}
default:
throw new IllegalArgumentException("Message corrupted");
}
return result;
}
1.2 Flutter官方回复
这是Git问题地址:https://github.com/flutter/flutter/issues/141876
这是回复,我翻译成中文。
问题:那么EventChannel只能用于与flutter进行原生单向通信吗?
答复:是的。
问题:如果是这样,为什么Flutter EventChannel提供了channel.binaryMessenger.send方法和channel.binaryMessenger.setMessageHandler监听
答复:事实并非如此; 这些是 BinaryMessenger 上的方法。 BinaryMessenger 是 EventChannel 构建的较低级别构造,这就是它采用二进制信使作为构造函数参数的原因。
问题:还是我的使用方式有问题?
答复:是的。 您试图绕过 EventChannel 抽象并发送您自己的原始二进制消息,这些消息不符合 EventChannel 使用二进制消息传递程序的方式,因此您收到的崩溃是预期的结果。
如果您想要发送自己的原始二进制消息,那么您需要使用自己的通道,而不是 EventChannel。
好了,不用纠结了,官方实锤 EventChannel 只能由 原生端 向 Flutter 单向通信。
别的不说,这官方回复问题的效率是真高。
Flutter:main_bilateral_event_channel.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
/// EventChannel
/// 使用方式:尝试双向通信,但失败
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late EventChannel channel;
// Android原生View 在Flutter引擎上注册的唯一标识,在Flutter端使用时必须一样
static const String CHANNEL_NAME = 'flutter.mix.android/bilateral_event_channel';
StreamSubscription? streamSubscription;
String msgState = "默认"; // 消息传递状态
@override
initState() {
super.initState();
initChannel();
}
/// 初始化消息通道
initChannel() {
// channel = EventChannel(CHANNEL_NAME, CustomizeStandardMethodCodec());
channel = const EventChannel(CHANNEL_NAME); // 创建 Flutter端和Android端的,相互通信的通道
// 监听来自 Android端 的消息通道
// Android端调用了函数,这个handler函数就会被触发
streamSubscription = channel
.receiveBroadcastStream()
.listen(onData, onError: onError, onDone: onDone);
}
/// 监听来自 Android端 的消息通道
/// 这几个函数就会根据情况被触发
/// 响应数据
onData(dynamic data) {
msgState = data;
setState(() {});
}
/// 发生异常
onError(dynamic error) {
msgState = error;
setState(() {});
}
/// 流被关闭
onDone() {
msgState = "流被关闭";
setState(() {});
}
/// Flutter端 向 Android端 发送数据,相当于 PUT 操作
flutterSendAndroidData() async {
final byteData = await rootBundle.load('assets/music/di_jia_a.mp3');
channel.binaryMessenger.send(CHANNEL_NAME, byteData);
}
@override
void dispose() {
super.dispose();
streamSubscription?.cancel();
}
@override
Widget build(BuildContext context) {
const defaultStyle = TextStyle(
fontSize: 30,
color: Colors.orangeAccent,
fontWeight: FontWeight.bold,
);
return Scaffold(
backgroundColor: Colors.blueGrey,
body: SafeArea(
top: true,
child: SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 16),
child: SizedBox(
width: 300,
child: Text(msgState,
textAlign: TextAlign.center, style: defaultStyle)),
),
ElevatedButton(
onPressed: flutterSendAndroidData,
child: const Text('发送',style: TextStyle(fontSize: 20)),
)
],
),
),
));
}
}
Android原生:TestBilateralEventChannel.kt
package com.example.flutter_android_channel.channel
import android.util.Log
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.EventChannel
/**
* EventChannel
*
* 使用方式:尝试双向通信,但失败
*/
class TestBilateralEventChannel(messenger: BinaryMessenger) : EventChannel.StreamHandler {
private lateinit var mChannel: EventChannel
companion object {
// Android原生View 在Flutter引擎上注册的唯一标识,在Flutter端使用时必须一样
private const val CHANNEL_NAME = "flutter.mix.android/bilateral_event_channel"
}
init {
initChannel(messenger)
}
/**
* 初始化消息通道
*/
private fun initChannel(messenger: BinaryMessenger) {
// 创建 Android端和Flutter端的,相互通信的通道
// 通道名称,两端必须一致
mChannel = EventChannel(messenger, CHANNEL_NAME)
// 监听来自 Flutter端 的消息通道
// Flutter端调用了函数,这个handler函数就会被触发
mChannel.setStreamHandler(this)
}
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
Log.d("TAG","arguments:$arguments")
}
override fun onCancel(arguments: Any?) {
}
/**
* 解除绑定
*/
fun closeChannel() {
mChannel.setStreamHandler(null)
}
}
Android原生:MainActivity.kt
package com.example.flutter_android_channel
import com.example.flutter_android_channel.channel.TestBilateralEventChannel
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
class MainActivity : FlutterActivity() {
private lateinit var testBilateralEventChannel: TestBilateralEventChannel
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
testBilateralEventChannel = TestBilateralEventChannel(flutterEngine.dartExecutor.binaryMessenger)
}
override fun onDestroy() {
super.onDestroy()
testBilateralEventChannel.closeChannel()
}
}
4、不同的Channel配合使用
不同的Channel针对不同的应用场景,实际开发中,根据业务需求会相互配合使用;
BasicMessageChannel 和 MethodChannel 一起配合使用,使用BasicMessageChannel 传输数据,传输完成后,调用 MethodChannel 向Flutter发送消息。
Flutter:main_mix_use_channel.dart
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
/// BasicMessageChannel + MethodChannel 一起配合使用
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late BasicMessageChannel mBasicMessageChannel;
late MethodChannel mMethodChannel;
// Android原生View 在Flutter引擎上注册的唯一标识,在Flutter端使用时必须一样
static const String MIX_BASIC_MESSAGE_CHANNEL_NAME = 'flutter.mix.android/mix_json_basic_message_channel';
static const String MIX_METHOD_CHANNEL_NAME = 'flutter.mix.android/mix_method_channel';
static const String ANDROID_SEND_FLUTTER_DATA_NOTICE = 'androidSendFlutterDataNotice'; // Android端 向 Flutter端 发送数据
String msgState = "默认"; // 消息传递状态
@override
initState() {
super.initState();
initChannel();
}
/// 初始化消息通道
initChannel() {
// 创建 Flutter端和Android端的,相互通信的通道
mBasicMessageChannel = const BasicMessageChannel(MIX_BASIC_MESSAGE_CHANNEL_NAME,JSONMessageCodec());
mMethodChannel = const MethodChannel(MIX_METHOD_CHANNEL_NAME);
// 监听来自 Android端 的消息通道
// Android端调用了函数,这个handler函数就会被触发
mBasicMessageChannel.setMessageHandler(messageHandler);
mMethodChannel.setMethodCallHandler(methodHandler);
}
/// 监听来自 Android端 的 BasicMessageChannel 消息通道
/// Android端调用了函数,这个handler函数就会被触发
Future<dynamic> messageHandler(dynamic message) async {}
/// 监听来自 Android端 的 MethodChannel 消息通道
/// Android端调用了函数,这个handler函数就会被触发
Future<dynamic> methodHandler(MethodCall call) async {
// 获取调用函数的名称
final String methodName = call.method;
switch (methodName) {
case ANDROID_SEND_FLUTTER_DATA_NOTICE:
{
int androidCount = call.arguments['androidNum'];
msgState = 'Flutter端接收Android端请求成功,数据:$androidCount';
setState(() {});
return '$ANDROID_SEND_FLUTTER_DATA_NOTICE ---> success'; // 返回给Android端
}
default:
{
return PlatformException(code: '-1', message: '未找到Flutter端具体实现函数', details: '具体描述'); // 返回给Android端
}
}
}
/// Flutter端 向 Android端 发送数据,PUT 操作
flutterSendAndroidData() {
var randomV = getRandomV();
Map<String, int> map = {'flutterNum': randomV};
// Android端调用Reply相关回调函数后,then、catchError 会接收到
mBasicMessageChannel.send(map).then((value) {
var flutterNum = value['flutterNum'];
msgState = 'Android端接收Flutter端请求成功,数据:$flutterNum ----> 5秒后,Android端会向Flutter端发送请求';
setState(() {});
}).catchError((e) {
if (e is MissingPluginException) {
debugPrint('flutterSendAndroidDataNotice --- Error:notImplemented --- 未找到Android端具体实现函数');
} else {
debugPrint('flutterSendAndroidDataNotice --- Error:$e');
}
});
}
/// 获取随机数
int getRandomV() {
return Random().nextInt(100); // 随机数范围(0-99)
}
@override
Widget build(BuildContext context) {
const defaultStyle = TextStyle(
fontSize: 16,
color: Colors.orangeAccent,
fontWeight: FontWeight.bold,
);
return Scaffold(
backgroundColor: Colors.blueGrey,
body: SafeArea(
top: true,
child: SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: 300,
child: Text(
msgState,
textAlign: TextAlign.center,
style: defaultStyle)
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: ElevatedButton(
onPressed: flutterSendAndroidData,
child: const Text('Flutter端向Android端发送数据'),
),
)
],
),
),
));
}
}
Android原生:TestMixUseChannel.kt
package com.example.flutter_android_channel.channel
import android.os.Handler
import android.os.Looper
import android.util.Log
import io.flutter.plugin.common.BasicMessageChannel
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.JSONMessageCodec
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import org.json.JSONException
import org.json.JSONObject
/**
* BasicMessageChannel + MethodChannel 一起配合使用
*
*/
class TestMixUseChannel(messenger: BinaryMessenger) : BasicMessageChannel.MessageHandler<Any>,MethodChannel.MethodCallHandler {
private lateinit var mBasicMessageChannel: BasicMessageChannel<Any>
private lateinit var mMethodChannel: MethodChannel
companion object {
// Android原生View 在Flutter引擎上注册的唯一标识,在Flutter端使用时必须一样
private const val MIX_BASIC_MESSAGE_CHANNEL_NAME = "flutter.mix.android/mix_json_basic_message_channel"
private const val MIX_METHOD_CHANNEL_NAME = "flutter.mix.android/mix_method_channel"
private const val ANDROID_SEND_FLUTTER_DATA_NOTICE: String = "androidSendFlutterDataNotice" // Android端 向 Flutter端 发送数据
}
init {
initChannel(messenger)
}
/**
* 初始化消息通道
*/
private fun initChannel(messenger: BinaryMessenger) {
// 创建 Android端和Flutter端的,相互通信的通道
// 通道名称,两端必须一致
mBasicMessageChannel = BasicMessageChannel(messenger, MIX_BASIC_MESSAGE_CHANNEL_NAME, JSONMessageCodec.INSTANCE)
mMethodChannel = MethodChannel(messenger, MIX_METHOD_CHANNEL_NAME)
// 监听来自 Flutter端 的消息通道
// Flutter端调用了函数,这个handler函数就会被触发
mBasicMessageChannel.setMessageHandler(this)
mMethodChannel.setMethodCallHandler(this)
}
/**
* 监听来自 Flutter端 的 BasicMessageChannel 消息通道
*
* message: Android端 接收到 Flutter端 发来的 数据对象
* reply:Android端 给 Flutter端 执行回调的接口对象
*/
override fun onMessage(message: Any?, reply: BasicMessageChannel.Reply<Any>) {
// 回调结果对象
// 获取Flutter端传过来的数据
val flutterCount = getMap(message.toString())?.get("flutterNum")
Log.d("TAG", "flutterCount:$flutterCount")
// 回调状态接口对象,里面只有一个回调方法
// reply.reply(@Nullable T reply)
reply.reply(message) // 返回给Flutter端
Handler(Looper.getMainLooper()).postDelayed({
androidSendFlutterData()
}, 5000)
}
/**
* 监听来自 Flutter端 的 MethodChannel 消息通道
*
* call: Android端 接收到 Flutter端 发来的 数据对象
* result:Android端 给 Flutter端 执行回调的接口对象
*/
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {}
/**
* Android端 向 Flutter端 发送数据,相当于 PUT 操作
*/
private fun androidSendFlutterData() {
val map: MutableMap<String, Int> = mutableMapOf<String, Int>()
map["androidNum"] = getRandomV() // 随机数范围(0-99)
mMethodChannel.invokeMethod(ANDROID_SEND_FLUTTER_DATA_NOTICE, map, object : MethodChannel.Result {
override fun success(result: Any?) {
Log.d("TAG", "$result")
}
override fun error(
errorCode: String,
errorMessage: String?,
errorDetails: Any?
) {
Log.d(
"TAG", "errorCode:$errorCode --- errorMessage:$errorMessage --- errorDetails:$errorDetails"
)
}
/**
* Flutter端 未实现 Android端 定义的接口方法
*/
override fun notImplemented() {
Log.d("TAG", "notImplemented")
}
})
}
/**
* 解除绑定
*/
fun closeChannel() {
mBasicMessageChannel.setMessageHandler(null)
mMethodChannel.setMethodCallHandler(null)
}
/**
* 获取随机数
*/
private fun getRandomV() = (0..100).random()
/**
* Json 转 Map
*/
private fun getMap(jsonString: String?): HashMap<String, Any>? {
val jsonObject: JSONObject
try {
jsonObject = JSONObject(jsonString)
val keyIter: Iterator<String> = jsonObject.keys()
var key: String
var value: Any
var valueMap = HashMap<String, Any>()
while (keyIter.hasNext()) {
key = keyIter.next()
value = jsonObject[key] as Any
valueMap[key] = value
}
return valueMap
} catch (e: JSONException) {
e.printStackTrace()
}
return null
}
}
Android原生:MainActivity.kt
package com.example.flutter_android_channel
import com.example.flutter_android_channel.channel.TestMixUseChannel
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
class MainActivity : FlutterActivity() {
private lateinit var testMixUseChannel: TestMixUseChannel
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
testMixUseChannel = TestMixUseChannel(flutterEngine.dartExecutor.binaryMessenger)
}
override fun onDestroy() {
super.onDestroy()
testMixUseChannel.closeChannel()
}
}
5、执行线程
都试了一遍,目前这些Channel都只能在主线程执行。
// Android原生
// 测试在子线程执行
Thread {
androidSendFlutterData()
}.start()
Process: com.example.flutter_android_channel, PID: 5776
java.lang.RuntimeException: Methods marked with @UiThread must be executed on the main thread. Current thread: Thread-4
at io.flutter.embedding.engine.FlutterJNI.ensureRunningOnMainThread(FlutterJNI.java:1496)
at io.flutter.embedding.engine.FlutterJNI.dispatchPlatformMessage(FlutterJNI.java:1103)
at io.flutter.embedding.engine.dart.DartMessenger.send(DartMessenger.java:282)
at io.flutter.embedding.engine.dart.DartExecutor$DefaultBinaryMessenger.send(DartExecutor.java:470)
at io.flutter.plugin.common.BasicMessageChannel.send(BasicMessageChannel.java:105)
at com.example.flutter_android_channel.channel.TestJsonBasicMessageChannel.androidSendFlutterData(TestJsonBasicMessageChannel.kt:80)
at com.example.flutter_android_channel.channel.TestJsonBasicMessageChannel.onMessage$lambda-1$lambda-0(TestJsonBasicMessageChannel.kt:68)
at com.example.flutter_android_channel.channel.TestJsonBasicMessageChannel.$r8$lambda$vHbhJxL-HBJ37W1nuB2sJQfndKs(Unknown Source:0)
at com.example.flutter_android_channel.channel.TestJsonBasicMessageChannel$$ExternalSyntheticLambda1.run(Unknown Source:2)
6、总结
BasicMessageChannel主要应用于:传输数据;
MethodChannel主要应用于:通过函数处理业务逻辑;
EventChannel主要应用于:一些只能由原生端API才能完成的操作,处理完后发送给Flutter;
双向通信:BasicMessageChannel 和 MethodChannel;
单向通信:EventChannel。
7、源码地址
https://github.com/LanSeLianMa/flutter_android_channel