| 导语 全部的跨平台方案,不论是最先的WebApp和HybridApp,仍是以前很是火热的RN和Weex,都面临着如何平衡跨平台性和效率这一问题。Flutter做为新一代的跨平台解决方案,传说中性能直逼原生,它为什么如此优秀呢?让咱们以Flutter的通讯机制为起点,一块儿探索Flutter和原生之间的小秘密。注:因为做者不太熟悉iOS开发,因此本文大部分使用的都是Android视角。java
跨平台解决方案由来已久,《聊聊移动端跨平台开发的各类技术》的做者将其大体分为了如下几种流派:react
Web 流:也被称为 Hybrid 技术,它基于 Web 相关技术来实现界面及功能android
代码转换流:将某个语言转成 Objective-C、Java 或 C#,而后使用不一样平台下的官方工具来开发git
编译流:将某个语言编译为二进制文件,生成动态库或打包成 apk/ipa/xap 文件github
虚拟机流:经过将某个语言的虚拟机移植到不一样平台上来运行react-native
以往最先的Hybrid开发,主要依赖于WebView。可是WebView是一个很重的控件,很容易产生内存问题,并且复杂的UI在WebView上显示的性能很差。react-native技术抛开了WebView,利用JavaScriptCore来作桥接,将js调用转为native调用,只牺牲了小部分性能获取的跨平台开发。数据结构
Flutter实现跨平台采用了更为完全的方案。它既没有采用WebView也没有采用JavaScriptCore,而是本身实现了一台UI框架,而后直接系统更底层渲染系统上画UI。因此它采用的开发语言不是JS,而是Dart。Dart语言有着适合Flutter的良好的特性,详情能够看FAQ.为何Flutter选择使用Dart语言?框架
Flutter和native间的通讯,应该分为 Flutter主动发送 和 native主动发送 两种状况,而Flutter的官方文档却仅仅介绍了Flutter主动发送的方式。对于 native主动发送的方式提供了一个plugin做为例子。具体的使用方式能够点开官网连接查看,在这就再也不赘述了,主要是探寻一下两边的代码究竟是怎么相互调用的。async
Flutter之间的消息传递,是经过 MethodChannel 来完成。咱们先看它的构造函数。能够发现,须要一个 name和一个可选的 MethodCodec。 name是这个 MethodChannel的标识,在后面会用到。 MethodCodec是个编/解码器,其决定了咱们能传递什么类型的数据。 StandarMethodCodec有以下规则:函数
Dart名(rtx) | Android | iOS |
---|---|---|
null | null | nil (NSNull when nested) |
bool | java.lang.Boolean | NSNumber numberWithBool: |
int | java.lang.Integer | NSNumber |
int | if 32 bits not enough | java.lang.Long |
int | if 64 bits not enough | java.math.BigInteger |
double | java.lang.Double | NSNumber numberWithDouble: |
String | java.lang.String | NSString |
Uint8List | byte[] | FlutterStandardTypedData |
Int32List | int[] | FlutterStandardTypedData |
Int64List | long[] | FlutterStandardTypedData |
Float64List | double[] | FlutterStandardTypedData |
List | java.util.ArrayList | NSArray |
Map | java.util.HashMap | NSDictionary |
查看invokeMethod()方法, method为 MethodCall的标识, arguments则天然是参数了,值得注意的是这里的参数必需要遵照上面的规则(默认状况下不能直接传自定义类,可是咱们能够将其转为Json以后再传递,也能够去改造 MethodCode,以此进行复杂数据结构的传递)。 注:能够看到,其是一个 async标记的方法,返回值为Future。那么咱们在接受这个方法的返回值的时候,就必需要使用 await进行修饰。要调用使用 await,必须在有 async标记的函数中运行。具体调用和使用的方式能够看官网的例子。在这咱们先继续深刻。
查看send()方法能够发现一个颇有意思的事情,若是 _mockHandlers取出来的值不为空的话,则message会直接被对应handler处理,不会再发送。查看 setMockMessageHandler()的注释,官方的解释是,该方法会将Message拦截,能够用于测试。
这里调用了一个native方法,那么咱们就须要去找这个native方法。
这段代码比较长,咱们一点点来看。 首先看64行,能够获取到一个有用信息,这个套机制仅能在主线程(isolate)上运行。 而后看最关键的方法: dart_state->window()->client()->HandlePlatformMessage()
顺着找下去,能够在 WindowClient中找到 HandlePlatformMessage(),可是能够发现,这是一个纯虚函数。这里使用了一个比较取巧的方式去找,直接去找都有哪一个类继承了 WindowClient,能够发现只有 RuntimeController是继承了这个类的,去看看这个方法的实现。
那么这个client是什么呢?是一个 RuntimeDelegate …………………… 跳啊跳啊,终于在 PlatformViewAndroid 找到了实现(固然对应的iOS的也有一个,这里由于我比较熟悉Android,就只把Android的拿出来看),中间的过程没什么养分就不进行详述了。
OK,经历了重重关卡,终于找到了jni的方法。那么也就是说,只要找到 g_handle_platform_message_method 所指向的方法,就能走入Android的世界了。(能够提早关注一下 pending_responses_,这个变量在后面会提到)
用简单暴力点的方法,直接去找赋值语句,发现只有一处,那么不用想,确定是它了。
而后再找这个类名,就能够在Flutter的Android工程中找到这个方法了。
终于回到了JAVA的世界,仍是比较感动的。其实这段代码核心就在 onMessage()直接看一看这个方法的实现。
果真是一个抽象函数,并且有三个实现。那么咱们就不能乱猜了,再回头看一下 handler这变量究竟是个什么类型。从133行能够看到 handler 是以channel的name为key从一个map( mMessageHandlers)中取出来的,那这个值是在哪被设置的呢?线索仿佛到这就断掉了。别急,咱们先来回忆一下,使用 MethodChannel的步骤。(下图为中文官网教程截图)
好了重点我已经圈出来了,跳进去看看就明白了。
那就肯定了 handler是 IncomingMethodCallHandler的对象。直接去看它的 onMessage方法。
终于,咱们看到了熟悉的 onMethodCall方法。
再看看这个回调函数,分别是调用了两个native层的函数。empty的那个就不用看了,知道非empty的怎么传天然也就知道它怎么传了。在 platform_view_android_jni.cc中,咱们能够找到对应的函数。
message_response看起来就像是一个回调函数。它是从 pending_responses_中取出来的。还记得第4点中有一个回调函数吗?它在 native层中被封装,并置入了 pending_responses_中(具体能够仔细看第5点的过程,中间有提到,在这就不重复贴图了),那么这里的 message_response通过一些处理以后,最终仍是会回到那个回调函数,这里的过程就不详述了。
《IVWEB 技术周刊》 震撼上线了,关注公众号:IVWEB社区,每周定时推送优质文章。