Flutter 与 Android原生 相互通信:BasicMessageChannel、MethodChannel、EventChannel

前言

本文主要讲解,使用不同的 Channel 让 Flutter 和 Android原生 进行通信,由于只是讲解两端通信,所以可视化效果不好

不过我写了一篇专门讲解 Flutter 嵌入 Android原生View的文章

Flutter 页面嵌入 Android原生 View-CSDN博客

可以先看完,再结合案例自己改动一下,做到可视化,不过我还是建议优先看当前文章

我采用在Android原生延时发送方式,来模拟Android原生向Flutter 发送消息。

1、BasicMessageChannel

主要应用于传输数据的通道,目前提供了以下几种消息类型:

JSONMessageCodecStandardMessageCodecStringCodecBinaryCodec

默认使用的消息类型是 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});@overrideWidget 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;@overrideState<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 = "默认"; // 消息传递状态@overrideinitState() {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 {// PUTvar 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)}@overrideWidget 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.channelimport 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: JSONObjecttry {jsonObject = JSONObject(jsonString)val keyIter: Iterator<String> = jsonObject.keys()var key: Stringvar value: Anyvar valueMap = HashMap<String, Any>()while (keyIter.hasNext()) {key = keyIter.next()value = jsonObject[key] as AnyvalueMap[key] = value}return valueMap} catch (e: JSONException) {e.printStackTrace()}return null}
}

Android原生:MainActivity.kt

package com.example.flutter_android_channelimport com.example.flutter_android_channel.channel.TestJsonBasicMessageChannel
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngineclass MainActivity : FlutterActivity() {private lateinit var testJsonBasicMessageChannel: TestJsonBasicMessageChanneloverride 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});@overrideWidget 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;@overrideState<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 = "默认"; // 消息传递状态@overrideinitState() {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 {// PUTvar 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();}}@overrideWidget 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.channelimport 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_channelimport com.example.flutter_android_channel.channel.TestByteBasicMessageChannel
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngineclass MainActivity : FlutterActivity() {private lateinit var testByteBasicMessageChannel: TestByteBasicMessageChanneloverride 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});@overrideWidget 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;@overrideState<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 = "默认"; // 消息传递状态@overrideinitState() {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)}@overrideWidget 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.channelimport 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: MethodChannelcompanion 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.methodwhen (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_channelimport com.example.flutter_android_channel.channel.TestMethodChannel
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngineclass MainActivity : FlutterActivity() {TestByteBasicMessageChannelprivate lateinit var testMethodChannel: TestMethodChanneloverride 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});@overrideWidget 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;@overrideState<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 = "默认"; // 消息传递状态@overrideinitState() {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(() {});}@overridevoid dispose() {super.dispose();streamSubscription?.cancel();}@overrideWidget 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.channelimport 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: EventChannelcompanion 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 = 10override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {// 一共10秒,每隔1秒执行一次object : CountDownTimer(10000, 1000) {override fun onTick(millisUntilFinished: Long) {// 还剩下多少秒,依次为2000、1000、0if (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_channelimport com.example.flutter_android_channel.channel.TestSingleEventChannel
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngineclass MainActivity : FlutterActivity() {private lateinit var testSingleEventChannel : TestSingleEventChanneloverride 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 listenerjava.lang.IllegalArgumentException: Message corruptedat 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 中的数据解析方法;

 @Nullableprotected 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});@overrideWidget 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;@overrideState<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 = "默认"; // 消息传递状态@overrideinitState() {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);}@overridevoid dispose() {super.dispose();streamSubscription?.cancel();}@overrideWidget 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.channelimport 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: EventChannelcompanion 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_channelimport com.example.flutter_android_channel.channel.TestBilateralEventChannel
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngineclass MainActivity : FlutterActivity() {private lateinit var testBilateralEventChannel: TestBilateralEventChanneloverride 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});@overrideWidget 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;@overrideState<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 = "默认"; // 消息传递状态@overrideinitState() {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)}@overrideWidget 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.channelimport 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: MethodChannelcompanion 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: JSONObjecttry {jsonObject = JSONObject(jsonString)val keyIter: Iterator<String> = jsonObject.keys()var key: Stringvar value: Anyvar valueMap = HashMap<String, Any>()while (keyIter.hasNext()) {key = keyIter.next()value = jsonObject[key] as AnyvalueMap[key] = value}return valueMap} catch (e: JSONException) {e.printStackTrace()}return null}}

