赞
踩
这是 《Flutter 工程化框架选择》 系列的第四篇 ,就像之前说的,这个系列只是单纯告诉你,创建一个 Flutter 工程,或者说搭建一个 Flutter 工程脚手架,应该如何快速选择适合自己的功能模块,可以说这是一个指引系列,所以比较适合新手同学。
本篇将着重介绍混合开发里的一些技术选型,顺便额外科普一些坑,看到后面你就会明白为什么要做这一期分类,算是相对偏技术的一期。
在 Flutter 里进行混合开发一直都是“痛点”,其中最主要的原因就是:Flutter 有自己独立的渲染引擎,因为 Flutter 控件渲染脱离了平台控件的 render tree,这也造成了 Flutter 在混合开发上的实现相对复杂了不少。
比较形象的理解:类似于把原生控件塞到 WebView 里渲染。
事实上在 Flutter 里混合开发也分为两种:在 Flutter 里混合原生平台控件(PlatformView
)和将 Flutter 混合到原生平台项目(add-to-app
),今年我们的主题是 PlatformView
,这其中又以 Android 的 PlatformView
混合原生控件“故事最多”,当然今天我们不是从头去讲解它的技术实现,对历史技术实现感兴趣的可以看之前的文章,今天主要是聊如何去“选“。
在 Flutter 里混合原生平台的控件需要通过 PlatformView
进行接入实现,在这方面 iOS 的实现方式一直变化不大,但是在 Android 上不同版本到目前就出现了三种实现逻辑,所以本篇主要介绍 Android 平台如何选择合适的 PlatformView
方案。
抛开最早期如 flutter_webview_plugin 这种直接进行覆盖原生控件的实现不谈, PlatformView
在 Android 上目前官方提供的就有三种实现方式:
VirtualDisplays
:类似于一个虚拟显示区域,将虚拟显示区域的内容渲染在一个内存 Surface
上,然后同步纹理到 Engine ,事实上控件并不在渲染位置。HybridComposition
: 通过 addView
直接添加原生平台控件,需要层级覆盖时利用 FlutterImageView
承载 Flutter 控件的渲染进行堆叠,原生控件会在渲染位置。TextureLayer
:在渲染位置通过原生的透明 PlatformViewWrapper
做容器,然后通过替换 Canvas 将原生控件的纹理渲染到特定 Surface 上,控件不在渲染位置,但是可以通过父容器做事件拦截。是不是看完有点懵?不怕后面会有对应的例子解释,不理解也不要紧,这一篇是教你如何选,所以也并不需要真的理解内部是如何实现。
但是上面介绍的这三种实现也是有前提条件的,比如:
VirtualDisplays
VirtualDisplays
和 HybridComposition
TextureLayer
和 HybridComposition
VirtualDisplays
、 TextureLayer
和 HybridComposition
是不是觉得有些奇怪,为什么会有奇怪的排列方式?这里面的故事线大概是这样的:
VirtualDisplays
存在触摸事件和键盘等问题,所以通过 addView
进行原生堆叠的 HybridComposition
出现,解决了手势和键盘等问题HybridComposition
存在一些场景缺陷,比如列表卡顿,渲染线程不同步导致闪动等问题,所以 VirtualDisplays
得以继续服役VirtualDisplays
和 HybridComposition
被替换为 TextureLayer
,但是 HybridComposition
实现依旧保留,在使用时需要显式执行 PlatformViewsService.initExpensiveAndroidView
才能使用,因为 TextureLayer
存在没办法替代原生控件 Canvas 的场景,同时 TextureLayer
要求 Flutter Mini SDK API 23VirtualDisplays
实现又回来了,因为需要支持 Mini SDK API 23 以下的场景,同时比如 SurfaceView
机制原因,这类场景 TextureLayer
实现没办法直接兼容,所以 3.3 开始在遇到 SurfaceView
实现时 TextureLayer
会切换为 VirtualDisplays
模式这里面固然有实现成本的问题,同时也有社区把控的问题,才造成了这样“混乱”的局面。
上面这么说可能有些抽象,我们再看具体例子,首先看官方的 webview_flutter 实现, 如下表格所示,最初的时候 WebView 插件默认是使用了 AndroidWebView
,也就是 VirtualDisplays
相关的实现;后来有了 HybridComposition
之后就新增了 SurfaceAndroidWebView
,用户在需要时可以根据场景自行切换。
默认判断 | VirtualDisplays | HybridComposition |
---|---|---|
这里就不得不提以前的 issue ,如果你的 WebView 存在触摸或者键盘问题就可以通过下述代码来解决,因为正如前面说过, HybridComposition
是通过 addView
的方式添加原生控件,所以能尽可能保存控件本身的特性。
if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
复制代码
而后来 webview_flutter 在更新迭代里默认实现里也修改为了 SurfaceAndroidWebView
,因为 HybridComposition
确实更适合 WebView 的场景,比如 H5 的详情页。
默认判断 | TextureLayer | HybridComposition |
---|---|---|
上面的例子可能还不够典型,接着我们再看一个典型的例子: MapView ,如下所示,目前国内 Flutter 里常见的地图实现有:
虽然他们都是采用了 PlatformView
的实现,但是选择的实现方式却存在一定差异,而通过这些差异也可以很好比对 PlatformView
实现的区别:
AndroidView
的实现方式,也就是在 3.0 以下是 VirtualDisplays
,在 3.0 以上是 TextureLayer
, 从表中的图 2 也可以看到在 Flutter 3.0 MapView 父容器对应的 TextureLayer
的 PlatformViewWrapper
。dart 实现 | TextureLayer |
---|---|
这里额外需要注意的是:由于隐私政策等原因,现在基本地图框架都需要做一定的隐私相关适配,高德也不例外,所以如果想正常使用 SDK ,就需要注意初始化时的适配问题。
对隐私和权限问题感兴趣可以看 《2022 年 App 上架审核问题集锦,全面踩坑上线不迷路》
HybridComposition
的实现方式,注意下图代码里的 initExpensiveAndroidView
,因为现在使用 PlatforViewLink
已经不能作为是否使用 HybridComposition
的判定标准,3.0 开始必须显式配置了 initExpensiveAndroidView
才是 HybridComposition
。3.0之前 PlatforViewLink | 3.0之后 PlatforViewLink + initSurfaceAndroidView | 3.0之后 PlatforViewLink + initExpensiveAndroidVie |
---|---|---|
HybridComposition | TextureLayer | HybridComposition |
对
HybridComposition
详细实现感兴趣的可以了解:混合开发下 HybridComposition 和 VirtualDisplay 的实现与未来演进 和 《1.20 下的 Hybrid Composition 深度解析》
AndroidView
的实现方式,但是原生端提供了 TextureView
和 SurfaceView
两种实现选择,这又产生了不同的组合结果。3.0之前 | 3.0 | 3.0之后 |
---|---|---|
VirtualDisplays | TextureLayer ( SurfaceView 方案无法工作) | VirtualDisplays (SurfaceView)/ TextureLayer (TextureView) |
从下方的 Layout Inspector 可以看到,在 Flutter 3.3 下,百度地图插件如果使用了 MapSurfaceView
就会采用 VirtualDisplays
,而如果使用了 MapTextureView
就会采用 TextureLayer
,这是为什么呢?
SurfaceView | TextureView |
---|---|
前面我们不是说过,Flutter 在 3.0 的时候通过 TextureLayer
取代了 VirtualDisplays
,而 TextureLayer
的实现方式就是通过 PlatformViewWrapper
来替换 child 的 Canvas 从而提取纹理,但是在某些场景下就会有问题,例如 SurfaceView
。
简单来说,
SurfaceView
是比较特殊的场景,本身它具备双缓冲机制,类似于两个Canvas
,同时在 WMS 有独立的WindowState
, 所以渲染逻辑其实是脱离了原本的 View hierachy ,因此 Flutter 3.0 下的TextureLayer
的 hook Canvas 实现没办法兼容。
所以在 Flutter 3.3 中 VirtualDisplays
又回来了,作为多年服役的"老将",当运行平台低于 23 或者原生控件是 SurfaceView
时,就会继续降级采用 VirtualDisplays
的相关实现,这也算是折中的实现。
SurfaceView | TextureView |
---|---|
如果对这部分感兴趣的可以查阅 Flutter 3.0下的混合开发演进 / Flutter 深入探索混合开发的技术演进
所以现在知道为什么要做这一期了吧?Flutter 的 PlatformView
实现因为历史包袱留下了很多“坑”,不理清楚这些坑,很可能就会让你在遇到问题时找不到方向,总结一下:
VirtualDisplays
老骥伏枥,采用的是虚拟显示提取纹理的方式,可能存在手势和键盘问题,适合列表和小控件场景,在 3.0 版本中缺席HybridComposition
在 1.2 版本之后出现,因为是通过 addView
和层级堆叠实现混合,能尽可能保证控件的“原汁原味”,但是性能开销会比较大,不适合列表里使用,在 3.0 之后需要显式调用 initExpensiveAndroidView
TextureLayer
在 3.0 版本开始出现,通过 Hook 住控件 Canvas 的方式实现纹理提取,但是需要 miniSDK 23 ,适用于替代 VirtualDisplays
的场景,但是在 3.3 开始遇到 SurfaceView
时会降级为 VirtualDisplays
。其实我有预感,在之后的版本这部分逻辑还会出现变化,但是可能 3.0 和 3.3 的发布一样,并不会将这种变更放在 release note 里。
最后,这里额外介绍一个关于地图的小知识点,现在国内三大地图 SDK 厂商都有商用 5 万起步的授权费用要求,如果你打算用在公司的项目里,那么这个是逃不掉的,因为迟早平台会打电话联系你缴费,所以在评估时最好将这个费用问题和产品沟通下。
高德 | 百度 | 腾讯 |
---|---|---|
当然,华为的地图服务目前看起来没有商业授权的要求,但是它打包配置麻烦,特别是需要 HMS 的场景,所以如果为了省点钱又不怕麻烦,也可以考虑华为地图。
那有人就要说了,我自己做一个行不行?我比较牛,有数据有能力自己做个简易版地图不就免费了?答案时不行,如下所示,应用平台会有关于地图相关的资质要求,如果没有你做出来了也上不去。
审核意见:我们发现您的应用内含有互联网地图服务相关内容,依据《关于加强互联网地图管理工作的通知》互联网地图服务单位应当依法取得相应的互联网地图服务测绘资质,并在资质许可的范围内提供互联网地图服务。 修改建议:请补充提供《测绘资质证书》并在专业范围中注明互联网地图服务;若接入第三方开放平台地图服务的,可提供双方合作协议(授权证明),同时需在地图显著位置补充标明合作单位。
好了,本篇就到这里结束了,可能相对而言会比之前的内容更“技术性”一些,但是本质还是希望可以帮助大家搞清楚 PlatformView
在选型上的区别,最后,如果你还有什么关于 Flutter 工程或者框架的疑问,欢迎留言评论,
作者:恋猫de小郭
链接:https://juejin.cn/post/7153184663077388295
更多Android学习笔记+视频资料可点击下方卡片获取~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。