Flutter Platform Channel - BasicMessageChannel & EventChannel 使用

484 阅读2分钟

一、前言

Flutter Platform Channel - MethodChannel 使用 一文中我们已经学习了 MethodChannel 如何使用,本篇文章我们将继续学习 BasicMessageChannel & EventChannel 如何使用,以及它们之间的区别。


二、BasicMessageChannel

本例我们将会通过 Flutter 端调用 Native 方法,然后 Native 返回给 Flutter 数据进行展示的案例来演示一下具体怎么使用。

1、Flutter 端

在上篇文章代码的基础上,我们继续编写 BasicMessageChannel 所需代码,首先修改 PluginTest 文件,修改如下:

const BasicMessageChannel<dynamic> basicMessageChannel =
    BasicMessageChannel<dynamic>(
        'flutter.billionbottle.com/basic-message-channel',
        StandardMessageCodec(),
    );
    
 _singleton = PluginTest._(
    methodChannel,
    eventChannel,
    basicMessageChannel,
  );
 
 PluginTest._(
    this._methodChannel,
    this._eventChanel,
    this._basicMessageChannel,
  );

final BasicMessageChannel<dynamic> _basicMessageChannel;
  
Future<Map<dynamic, dynamic>?> sendBasicMessage(
   Map<String, dynamic> params,
) async {
    final Map<dynamic, dynamic>? result =
        await _basicMessageChannel.send(params);
    return result;
}
  • 参数类型任意,多个参数通常使用 Map
  • 返回 Future,原生端返回的数据;

然后修改 PluginDemoPage 代码,如下:

String? basicMessageResult;
Future<void> sendBasicMessage() async {
    final PluginTest plugin = PluginTest();
    final Map<dynamic, dynamic>? result = await plugin.sendBasicMessage(
      <String, dynamic>{
        'name': 'tom',
        'age': '20',
      },
    );
    setState(() {
      basicMessageResult = '$result';
    });
  }
  
OutlinedButton(
  onPressed: sendBasicMessage,
  child: Text('SendBasicMessage'),
),
Text('basicMessageResult 返回的数据:$basicMessageResult'),

2、Android 端

然后再新建一个 BasicMessageChannelTest 文件,编写如下代码:

class BasicMessageChannelTest(messenger: BinaryMessenger):BasicMessageChannel.MessageHandler<Any> {
    private val channelName = "flutter.billionbottle.com/basic-message-channel"
    private val channel: BasicMessageChannel<Any>

    init {
        channel = BasicMessageChannel(messenger, channelName, StandardMessageCodec())
        channel.setMessageHandler(this)
    }

    override fun onMessage(message: Any?, reply: BasicMessageChannel.Reply<Any>) {
        val name = (message as Map<String, Any>)["name"]
        val age = (message as Map<String, Any>)["age"]
        var result = mapOf("name" to name,"age" to age)
        reply.reply(result)
    }
}

最后在 MainActivity#configureFlutterEngine 中添加如下代码:

BasicMessageChannelTest(flutterEngine.dartExecutor.binaryMessenger)
  • message 是传入的参数,由于 Flutter 端传入的是 Map,所以上面的解析按照 Map 解析;
  • reply.reply()  是返回给 Flutter 的结果;

此时运行程序我们可以下看到如下效果:

image.png

相关代码修改可以查看 Git 提交记录


三、EventChannel 使用

本例我们将会通过 Flutter 端调用 Native 方法,然后 Native 返回给 Flutter 数据进行展示的案例来演示一下 EventChannel 的使用。

1、Flutter 端

我们在上述代码的基础上继续编写 EventChannel 所需代码,首先修改 PluginTest 文件,修改如下:

  StreamSubscription<dynamic> onProgress(MethodCallback callback) {
    return _eventChanel.receiveBroadcastStream().listen(callback);
  }

然后再编写 PluginDemoPage 页面的代码,文件修改如下:


class PluginDemoPage extends StatefulWidget {
  PluginDemoPage({super.key});

  @override
  State<PluginDemoPage> createState() => _PluginDemoPageState();
}

class _PluginDemoPageState extends State<PluginDemoPage> {
  dynamic eventChannelResult;
  StreamSubscription<dynamic>? subscription;

