Flutter 正在推进全新 PlatformView 实现 HCPP, 它又用到了 Android 上的什么黑科技

跨平台开发里的 PlatformView 实现一直是一个经久不衰的话题,在之前的 《深入 Flutter 和 Compose 的 PlatformView 实现对比》 我们就详细聊过 Flutter 和 Compose 在 PlatformView 实现上的异同之处,也聊到了 Compose 为什么在相同实现上对比 Flutter 会更有优势的原因。

那么随着 3.29 的发布,恰好关注到其实 Flutter 在 Android 的 PlatformView 上其实正在落地另外一种实现,而这种实现目前看来可以做到在 HC 的基础上得到更好的性能,所以也被暂时称为 HCPP。

在聊 HCPP 之前我们再简单回顾下 Flutter 在 Android 上的 PlatformView 实现模式:

  • VD:最老的实现模式,利用副屏 VirtualDisplay 的相关支持在内存实现原生控件的模拟绘制和纹理提取
  • HC:通过直接将原生控件 add 到 FlutterView 上,然后通过新的 FlutterImageView 提供新的 Surface 来实现控件 UI 堆叠合成
  • TLHC:还是直接将原生控件 add 到 FlutterView 上,但是中间利用通过 parent 替代掉 child 的 canvas,让原生控件绘制的对应的 surface.lockHardwareCanvas

有点抽象?,没关系,后面有简单的直观例子。

在这个过程中几种模式各有优劣,比如:

  • TLHC 模式不支持 SurfaceView 等控件,因为 SurfaceView 有自己独立的 Surface 和 Canva ,它的 Surface 直接来自 SurfaceFlinger ,也就是当前 Window 下
  • TLHC 模式与异步更新 View 一起使用时(如 TextureView 或基于 GL 的渲染器),需要在更新内容时在对应 PlatformView 显式调用 invalidate 才能保证正确渲染,例如 textureViewonSurfaceTextureUpdated 调用 mapView.invalidate
  • HC 模式因为不同 API 版本和线程问题,会有同步和额外的性能开销
  • ····

所以目前这三种模式是协同工作,例如:

  • initAndroidView : 默认会使用最新的模式,目前就是 TLHC,如果遇到不支持的就降级到 VD

  • initSurfaceAndroidView: 默认会使用最新的模,目前就是 TLHC,如果遇到不支持的就降级到 HC

  • initExpensiveAndroidView:直接强制使用 HC 模式

那么,回到本次的主题 ,针对全新的 HCPP 实现,Flutter 提供了全新的 API initHybridAndroidView ,可以看到,它需要 Vulkan 和 API 34 的环境才支持使用,如果从这点看,它的通用性又相对较低

那为什么它需要 API 34 呢?这和它直接使用 SurfaceControl 的 API 逻辑有很大关系,另外,从 Engine 的判断逻辑上可以看到,目前除了判断 Vulkan 和 API 之后,还需要配置对应的 EnableSurfaceControl 才可以测试 HCPP,也就是在 AndroidManifest 增加:

<meta-data
       android:name="io.flutter.embedding.android.EnableSurfaceControl"
       android:value="true" />

接着就让我们来看看 HCPP 和其他几种模式有什么区别,其实主要就是和 HC 和 TLHC 进行比较,这里首先做一个容器 Demo ,主要是通过混合 Flutter 和原生控件的效果来区分它们的实现,让 platformView 渲染在两个 Flutter Widget 之间:

return MaterialApp(
  debugShowCheckedModeBanner: false,
  home: Stack(
    alignment: AlignmentDirectional.center,
    children: <Widget>[
      ///200x200的绿色 Flutter 方块
      TextButton(
        key: const ValueKey<String>('AddOverlay'),
        onPressed: _togglePlatformView,
        child: const SizedBox(width: 190, height: 190, child: ColoredBox(color: Colors.green)),
      ),
      
      ///200x200的原生控件,这里用的是一个红色的原生方块
      if (showPlatformView) ...<Widget>[
        SizedBox(width: 200, height: 200, child: widget.platformView),
        
        
        ///黄色 Flutter 条
        TextButton(
          key: const ValueKey<String>('RemoveOverlay'),
          onPressed: _togglePlatformView,
          child: const SizedBox(
            width: 800,
            height: 25,
            child: ColoredBox(color: Colors.yellow),
          ),
        ),
      ],
    ],
  ),
);

