从零开始给 Surface 实现 SurfaceSyncGroup

背景

我的上一篇文章讲到了给 Surface 实现帧同步,作为实现帧同步的重要手段,在做动画需求,特别是跨进程同步渲染的需求中发挥了重要的作用。但还有另一种跨进程渲染的方案,在 Android13 被提了出来,这便是 SurfaceSyncGroupSurfaceSyncGroup 是用来替代 SurfaceSyncer 的方案的,SurfaceSyncer 我没用过(Android13 发布的时候我刚工作一年),所以就不提了,不过值得注意的是,这个用来帧同步的 SurfaceSyncGroup 居然是作为开放 API 发布的,难得见到谷歌慷慨了一回啊,之前图形绘制相关的 API,不管是 BLAST 相关还是 SurfaceSyncer 都是标明了 hide 接口的。

所以我为什么老是需要用到帧同步这些东西呢,因为我在去年年末的时候接手了桌面的窗口动画业务,特别是在后续的需求中引入多个 Surface 动画之后,有些场景并不能单纯依靠事务。比如绘制的同步就不能依靠我上一篇文章中的那种方法,这就涉及到 Surface 绘制的原理了,绘制是一种隐式的事务,在绘制操作从主线程转移到 RenderThread 上帧渲染完成之后,缓冲区就会挂到一个 Transaction 上,然后这个 Transaction 被发送到 SurfaceFlinger 去进行合成。这点可以通过 Winscope 来验证,在 WindscopeTranscation 一栏,是可以找到带有 BufferTransaction 的。

关于 SurfaceSyncGroup 的原理,网上一搜讲解很多,我这里就不详细展开了,简单来说就是通过一个根 group,来管理所有的子 group。对于这个根 group,如果是本地进程就由本地进程管理,如果是跨进程则通过 WMS 来管理。每个子 group 在绘制完成之后需要调用 markSyncReady 来通知根 group,然后根 group 会检查是否所有的子 group 已经全部准备好,如果全部准备好了,就调用 Transaction#apply 来应用事务。那怎么做到同步帧缓冲的呢?想起我上面说的了吗,绘制也是一种隐式的事务,最终也是要转为事务来应用的,只不过对于开发者来说是个黑箱,无感知而已。

阅读更多

从零开始给 Surface 实现帧同步

背景

参与系统开发的工程师应该都比较清楚,很多时候我们用 Surface 相关功能并不是使用 SurfaceView 的,而是自己创建一个 Surface,这样 Surface 的可定制程度就可以很高。有的需求需要做到使 TransactionSurface 里面绘制的内容的位置等几何信息进行同步(例如 ViewRootImpl 本身也是直接使用 Surface 来绘制,也是自行实现了一套帧同步逻辑)。

但遗憾的是,Surface 本身只提供了绘制等基础功能,本身并不存在帧同步的实现。那么,我们可以参照 ViewRootImpl 以及 SurfaceView 的帧同步实现来给单独的 Surface 实现帧同步功能。

阅读更多

ViewRootImpl#applyTransactionOnDraw 的坑

前情提要

之前在帮一同事做 SurfaceView 相关的内容(他本人对 SurfaceControl 相关接口不太熟悉)。在即将解决需要使用到帧同步接口的时候, 无意间看到了 SurfaceView 使用到了 ViewRootImpl#applyTransactionOnDraw 来做帧同步内容。

阅读更多

不要再用 binder 跨进程同步 ui 了

前言

最近在搞低端机上的性能优化,碰上一堆头疼的问题。有一个特别坑的玩意,那就是谷歌自己写的一套绝世“好代码“,用户手机上经常用到的负一屏的基础架构。本来我两年前开始搞这玩意的时候觉得没啥,滚动同步 UI 嘛,而且还要跨进程,那看起来 binder 通信不就是不二之选:桌面滚动到边缘触发 EdgeEffectEdgeEffect 来通过 binder 通知负一屏滚动了多少,然后负一屏那边在回调真正的滚动进度回桌面。这一套行云流水的操作下来可以说是没什么问题吧,至少其他厂家也在用,谷歌自己也在用。

阅读更多

Android.bp 代码 overlay 的一些问题

起因

之前由于业务上需要,需要按照不同的配置项给同一个项目打包出两个不同的产物(有点类似于渠道包,不过比渠道包复杂得多)。

示例

假如有一个项目,你需要从源码中获取某个标识符,来确定打包的渠道,你需要在源码编译时候去决定打包的内容。当然,具体场景肯定没这么简单,是包含了大量具体的复杂逻辑执行的。于是你创建一个项目,包含了如下的代码:

阅读更多

Android AIDL 解析

编译器做了什么

通过查看编译后产生的Java文件,观察其结构

构造后的AIDL类产生了一个同名接口,这个接口包含了AIDL内声明的所有方法,并且继承了android.os.IInterface。接口包含了两个静态类:

  • class Default
  • class Stub

Default类默认实现了AIDL接口以及IInterface的asBinder()方法,但都为空实现。

阅读更多

Android 各版本适配点

Android M 6.0

增加了运行时的权限申请

Android N 7.0

强制执行 StrictMode API,Intent 的 Uri 中 scheme 不能为 file 类型。如果要共享文件,需要使用 content:// 类型的 data。如果要共享文件,则使用 FileProvider。

阅读更多

Android Framework 单编

关于 Android11 下单编 Framework 的问题

在 Android 11 下,不知道为什么原本我在 Android 6.0 下使用的 mm 出现了错误(会把 test 也一并编译,导致问题),因此更换为下面的命令:

make -j16 SystemUI

make -j16 framework or make -j16framework-minus-apex

make -j16 services