  @override
  void initState() {
    super.initState();
    final PluginTest plugin = PluginTest();
    subscription = plugin.onProgress(_onData);
  }

  void _onData(dynamic event) {
    setState(() {
      eventChannelResult = event;
    });
  }

  @override
  void dispose() {
    super.dispose();
    subscription?.cancel();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Scaffold(
        appBar: AppBar(
          title: Text('plugin Demo'),
          centerTitle: true,
        ),
        body: Column(
          children: <Widget>[
            Text('eventChannel 返回的数据:$eventChannelResult'),
          ],
        ),
      ),
    );
  }
}

2、Android 端

新建 EventChannelTest 文件,代码如下:

class EventChannelTest(var activity: Activity,messenger: BinaryMessenger):EventChannel.StreamHandler {

    private val channelName = "flutter.billionbottle.com/event-channel"
    private val channel: EventChannel
    private var events: EventChannel.EventSink? = null
    private var eventChannelCount = 20

    init {
        channel = EventChannel(messenger, channelName)
        channel.setStreamHandler(this)
        starTimer()
    }

    private fun starTimer() {
        var timer = Timer().schedule(timerTask {
            activity.runOnUiThread {
                var map = mapOf("eventChannelCount" to eventChannelCount++,"eventChannel" to "eventChannel")
                events?.success(map)
            }
        }, 0, 1000)
    }

    override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
        this.events = events
    }

    override fun onCancel(arguments: Any?) {
        this.events = null
    }

}

然后修改 MainActivity#configureFlutterEngine代码如下:

EventChannelTest(this,flutterEngine.dartExecutor.binaryMessenger)

events?.success(map) 方法向 Flutter 端发送数据,此方法必须在主线程执行。

运行程序后,UI 效果如下:

image.png

相关代码修改可以查看 Git 提交记录


四、源码分析

至此,我们已经学习了如何使用 BasicMessageChannelMethodChannel,下面我们看下 Flutter 端它们的源码。

BasicMessageChannel 的源码如下:

image.png

通过源码我们可以看出 BasicMessageChannel 主要有一个构造函数,codecname 两个变量和 一个 binaryMessenger getter 属性,剩下的就是 sendsetMessageHandler 两个方法了。

MethodChannel 的源码如下:

image.png image.png

通过它的源码我们可以看出,MethodChannel 的方法相对来说比较多。除了有 setMethodCallHandler,还有 invokeMethodinvokeListMethodinvokeMapMethod 等。不过仔细查看源码,我们发现这几个方法其实都是调用了 _invokeMethod 方法,而 _invokeMethod方法最终会调用 binaryMessenger.send 方法,其实就是跟 BasicMessageChannel#send 差不多。

BasicMessageChannel#send 方法如下:

Future<T?> send(T message) async {
    return codec.decodeMessage(await binaryMessenger.send(name, codec.encodeMessage(message)));
  }

MethodChannel#_invokeMethod 方法如下:

  Future<T?> _invokeMethod<T>(String method, { required bool missingOk, dynamic arguments }) async {
    assert(method != null);
    final ByteData? result = await binaryMessenger.send(
      name,
      codec.encodeMethodCall(MethodCall(method, arguments)),
    );
    if (result == null) {
      if (missingOk) {
        return null;
      }
      throw MissingPluginException('No implementation found for method $method on channel $name');
    }
    return codec.decodeEnvelope(result) as T?;
  }

主要是把方法名和参数通过 codec 转化为二进制数据,通过 BinaryMessages send 出去,并等待这个方法返回 result。

其实看完他们的源码就会发现 MethodChannel 其实就是为了方便使用对 BasicMessageChannel 进行的一层封装而已。

EventChannel 源码如下:

image.png

通过源码可以发现,EventChannel 其实是基于 MethodChannel 来实现的。

发送消息会对消息进行编码解码,我们来看下有哪些编解码类型:

image.png

  • BinaryCodec 发送二进制消息时;
  • JSONMessageCodec 发送 Json 格式消息时;
  • StandardMessageCodec 发送基本型数据时;
  • StringCodec 发送 String 类型消息时;

大部分情况下我们使用 StandardMessageCodec 类型就可以了,MethodChannel 中默认 codec 就是 StandardMethodCodec,当然我们也可以根据自己的需求选择不同的 codec