之后我们可以通过 initExpensiveAndroidView 强制 PlatformView 使用 HC 模式,可以看到,在 HC 模式下出现很多经典的原生层,特别是多了 FlutterImageView 的转换还有它的子类 PlatformOverlayView

我们通过 3D 图可以看到,红色的原生 BoxPlatformView 正常被渲染,然后在其之上的 Flutter 控件(一部分黄色条),是通过 FlutterImageView 的子类 PlatformOverlayView 提供的 Surface 独立渲染:

然后我们再通过 initAndroidView 来使用 TLHC 模式,可以看到此时是通过 PlatformViewWrapper 这个 parent 作为容器来承载,而 PlatformViewWrapper 会替换掉原生 BoxPlatformView 的 Canvas,让原生控件的内容渲染到指定 Surface 上 :

我们通过原生 3D 图可以看到,此时的 BoxPlatformView 其实在原生层并没有绘制任何东西,因为其 Canvas 是被替换到内存的 SurfaceTexture 上:

说到 SurfaceTexture ,这个插个题外话,对于 THLC 和 VD 而言,现在创建纹理时是会根据 Android API 来使用不同实现,其中 SurfaceProducer 比较特殊:

因为在此之前,Android 上的 Flutter 引擎支持两个外部渲染源:SurfaceTexture (OpenGLES 纹理)和 ImageReader(GPU-ready buffer),其中 Image.getHardwareBuffer 需要 API 28 支持。

而为了适配 Impeller 团队提出了 SurfaceProducer 概念,让 Android 在运行时选择“最佳”渲染 Surface,除了 PlatformView 场景,在外界纹理场景也需要适配的情况:

- TextureRegistry.SurfaceTextureEntry entry = textureRegistry.createSurfaceTexture();
+ TextureRegistry.SurfaceProducer producer = textureRegistry.createSurfaceProducer();

- Surface surface = new Surface(entry.surfaceTexture());
+ Surface surface = producer.getSurface();

那么我们看 HCPP,通过 initHybridAndroidView 我们启用了 HCPP 模式,可以看到,此时 UI 的层级结构类似 TLHC, 但是 parent 使用的是 HC 模式中的 FlutterMutatorView

然后我们看 3D 效果,原生控件 BoxPlatformView 其实可以被完整被渲染,证明其 Canvas 并没有被替代,那么这里就有一个神奇的问题了:Flutter 的黄色控件,是如何渲染到红色的 BoxPlatformView 之上的

这就不得不提 PlatformViewsController2 ,作为一个 HCPP 的临时对象,它的实现里有一个关键的对象 SurfaceControl ,并且在事务提交时通过 setLayer 设置了 z 轴为 1000

我们可以看提交更改里,基本上全新的 PlatformViewsController2 核心逻辑都在于操作 SurfaceControl

在 Android 里,SurfaceControl 是一种用于管理和操作与显示系统相关的图形资源的类,简单说就是与 Surface 相关的操作,SurfaceControl 可以用于创建和管理 Surface ,它是和SurfaceFlinger 交互的一个主要接口,交互的方式则是通过 Transaction

而在 HCPP 里,我们可以看到,此时的 Surface 正是通过一个全新的 SurfaceControl 创建得到,而这个 SurfaceControlTransaction 来自 FlutterView

也就是,在 HCPP 模式里,Flutter 通过 SurfaceControl.Transaction 构造了一个全新的 Surface 用于 SurfaceFlinger 合成,并且还通过 setLayer 将 Surface 的 z 轴设置到了 1000 ,而这个 1000 就是黄色 Flutter 控件可以渲染到原生红色方块之上的原因

