团队开发的第一款flutter app即将上线了,也是职业生涯第一个正式的flutter app,现在回过头来,再来回顾下,为什么flutter可以实现跨平台,也算是为我自己解惑,解答这个问题,还是要看flutter的架构设计
flutter的架构
了解flutter的架构,用下面一张图片就可以了,相信很多人都有看过,一共有三层
Embedder
首页,最下面的是嵌入层(Embedder),嵌入层作为一个flutter应用入口,可以与原生底层操作系统进行交互,可以访问系统渲染,输入,消息总线,线程创建等各个系统的底层能力,嵌入层在是根据不同的平台单独实现,实现语言也不一样
平台 | 语言 | 备注 |
---|---|---|
Android | Java C | Kotlin语言,其实也是运行在Java虚拟机上 |
ios macOs | Object-C Object-C | |
Windows | C | |
Linux | C |
由于flutter是开源的,我们也可以去github上查看嵌入层的源码:https://github.com/flutter/engine/tree/master/shell/platform
可以把嵌入层理解成一个壳,flutter的应用本体是一个模块,套一个Android的壳,就是一个Android的应用,套一个ios的壳,就是ios的应用
engine
引擎层是flutter的核心部分,核心api的底层实现,比如图形绘制、文本布局、网络请求、io操作,dart运行环境创建等; 引擎层也会把底层的C 包装成Dart的代码,给UI层调用 github上可以看到引擎层的源码,https://github.com/flutter/engine/tree/master,主要是由C 跟dart来实现的
框架层
最上面的就是框架层了,通俗的可以理解为UI层,也是日常开发接触的层级;最上面的是Material跟Cupertino库,提供符合Material跟ios的设计规范,目前团队开发的app,最顶层用的统一是Material; 然后就是widget层,这里就是日常开发的大量耗时工作量地方,开发各个页面的widget,所以说,在flutter,一切皆widget 再往下就是渲染层,用于基于widget树生成渲染树,还有底层的基础层,这块在实际开发中,很少直接打交道
flutter用一个跨平台的开发语言Dart来开发UI层,然后核心功能,用C 实现,最后用嵌入层做一层包装,适配各个不同的平台上使用,由于UI部分,都是在框架层,从而实现跨平台实现;另外由于flutter是直接跟原生接口打交道,所以在性能上也会媲美原生app
渲染机制
上面提到的engine层,有个很重要的部分就是图形渲染,所有的widget最终的目的,都是为了绘制在屏幕上,这块的底层实现就是依靠Skia,Skia也是开源库,同时也兼容了多个平台,可以看下skia在Wikipedia上描述,基本兼容各主流的平台了
在github上,可以看到skia的源码:https://github.com/google/skia,大多数是C跟C
所有flutter UI层的代码,都是dart语言编写的,在发布的时候,会编译成native语言,然后交给Skia去渲染,可以看下目前开发的app的apk的反编译后的包体目录
我们写的dart的代码,打包成libapp.so,整个flutter框架,打包成了libflutter.so,都变成底层语言了
所以flutter有个很大的特性,每次flutter是SDK大版本更新,只要打包环境的flutter版本更新下,最终生成的安装包就可以包含新的特性了,而不依赖Android系统或者ios系统的更新(当然有利也有弊,缺点是包体变大了,会大几兆)
跟原生平台交互
有时候,难免碰到flutter需要调用原生的功能和方法,官方提供了一个MethodChannel的方法,可以方便的实现跟原生交互,包括调用原生的方法,接口返回结果等
可以看到MethodChannel的核心实现是在engine层 跟原生交互,也需要各个原生各自适配,比如这次团队开发的地图的POI搜索,由于使用的高德官方的flutter地图插件,不支持POI搜索,就需要flutter用methodChannel发起一个方法调用,ios跟Android接受这个方法,各自集成原生的地图SDK,然后通过原生的SDK调用POI功能,再把结果返回给flutter
渲染原生的UI
整个flutter的框架,其实是一个独立的整体,跟原生是独立的,那有些功能,原生已经有成熟的实现了,flutter为了避免重复实现一套,希望可以直接用原生的UI展示在flutter上面 flutter为了解决这个问题,使用两个特定的widget来实现 (AndroidView and UiKitView),实现代码大致如下
代码语言:javascript复制if (defaultTargetPlatform == TargetPlatform.android) {
return AndroidView(
viewType: 'plugins.flutter.io/google_maps',
onPlatformViewCreated: onPlatformViewCreated,
gestureRecognizers: gestureRecognizers,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
);
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
return UiKitView(
viewType: 'plugins.flutter.io/google_maps',
onPlatformViewCreated: onPlatformViewCreated,
gestureRecognizers: gestureRecognizers,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
);
}
return Text(
'$defaultTargetPlatform is not yet supported by the maps plugin');
原生渲染大致原理如下 1、拷贝原生视图的渲染纹理,在flutter渲染的时候,交给flutter去渲染 2、flutter接收到用户的点击事件,转换为原生输入事件,传给原生控件
可以知道底层实现,也是类似MethodChannel,而且目前仅支持ios跟Android,不过在技术上,也是可以支持Mac跟window,官方有可能后续会支持,有个缺点就是,这种实现会带来比较大的性能跟资源开销
总结
flutter可以跨平台,关键是跟他的架构设置有关,自己实现了dart语言,包括整个engine层,再结合嵌入层的壳效果,可以轻松的实现跨平台。 由于flutter的框架设计的很完善,大多数开发,基本都是跟widget打交道,不需要涉及底层和平台特性;不过实际开发中,还是会碰到不少不同平台的特性问题,这个是另外一个话题了,再次有空继续展开...