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

前言

本文主要讲解,使用不同的 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

Groovy 复制代码
just_audio: ^0.9.36

Android原生 需要的配置,app/build.gradle

Groovy 复制代码
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

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

Dart 复制代码
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

Kotlin 复制代码
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

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

Kotlin 复制代码
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

Kotlin 复制代码
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

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

Kotlin 复制代码
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

Kotlin 复制代码
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

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

Kotlin 复制代码
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

Kotlin 复制代码
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原生也成功接收到数据,但遇到了解析异常;

java 复制代码
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 中的数据解析方法;

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

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

Kotlin 复制代码
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

Kotlin 复制代码
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

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

Kotlin 复制代码
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

Kotlin 复制代码
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都只能在主线程执行。

Kotlin 复制代码
// Android原生

// 测试在子线程执行
Thread {
    androidSendFlutterData()
}.start()
Kotlin 复制代码
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;

双向通信:BasicMessageChannelMethodChannel

单向通信:EventChannel

7、源码地址

https://github.com/LanSeLianMa/flutter_android_channel

相关推荐
fanged6 小时前
天马G前端的使用
android·游戏
molong93110 小时前
Kotlin 内联函数、高阶函数、扩展函数
android·开发语言·kotlin
叶辞树11 小时前
Android framework调试和AMS等服务调试
android
慕伏白13 小时前
【慕伏白】Android Studio 无线调试配置
android·ide·android studio
大雷神13 小时前
Flutter鸿蒙开发
flutter·华为·harmonyos
低调小一14 小时前
Kuikly 小白拆解系列 · 第1篇|两棵树直调(Kotlin 构建与原生承载)
android·开发语言·kotlin
跟着珅聪学java14 小时前
spring boot 整合 activiti 教程
android·java·spring
川石课堂软件测试15 小时前
全链路Controller压测负载均衡
android·运维·开发语言·python·mysql·adb·负载均衡
2501_9159214316 小时前
iOS 26 电耗监测与优化,耗电问题实战 + 多工具 辅助策略
android·macos·ios·小程序·uni-app·cocoa·iphone
2501_9159214316 小时前
苹果软件混淆与 iOS 应用加固白皮书,IPA 文件加密、反编译防护与无源码混淆方案全解析
android·ios·小程序·https·uni-app·iphone·webview