举个例子,我们将 SurfaceControl 这部分代码复制到一个简单的纯原生项目里,并且同样对创建的 Surface 设置 1000 和绘制红色:

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
  
    	 ········
   
       // 获取 FrameLayout
        FrameLayout rootView = findViewById(R.id.container);
        rootView.postDelayed(new Runnable() {
            @Override
            public void run() {
                final SurfaceControl.Builder surfaceControlBuilder = new SurfaceControl.Builder();
                surfaceControlBuilder.setBufferSize(500, 500);
                surfaceControlBuilder.setFormat(PixelFormat.RGBA_8888);
                surfaceControlBuilder.setName("Flutter Overlay Surface");
                surfaceControlBuilder.setOpaque(false);
                surfaceControlBuilder.setHidden(false);
                final SurfaceControl surfaceControl = surfaceControlBuilder.build();
                final SurfaceControl.Transaction tx =
                        binding.container.getRootSurfaceControl().buildReparentTransaction(surfaceControl);
                tx.setLayer(surfaceControl, 1000);
                tx.apply();
                surface1 = new Surface(surfaceControl);
                surfaceControl1 = surfaceControl;

                // 在 SurfaceView 上绘制一些内容
                drawOnSurface(surface1, Color.RED);   
            }
        }, 2000);

}

private void drawOnSurface(Surface surface, int color) {
    Canvas canvas = surface.lockCanvas(null);
    if (canvas != null) {
        canvas.drawColor(color);
        surface.unlockCanvasAndPost(canvas);
    }
}

然后我们看最终绘制的效果,可以看到绿色背景的 FrameLayout 是在 WebView 下方的,但是通过 container.getRootSurfaceControl() 创建的 Surface 因为 z 轴为 1000 的原因,最终红色方块会绘制到 WebView 之上:

另外,在目前逻辑中,Engine 如果判断当前帧如果不存在 PlatformView ,并且上一帧存在 PlatformView,那么就会调用 hideOverlaySurface2 从而直接触发 Java 层面的 platformViewsController2.hideOverlaySurface() ,进而隐藏不需要的 Layer :

  if (!FrameHasPlatformLayers()) {
    frame->Submit();
    // If the previous frame had platform views, hide the overlay surface.
    if (previous_frame_view_count_ > 0) {
      jni_facade_->hideOverlaySurface2();
    }
    jni_facade_->applyTransaction();
    return;
  }

所以可以看到,HCPP 主要就是通过 SurfaceControl 来构造一个高层级的 Surface 从而实现最终绘制时混合覆盖的问题,这和我们之前聊 《深入 Flutter 和 Compose 的 PlatformView 实现对比》 里 Compose 可以在 PlatformView 里直接使用 SurfaceView 的道理类似,都是 SurfaceFlinger 合成时的层级操作。

至于为什么需要 API 34, 主要也是 SurfaceControl 对应的一些 API 需要的版本都很高,另外我依稀记得, Android 14 在通过 SurfaceControl 实现低延迟绘图时,可以更好支持 Canvas API 通过硬件加速绘制到 HardwareBuffer :

如果对于 Engine 部份逻辑感兴趣的,也可以看 external_view_embedder_2#SubmitFlutterView 这部分逻辑里如何通过 GetLayer 去创建 FlutterOverlaySurface

目前 HCPP 还处于 main 分之的 beta 状态,如果后续正式落地,那对于 Android PlatformView 实现将会是存在 4 种组合模式,相比较 iOS 端多个 CALayer 的合成模式,Android 的 PlatformView 可以说是一路坎坷至今。

最后,你觉得 HCPP 会成为落地为全新的 PlatformView 支持吗?

PS :io.flutter.embedding.android.EnableSurfaceControl 标识还用于控制 Impeller 内部使用 Vulkan swapchain 或者 Android SurfaceControl (AHB swapchain),在 Android SurfaceControl 模式下,Java 端创建的 Transaction 会链接到 AHB swapchain。

当然, AHBSwapchainVK 交换链实现并非在所有 Android 版本上都可用,一般不支持的话,会回退到 KHR swapchain。

