一文搞懂FlutterMethodChannel原理

  • Post author:
  • Post category:其他



目录


前言


swift端Channel实现


flutter端Channel实现


flutterMethodChannel原理


MethodChannel整体架构图


IOS端MethodChannel分析


如何注册MethodChannel监听?


总结


前言

在跟甲方合作中,需要开发一个App,除了店长管理日常日报和订单退款之外,还需要实现通过消息推送将每笔订单进行语音播报。App开发开始从其他团队借调一个做Android的人过来先完成了Android开发,ios本打算靠后一点发布。刚好遇到组织架构重组,ios就停滞了。离甲方要求的时间只有一个月时间了,产品和运营就来找我们技术团队说App能不能做,但是重新学习一门新的语言,确实一个非常大的挑战。恰好当时我很感兴趣flutter开发app,就主动提出来接这个锅。虽然年龄已经过了30,也想试一试是否还能学得动,学习新的语言和技术毕竟是兴趣使然!最终花了5天入门dart语言,5天熟悉开发环境和调试打包,真机调试等,并在1多个月内完成了Android版本的发布和ios 的开发提测!!!整体感觉不错,特别是ios的语音播报问题,从未接触过swift到最终实现ios的语音播报,特别写下这篇文章表示对自己的一份总结吧。

基于flutter开发,对于大部分功能界面确实能做到开发一套代码,在Android和ios就能做到完全兼容,在涉及到语音播报的时候,就很麻烦了,本来打算使用TTS来实现语音播报的,参考了下网上资料,大多都使用百度语音合成和科大讯飞,可是需要注册,且必须交年费才能使用,于是放弃。最后使用1到10,十,百,千,万等mp3静态资源,使用dart解析订单金额并将mp3读入内存进行语音播报,比如10元,那么解析出来的就是[“file://**/1.mp3″,”file://**/.十.mp3″,”file://**/yuan.mp3”]。Android中使用flutter_exoplayer,其原理是通过MethodChannel调用原生的播放器进行语音播放,将解析出来的静态资源列表传给flutter_exoplayer插件,就可以完成多mp3无缝连播了。Java核心代码:



flutter_exoplayer

并不支持ios,而且现在该插件好像也不支持更新了,查了很多插件都不满足要求,最后索性自己探索自己搞,写出一个语音播报的功能出来。

swift端Channel实现

为了能在ios支持语音播报,坚持3天swfit的学习,基本掌握了swift语法,并通过FlutterMethodChannel实现语音播报。语音播报最后封装在SpeechPlayer类中,类之间关系如下图所示:

在SpeechPlayer初始化时,首先创建一个具有唯一名称的Channel:

在channel对象的setMethodCallHandler回调中监听dart调用的speech方法,核心代码如下图所示:

这样,我们就可以在dart代码中调用speech实现ios的语音播报了。当然,iOS7以上提供了AVSpeechSynthesizer类实现语音播报是非常方便的,而且,就不需要像Android一样,先解析mp3静态资源列表,再使用native播放器播放,

flutter端Channel实现

必须定义一个与ios端名称一致的Channel:

并定义一个speakTTs方法,调用_channel对象的invokeMethod方法:

flutter端代码实现很简单, 核心就是invokeMethod实现调用native的方法。iOS app运行之后,就能听到语音播报了。

flutterMethodChannel原理

使用起来只要手动做一遍,是没什么难度的,具体如何实现swfit和dart之间的通信互调用呢?原理是什么呢?今天我们就重点学习一下。

首先来一张flutter的整体架构图

从上图可以知道,dart与Native之间多了一层C++写的Engine,至于flutter的C++源码,大家可以在

github中下载

。通过dart的invokeMethod一路跟踪调用发现,其实真正起到dart数据和native数据之间通信的是在Engine层,而且是通过Binary字节串实现双方的数据拦截。

MethodChannel整体架构图

FlutterMethodChannel在Platform Channels中定义,整体架构图如下:

IOS端MethodChannel分析

首先,根据上面说到的speakTTs调用invokeMethod,一路跟踪调用路径,流转到MethodChannel类的invokeMethod和_invokeMethod方法,如下两图的代码:

其中,binaryMessager是一个继承于BinaryMessager的_DefaultBinaryMessenger对象,并实现了send方法:

而codec是一个继承于MethodCodec的StandardMessageCodec对象,其提供的功能包括将invokeMethod的方法名和参数编码成Uint8List的无符号字节流和将Uint8List转换成dart的标准类型。其中encodeMethodCall代码如下:

最后流转到platform_dispatcher.dart中的sendPlatformMessage方法,并调用native的PlatformConfiguration_sendPlatformMessage这个api。

我在github上下载了dart的c++代码,发现PlatformConfiguration_sendPlatformMessage在Engine层的lib/ui/window/platform_configuration.cc文件中定义,代码如下:

最后返回HandlePlatformMessage方法:

在PlatformMessageHandler中HandlePlatformMessage其实是一个纯虚函数,那么应该有个类继承PlatformMessageHandler并实现HandlePlatformMessage。

果然,最终PlatformMessageHandlerIos继承HandlePlatformMessage并实现了HandlePlatformMessage方法,起作用就是将dart传进来的Uint8转换成swfit的标准类型NSData并调用dispach_async异步执行真正的native操作。

如何注册MethodChannel监听?

那native端如何初始化Channel并监听invokeMethod的呢?其实在iOS中有一个FutterViewController.mm文件,在viewDidLoadÅ里进行了初始化。调用路径最后跟踪到shell/common/shell.cc的OnEngineHandlePlatformMessage。

OnEngineHandlePlatformMessage就实现了所有Channel回调的监听,学过C#的话,就跟C#里的事件和委托一样,在底层实现了挂载OnEngineHandlePlatformMessage监听Channel回调注册,一旦有一个platform_message_handler_进来就将其挂载放到Channel队列中。

总结

  • MethodChannel主要由三部分组成:MethodChannel负责创建通信隧道、StandardMethodCodec负责对native方法名及参数编码dart类型转换成字节流和解码字节流转换成不同平台的标准数据类型、BinaryMessenger负责和底层native进行通信。
  • 除了MethodChannel之外,还可以通过EventChannel(用于数据流的通信,只支持native到dart)和BasicMethodChannel(主要是JSON字符串)实现native和dart的通信,后面可以继续研究起来。
  • 我这里只做了dart到native单向的研究,其实也可以做到native调用dart的方法,如下图所示:

swift代码:

dart代码:

由于时间问题,在这里就不展开了,下一次将会讲解native调用dart方法的原理。



版权声明:本文为weixin_47954860原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。