GSYFlutterBook/Flutter-N30.md

112 lines
7.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

![](http://img.cdn.guoshuyu.cn/20230719_N30/image1.png)
# Flutter III 之你不知道的 PlatformView 的混乱之治
如果你是从 2018 年开始使用 Flutter ,那么相信你对于 Flutter 在混合开发的支持历程应该会有一个深刻的体会,如果你没尽力过这个时期,不要担心,通过我过往 `PlatformView` 的相关文章,你也可以有一个清晰的感受:
- [Flutter 3.0下的混合开发演进](https://juejin.cn/post/7113655154347343909)
- [告别 VirtualDisplay ,拥抱 TextureLayer](https://juejin.cn/post/7098275267818291236)
- [Flutter 深入探索混合开发的技术演进](https://juejin.cn/post/7093858055439253534)
- [HybridComposition 和 VirtualDisplay 的实现与未来演进](https://juejin.cn/post/7071549421116194847)
- [ Hybrid Composition 深度解析](https://juejin.cn/post/6858473695939084295)
- [Android PlatformView 和键盘问题](https://juejin.cn/post/6844904070906380296)
总而言之,目前 Flutter 对于 `PlatformView` 的支持,特别是在 Android 平台上,只能用一个字来形容:「乱」。
![](http://img.cdn.guoshuyu.cn/20230719_N30/image2.png)
这个「乱」不只是体现在 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 里进行渲染。
![](http://img.cdn.guoshuyu.cn/20230719_N30/image3.png)
问题也很明显,因为控件不会真实存在渲染的位置,所以此时的点击和对原生控件的操作,其实都是需要由 Flutter 这个 View 进行二次转发,另外因为控件是渲染在内存里,所以和键盘交互需要通过二级代理处理,这就产生了各种键盘输入的异常问题。
> 键盘问题突出在不同版本的 Android 兼容上。
## HC
1.2 版本开始支持 HC简单说就是直接把原生控件覆盖在 Flutter 上进行堆叠,如果出现 Flutter Widget 需要渲染在 Native Widget 上,就采用新的 `FlutterImageView` 来承载新图层。
![](http://img.cdn.guoshuyu.cn/20230719_N30/image4.png)
好处是原生视图是直接显示渲染,坏处就是在 Android 10 之前存在 GPU->CPU->GPU的性能损耗。
另外因为此时原生控件是直接渲染,所以需要在原生的平台线程上执行,这和 Flutter 的 UI 线程就存在线程同步问题,所以在此之前一些场景下会有画面闪烁 bug 。
## TLHC
3.0 版本开始支持 TLHC 模式,最初的目的是取代上面这两种模式,奈何最终只能共存下来,该模式下控件虽然在还是布局在该有的位置上,但是其实是通过一个 `FrameLayout` 代理 `onDraw` 然后替换掉 child 原生控件的 `Canvas` 来实现混合绘制。
![](http://img.cdn.guoshuyu.cn/20230719_N30/image5.png)
> 所以看到此时上图 `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](https://github.com/flutter/flutter/issues/109690)。
- 对于 TextureView 场景,有时候会出现不正常更新的异常情况[#103686](https://github.com/flutter/flutter/issues/103686) 。
现在你看出 PlatformView 的混乱了吧?从底层实现的不统一,到 API 再不同版本下不同的行为变化,这就是目前 Android 在 PlatformView 支持下的混乱生态,同时如果你对于目前 PlatformView 存在的问题刚兴趣,可以查阅以下相关 issue
- [#103686](https://github.com/flutter/flutter/issues/103686)
- [#109690](https://github.com/flutter/flutter/issues/109690)
- [#112712](https://github.com/flutter/flutter/issues/112712)
- [#130692](https://github.com/flutter/flutter/issues/130692)
所以,目前的 Android PlatformView 就给我一种既视感,好比魔兽世界里的平行分支:
- 地狱咆哮喝下了恶魔之血,绿皮吼爷打爆深渊领主玛诺洛斯
- 地狱咆哮拒绝喝恶魔之血,橙皮吼爷打爆深渊领主玛诺洛斯
虽然都是打爆了玛诺洛斯,虽然吼爷结局都一样扑街,但是中间的剧情走向还是有着极大的分歧,所以只能寄希望未来的世界线可以正常「收束」,能有一位「伯瓦尔」来结束这个混乱之治的时代。
![](http://img.cdn.guoshuyu.cn/20230719_N30/image6.png)