参考链接

  • https://github.com/flutter/flutter/issues/163073

  • https://github.com/flutter/flutter/pull/161829

  • https://github.com/flutter/flutter/issues/144184

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/973387.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Qt/C++面试【速通笔记一】

Qt 信号与槽机制 什么是信号&#xff08;Signal&#xff09;和槽&#xff08;Slot&#xff09;&#xff1f; 在Qt中&#xff0c;信号&#xff08;Signal&#xff09;和槽&#xff08;Slot&#xff09;是实现对象之间通信的一种机制。信号是对象在某些事件发生时发出的通知&…

《跟李沐学 AI》AlexNet论文逐段精读学习心得 | PyTorch 深度学习实战

前一篇文章&#xff0c;使用 AlexNet 实现图片分类 | PyTorch 深度学习实战 本系列文章 GitHub Repo: https://github.com/hailiang-wang/pytorch-get-started 本篇文章内容来自于学习 9年后重读深度学习奠基作之一&#xff1a;AlexNet【下】【论文精读】】的心得。 《跟李沐…

【科研绘图系列】R语言绘制小提琴图、散点图和韦恩图(violin scatter plot Venn)

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载画图1画图2画图3画图4画图5画图6画图7参考介绍 【科研绘图系列】R语言绘制小提琴图、散点图和韦恩图(violin & scatter plot & Venn) 加载R包 library…

IMX6ULL的ALT0、ALT1、ALT2、ALT3、ALT4等是啥意思?

在IMX6ULL的手册IMX6ULLRM.pdf中&#xff0c;发现了题目中这些描述&#xff0c;相关截图如下&#xff1a; 那么红框中的ALT0、ALT1、ALT2、ALT3、ALT4等是啥意思呢&#xff1f; 在IMX6ULL及其他NXP&#xff08;Freescale&#xff09;芯片中&#xff0c;ALT0、ALT1、ALT2、ALT…

Android Http-server 本地 web 服务

时间&#xff1a;2025年2月16日 地点&#xff1a;深圳.前海湾 需求 我们都知道 webview 可加载 URI&#xff0c;他有自己的协议 scheme&#xff1a; content:// 标识数据由 Content Provider 管理file:// 本地文件 http:// 网络资源 特别的&#xff0c;如果你想直接…

DeepSeek 冲击(含本地化部署实践)

DeepSeek无疑是春节档最火爆的话题&#xff0c;上线不足一月&#xff0c;其全球累计下载量已达4000万&#xff0c;反超ChatGPT成为全球增长最快的AI应用&#xff0c;并且完全开源。那么究竟DeepSeek有什么魔力&#xff0c;能够让大家趋之若鹜&#xff0c;他又将怎样改变世界AI格…

神经网络八股(1)

1.什么是有监督学习&#xff0c;无监督学习 有监督学习是带有标签的&#xff0c;无监督学习是没有标签的&#xff0c;简单来说就是有监督学习的输入输出都是固定的&#xff0c;已知的&#xff0c;无监督学习输入是已知的&#xff0c;输出是不固定的&#xff0c;无监督学习是通…

DeepSeek 助力 Vue 开发:打造丝滑的瀑布流布局(Masonry Layout)

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…

【分布式理论14】分布式数据库存储:分表分库、主从复制与数据扩容策略

文章目录 一、分表分库1. 数据分表的必要性与方式2. 数据分库原则与优势 二、主从复制1. 读写分离架构设计2. 数据复制方式3. MySQL实现主从复制4. MySQL主从复制实践与高可用方案 三、数据扩容 随着业务的不断发展和数据量的增长&#xff0c;传统的单机关系型数据库已经逐渐不…

从传统到轻量级5G:网络架构演变与优化路径

轻量级5G​​​​ 随着5G技术的不断发展&#xff0c;通信网络架构正经历着前所未有的变革。传统的5G核心网架构虽然在性能和容量方面表现出色&#xff0c;但在灵活性、部署效率以及成本控制方面却面临一些挑战。为了应对日益复杂的通信需求&#xff0c;轻量级5G核心网成为了一种…