Android原生:MainActivity.kt

package com.example.flutter_android_channelimport com.example.flutter_android_channel.channel.TestMixUseChannelimport io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngineclass MainActivity : FlutterActivity() {private lateinit var testMixUseChannel: TestMixUseChanneloverride 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: 5776java.lang.RuntimeException: Methods marked with @UiThread must be executed on the main thread. Current thread: Thread-4at 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;

双向通信:BasicMessageChannelMethodChannel

单向通信:EventChannel

7、源码地址

https://github.com/LanSeLianMa/flutter_android_channel

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/637509.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小程序使用echarts图表-雷达图

本文介绍下小程序中如何使用echarts 如果是通过npm安装&#xff0c;这样是全部安装的&#xff0c;体积有点大 我这边是使用echarts中的一个组件来实现的&#xff0c;下边是具体流程&#xff0c;实际效果是没有外边的红色边框的&#xff0c;加红色边框的效果是这篇说明 1.echa…

IDEA的database使用

一、数据据库 在使用database之前&#xff0c;首先你的电脑要安装好了数据库并且启动。 MySQL卸载手册 链接&#xff1a;https://pan.baidu.com/doc/share/AVXW5SG6T76puBOWnPegmw-602323264797863 提取码&#xff1a;hlgf MySQL安装图解 链接&#xff1a;https://pan.baidu.…

机器学习笔记——机器学习的分类

1 机器学习是啥 机器学习是人工智能的一个分支&#xff0c;它是一门研究机器获取新知识和新技能&#xff0c;并识别现有知识的学问。 机器学习已广泛应用于数据挖掘、计算机视觉、自然语言处理、生物特征识别、搜索引擎、医学诊断、检测信用卡欺诈、证券市场分析、DNA 序列测…

用Python实现Excel中的Vlookup功能

目录 一、引言 二、准备工作 三、实现Vlookup功能 1、导入pandas库 2、准备数据 3、实现Vlookup功能 4、处理结果 5、保存结果 四、完整代码示例 五、注意事项 六、总结 一、引言 在Excel中&#xff0c;Vlookup是一个非常实用的函数&#xff0c;它可以帮助我们在表…

Web概述

Web 概述&#xff1a;Web是World Wide Web的简称&#xff0c;是一个由许多互联网服务组成的信息空间。它由超文本文档、图像、视频和其他多媒体资源组成&#xff0c;并通过超文本传输协议&#xff08;HTTP&#xff09;进行传输。特点&#xff1a;Web的主要特点是其开放性和可访…

java数据结构与算法刷题-----LeetCode485. 最大连续 1 的个数

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 1. 法一&#xff0c;双指针2. 法二&#xff1a;变量计数 1. 法一…

面对不平衡二元分类问题是否需要使用SMOTE技术?

摘要 在训练分类模型之前平衡数据是解决表格数据中不平衡二元分类任务的流行技术。平衡通常是通过复制少数样本或生成合成少数样本来实现的。虽然众所周知&#xff0c;平衡对每个分类模型的影响不同&#xff0c;但大多数先进的实证研究并未将强大的最先进&#xff08;SOTA&…

Qt6入门教程 8:信号和槽机制(连接方式)

目录 一.一个信号与槽连接的例子 二.第五个参数 1.Qt::AutoConnection 2.Qt::DirectConnection 3.Qt::QueuedConnection 4.Qt::BlockingQueuedConnection 5.Qt::UniqueConnection 三.信号 四.connect函数原型 五.信号与槽的多种用法 六.槽的属性 一.一个信号与槽连接…

R语言【cli】——builtin_theme():内置的CLI主题

Package cli version 3.6.0 Description 此主题始终处于活动状态&#xff0c;并且位于主题堆栈的底部。 Usage builtin_theme(dark getOption("cli.theme_dark", "auto")) Argument 参数【dark】&#xff1a;是否使用黑暗主题。cli.theme_dark选项可用…

3d渲染软件有哪些?3d云渲染推荐

目前市面上的3D渲染软件非常多&#xff0c;不同的建模软件都有自己的渲染方式&#xff0c;根据所处行业的不同和项目需要&#xff0c;设计师可以选择不同的软件帮助展示最终效果。 主流的渲染软件有&#xff1a;VRay和Corona&#xff1a;一般用于室内效果图渲染&#xff0c;与3…

go 语言爬虫库goquery介绍

文章目录 爬虫介绍goquery介绍利用NewDocumentFromReader方法获取主页信息Document介绍通过查询获取文章信息css选择器介绍goquery中的选择器获取主页中的文章链接 爬取总结 爬虫介绍 爬虫&#xff0c;又称网页抓取、网络蜘蛛或网络爬虫&#xff0c;是一种自动浏览互联网并从网…

chapter10-让你拥有“火眼金睛”的 Fiddr4 和其他工具

在前面的课程中&#xff0c;我们通过一个简单的天气预报服务&#xff0c;拓展了如何使用邮件、短信&#xff0c;以及部署在服务器上&#xff0c;完整的开发了一款可以正式使用的小程序。但是有的同学可能也会产生抱怨&#xff1a;这门课不是是爬虫入门吗&#xff1f;为什么讲的…

如何在Servlet中获取请求参数的值

看看这个大佬做的动图吧&#xff01; 在Servlet中&#xff0c;你可以使用HttpServletRequest对象来获取请求参数的值。HttpServletRequest对象提供了一些方法&#xff0c;允许你访问从客户端发送的请求信息。以下是一些获取请求参数的常用方法&#xff1a; getParameter(String…

node.js项目express的初始化

目录 1.初始化项目2.配置跨域3.开始编写API3.1准备3.2路由处理函数router_make下的user.js3.3路由模块router下的user.js3.4入口文件app.js里面去新增这段代码3.5启动项目进行测试 &#x1f44d; 点赞&#xff0c;你的认可是我创作的动力&#xff01; ⭐️ 收藏&#xff0c;你…

小程序学习-19

Vant Weapp - 轻量、可靠的小程序 UI 组件库 ​​​​​ Vant Weapp - 轻量、可靠的小程序 UI 组件库 安装出现问题&#xff1a;rollbackFailedOptional: verb npm-session 53699a8e64f465b9 解决办法&#xff1a;http://t.csdnimg.cn/rGUbe Vant Weapp - 轻量、可靠的小程序…

Unity中URP下的SimpleLit片元着色器

文章目录 前言一、SimpleLit片元着色器大体框架1、传入 和 返回2、GPU实例化部分3、准备 BlinnPhong 光照模型计算需要的 SurfaceData4、准备 BlinnPhong 光照模型计算需要的 InputData5、进行 BlinnPhong 的计算、雾效颜色混合及透明度计算 二、准备SurfaceData1、SurfaceData…

基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖微信小程序端(十三)

地址簿相关功能 1.1 需求分析和设计1.1.1 产品原型1.1.2 接口设计1.1.3 表设计 1.2 代码实现1.2.1 Mapper层1.2.2 Service层1.2.3 Controller层 1.1 需求分析和设计 1.1.1 产品原型 地址簿&#xff0c;指的是消费者用户的地址信息&#xff0c;用户登录成功后可以维护自己的地…

Salesforce开发者 - 从入门到精深

# 前言 1.1 Salesforce 平台简介 Salesforce 是一种云计算平台&#xff0c;专注于客户关系管理&#xff08;CRM&#xff09;。它提供了一整套工具和服务&#xff0c;用于开发、定制和管理企业应用程序。 1.2 为什么选择 Salesforce 开发 - 快速开发&#xff1a;通过Salesfo…

51单片机中断

1、什么是中断&#xff1f; CPU在处理某一事件A时&#xff0c;发生了另一事件B请求CPU迅速去处理&#xff08;中断发生&#xff09;&#xff1b; CPU暂时中断当前的工作&#xff0c;转去处理事件B&#xff08;中断响应和中断服务&#xff09;&#xff1b; 待CPU将事件B处理完…

【书生·浦语】大模型实战营——第六课笔记

视频链接&#xff1a;https://www.bilibili.com/video/BV1Gg4y1U7uc/?vd_source5d94ee72ede352cb2dfc19e4694f7622 教程文档&#xff1a;https://github.com/InternLM/tutorial/blob/main/opencompass/opencompass_tutorial.md 仓库&#xff1a;https://github.com/open-compa…