FlutterChannel是一个异步调用通道。如果想在Dart端同步得到Native返回的结果,调用时加上await即可:finalintresult=awaitplatform.invokeMethod('hellochannel');所以这篇文章到此结束?不!上面这行代码其实是一个“伪同步”,因为它只保证了Dart代码的同步执行,Native代码和Dart不在同一个线程中执行。试想一下,如果通过FlutterChannel进行日志记录,但是由于日志消息是异步传递给Native的,所以最终的日志顺序可能是错误的。在通过日志排查一些时序相关的bug时,日志的顺序很重要。因为FlutterChannel一开始就设计成异步的,使用await来回切换线程带来的开销不小。而且协程的await语法是可传递的,上层调用者也需要使用await层层传递。而DartNative(https://github.com/dart-native/dart_native)本来就是设计成同步调用的,也支持异步调用://newDNTestinstanceandcallhellomethod.DNTest().hello('DartNative');WhyDartNative?DartNative是“真同步”,保证了执行顺序。它还支持异步调用。一行代码实现同步调用,告别FlutterChannel胶水代码带来的开发成本。同步调用的性能是FlutterChannel的数倍。使用FlutterChannel和DartNative调用fooNSString:方法分别需要三到四倍的时间差。性能数据在不同场景下可能会有波动,您可以通过执行Benchmark代码来对比结果。实现原理下图以Dart同步调用iOSObjective-CAPI为例,描述DartNative同步调用的原理。以一个字符串参数为例,描述了DartString自动转换为Objective-CNSString并传递给hello:方法的过程。返回值也是自动转换的,由于篇幅原因图中没有描述。实现了基本的同步调用后,开发重心也转移到了性能优化上。方法签名优化Dart同步调用Native时,为了实现跨语言调用时参数和返回值类型的自动转换,需要先获取Native的方法签名。这里做了两个性能优化:通过DartFFI调用OCRuntime获取方法签名需要一定的时间。可以在Dart端加一层Cache,减少通信和反射的次数。方法签名串的组成是“TypeEncoding+offset”的组合。在不同语言之间传递字符串的编码和解码需要花费大量的时间,自动类型转换只需要TypeEncoding部分。大多数类型对应的TypeEncoding是固定的,所以只需要传递TypeEncoding指针即可。字符串转换的优化在DartString和Objective-CNSString的转换过程中,数据传输格式的选择非常重要。因为DartString是UTF16编码的,所以DartNative使用Uint16List作为数据传输的格式。通过性能测试,使用UTF16来回传输字符串的总耗时(包括Native方法本身的耗时)相比UTF8减少了约35%。如果仅仅通过通道自动类型转换的计算来减少时间消耗,这个比例会更大。将DartString转为Objective-CNSString:使用DartFFI在堆上创建一个uint16_t数组,将DartString转为UTF16格式并加载进去。最后通过perform方法反射调用stringWithCharacters:length:方法创建NSString对象。finalunits=value.codeUnits;finalPointer