搭建Kubernetes (K8s) 集群----Centos系统

前期准备 准备3台Linux虚拟机&#xff08;CentOS系统&#xff09;&#xff0c;参考 https://carry.blog.csdn.net/article/details/144578009https://carry.blog.csdn.net/article/details/144578009搭建Docker环境&#xff0c;参考 https://carry.blog.csdn.net/article/de…

OpenSSL实验

文章目录 一、OpenSSL安装二、OpenSSL配置常见路径查找配置文件的方法示例**1. 配置文件结构****2. 主要段落及其作用****(1) 默认段&#xff08;Default Section&#xff09;****(2) OID段&#xff08;OID Section&#xff09;****(3) CA相关段&#xff08;CA Section&#xf…

51单片机-按键

1、独立按键 1.1、按键介绍 轻触开关是一种电子开关&#xff0c;使用时&#xff0c;轻轻按开关按钮就可使开关接通&#xff0c;当松开手时&#xff0c;开关断开。 1.2、独立按键原理 按键在闭合和断开时&#xff0c;触点会存在抖动现象。P2\P3\P1都是准双向IO口&#xff0c;…

DeepSeek动画视频全攻略:从架构到本地部署

DeepSeek 本身并不直接生成动画视频,而是通过与一系列先进的 AI 工具和传统软件协作,完成动画视频的制作任务。这一独特的架构模式,使得 DeepSeek 在动画视频创作领域发挥着不可或缺的辅助作用。其核心流程主要包括脚本生成、画面设计、视频合成与后期处理这几个关键环节。 …

EasyRTC智能硬件:实时畅联、沉浸互动、消音护航

在当今智能硬件迅猛发展的时代&#xff0c;音视频通讯技术已成为设备与用户、设备与设备间不可或缺的沟通纽带。而EasyRTC&#xff0c;凭借其无可比拟的实时性能、卓越的互动感受以及强大的交互实力&#xff0c;正逐步演变为智能硬件领域的“超级动力”核心。特别是其倾力打造的…

[AI相关]Unity的C#代码如何简写

是一个某培训机构的飞行棋教学源码 不知道&#xff0c;是否有人想知道怎么可以简写 &#xff08;这个问AI&#xff0c;DeepSeek也应该找不到答案的&#xff09; 静态变量 属性引用 单例 注入 一些UnityEvent特性就不说了。。。 IL 注入 运算符号改写

ubuntu 执行 sudo apt-get update 报错

记录一下&#xff0c;遇到这个问题了&#xff0c;网络上看到的解决办法&#xff0c;亲测有效 执行sudo apt-get update ,却报以下错误&#xff0c;“SECURITY: URL redirect target contains control characters rejecting ” 经检查发现&#xff0c;/etc/apt/source.list 下的…

蓝桥杯学习大纲

&#xff08;致酷德与热爱算法、编程的小伙伴们&#xff09; 在查阅了相当多的资料后&#xff0c;发现没有那篇博客、文章很符合我们备战蓝桥杯的学习路径。所以&#xff0c;干脆自己整理一篇&#xff0c;欢迎大家补充&#xff01; 一、蓝桥必备高频考点 我们以此为重点学习…

【插件】前端生成word 文件

文章目录 1、背景2、方式一&#xff1a;html-docx-js2.1 具体代码2.2 前端生成word文件的样式2.3 总结 3、方式二&#xff1a;pizzip docxtemplater3.1 具体代码3.2 前端生成word文件的样式3.3 总结 4、参考链接 1、背景 在实际开发中&#xff0c;业务需要&#xff0c;需要把数…

4. grafana(7.5.17)功能菜单简介

点击可以返回home页面 搜索Dashboard 新建按钮&#xff1a;用户创建Dashboard、文件夹。以及导入外部&#xff08;社区&#xff09;Dashboard 用于查看活管理Dashboard&#xff0c;包括home、Manage、playlists、snapshots功能 explore&#xff08;探索&#xff09;&#x…