likes
comments
collection
share

Flutter III 之你不知道的 PlatformView 的混乱之治

作者站长头像
站长
· 阅读数 11

如果你是从 2018 年开始使用 Flutter ,那么相信你对于 Flutter 在混合开发的支持历程应该会有一个深刻的体会,如果你没尽力过这个时期,不要担心,通过我过往 PlatformView 的相关文章,你也可以有一个清晰的感受:

总而言之,目前 Flutter 对于 PlatformView 的支持,特别是在 Android 平台上,只能用一个字来形容:「乱」。

Flutter III 之你不知道的 PlatformView 的混乱之治

这个「乱」不只是体现在 API 和底层实现方案上,更表现在你遇到 issue 时,不确定到底是因为什么引起的困惑上,因为目前 Flutter 在 Android 平台的 PlatformView 会根据不同的 SDK 版本和场景进行「兜底」兼容,存在各种历史包袱。

其实我已经不是很想写这方面的内容了,但是奈何总有人问,那么本篇就来个总结式科普。

目前活跃在 Android 平台的 PlatformView 支持主要有以下三种:

  • Virtual Display (VD)
  • Hybrid Composition (HC)
  • Texture Layer Hybrid Composition (TLHC)

可以看到官方都已经为大家定义好了简称 VD、HC、TLHC ,有了简称也方便大家提 issue 时沟通,毕竟每次在讨论时都用全称很费劲:

因为你需要不停指出你用的是什么模式,然后在什么模式下正常or不正常,另外知道这些简称最大的作用就是看 issue 时不迷糊

那么,接下来主要简单介绍它们的区别:

VD

VD简单来说就是使用 VirtualDisplay 渲染原生控件到内存,然后利用 id 在 Flutter 界面上占用一个相应大小的位置,最后通过 id 关联到 Flutter Texture 里进行渲染。

Flutter III 之你不知道的 PlatformView 的混乱之治

问题也很明显,因为控件不会真实存在渲染的位置,所以此时的点击和对原生控件的操作,其实都是需要由 Flutter 这个 View 进行二次转发,另外因为控件是渲染在内存里,所以和键盘交互需要通过二级代理处理,这就产生了各种键盘输入的异常问题。

键盘问题突出在不同版本的 Android 兼容上。

HC

1.2 版本开始支持 HC,简单说就是直接把原生控件覆盖在 Flutter 上进行堆叠,如果出现 Flutter Widget 需要渲染在 Native Widget 上,就采用新的 FlutterImageView 来承载新图层。

Flutter III 之你不知道的 PlatformView 的混乱之治

好处是原生视图是直接显示渲染,坏处就是在 Android 10 之前存在 GPU->CPU->GPU的性能损耗。

另外因为此时原生控件是直接渲染,所以需要在原生的平台线程上执行,这和 Flutter 的 UI 线程就存在线程同步问题,所以在此之前一些场景下会有画面闪烁 bug 。

TLHC

3.0 版本开始支持 TLHC 模式,最初的目的是取代上面这两种模式,奈何最终只能共存下来,该模式下控件虽然在还是布局在该有的位置上,但是其实是通过一个 FrameLayout 代理 onDraw 然后替换掉 child 原生控件的 Canvas 来实现混合绘制。

Flutter III 之你不知道的 PlatformView 的混乱之治

所以看到此时上图 TextView 里没有了内容,因为 TextView 里的 Canvas 被替换成 Flutter 在内存里创建的 Canvas

但是这种实现天然不支持 SurfaceView ,因为 SurfaceView 是双缓冲机制,所以通过 parent 替换 Canvas 的实现并不支持。

总结

上述就是这目前三种模式的简单描述和对比,如果看不明白,可以通过前面的历史文章进行了解,总结下以下它们的主要问题:

  • VD : 控件不是被真实渲染,容易有触摸和键盘等问题
  • HC: 直接堆叠控件,会有性能开销和线程同步问题,某些场景容易出现闪烁和卡顿
  • TLHC:不支持 SurfaceView ,对于使用 SurfaceView 的播放器、地图等插件会有兼容性问题。

所以这也是为什么 1.2 HC 出来之后,VD 还在继续被投入使用,以至于 TLHC 发布之后,依然没能完全取代 VD 和 HC 的主要原因,因为目前它们都不是最优解。

而从目前的情况下,PlatformView 也成了 Android 平台的沉重包袱,因为多种底层模式在同时工作,并且还在互相「兼容」。

API

那么回归到 API 上,在目前 3.0+ 的 Flutter 上同样对应有三个 API ,但是这三个 API 并不是直接对应上述三种模式:

  • initAndroidView :默认情况下会使用 TLHC 模式,当 SDK 低于 23 或者存在 SurfaceView 的时候,会使用 VD 模式兼容
  • initSurfaceAndroidView : 默认情况下会使用 TLHC 模式,当 SDK 低于 23 或者存在 SurfaceView 的时候,会使用 HC 模式兼容
  • initExpensiveAndroidView: 强行完全使用 HC 模式

看到没有,这里有一个问题就是:你其实没办法主动控制是 TLHC 还是 VD ,对于 HC 你倒是可以强行指定

另外,不知道你注意到没有,不管是 initAndroidView 还是 initSurfaceAndroidView ,它们都可能会在升级到新版本时使用 TLHC 模式,也就是如果你的 Plugin 没有针对性做更新,那么可能会在不知觉的情况下换了模式,从而有可能出现 bug

例如 TLHC 模式:

  • 对于 SurfaceView 的不支持存在一些特殊情况,假设一开始 PlatformView 创建时不存在 SurfaceView ,但是后续又添加了 SurfaceView ,那么该模式将无法正常工作 #109690
  • 对于 TextureView 场景,有时候会出现不正常更新的异常情况#103686

现在你看出 PlatformView 的混乱了吧?从底层实现的不统一,到 API 再不同版本下不同的行为变化,这就是目前 Android 在 PlatformView 支持下的混乱生态,同时如果你对于目前 PlatformView 存在的问题刚兴趣,可以查阅以下相关 issue:

所以,目前的 Android PlatformView 就给我一种既视感,好比魔兽世界里的平行分支:

  • 地狱咆哮喝下了恶魔之血,绿皮吼爷打爆深渊领主玛诺洛斯
  • 地狱咆哮拒绝喝恶魔之血,橙皮吼爷打爆深渊领主玛诺洛斯

虽然都是打爆了玛诺洛斯,虽然吼爷结局都一样扑街,但是中间的剧情走向还是有着极大的分歧,所以只能寄希望未来的世界线可以正常「收束」,能有一位「伯瓦尔」来结束这个混乱之治的时代。

Flutter III 之你不知道的 PlatformView 的混乱之治