Android Chromium 沉浸式地址栏[2]

92 阅读5分钟

前要

前文:Android Chromium 沉浸式地址栏

文章中介绍了 Chromium 内核地址栏通知上层偏移的回调 ViewAndroidDelegate.onTopControlsChanged 在此回调中对地址栏组件进行对应的 Y 轴偏移即可,但是这只是地址栏的 Y 轴偏移,网页内容并没有跟随偏移,如果也对网页组件进行偏移,会发现这样的现象:

在回调中同时对地址栏组件偏移 YOffset,网页组件偏移 YOffset,实际呈现出来的效果网页组件并没有与地址栏完成无缝的偏移联动,会发现地址栏偏移多一点或者网页组件偏移多一点,在偏移的过程中总会存在或多或少的细微抖动,体验不好.

这是因为偏移值是 Chromium 绘制过程中提供到的,在地址栏偏移的过程中,Chromium 实际绘制内容已经偏移到下一个位置,滑的越快,抖动越明显; Chromium 浏览器没有这样的现象是因为 Chromium 并没有对网页组件进行偏移而是在网页内容 GPU 绘制的过程中就完成了对绘制层的偏移,这样就不存在网页组件偏移与网页实际GPU绘制偏移的误差了,就能达到网页内容与地址栏组件偏移的无缝联动

原理

1. Chromium Layer Bind Compositor

CompositorView 是 Chromium 浏览器上提供 Surface 的绘制组件,CompositorImplAndroid 根据 CompositorView 提供的 Surface 创建 Render 可以上屏的绘制通道,同时 Compositor 负责将所有的内容 Layer 合成绘制上屏

1.jpg

CompositorImpl 创建时需要传入 WindowAndroid 参数将该合成器与当前的Android窗口绑定上,通过图中所式的流程,完成 WindowAndroid 与 Layer 的绑定,WindowAndroid Layer 与 Compositor 中 LayerTreeHost 的绑定

2.jpg

2. Create Layer To Draw WebContent

当 WebContents 初始化时会为当前的 WebContents 创建 ViewAndroid 并生成相应的 Layer,当网页加载的时候,通过 WebContentsViewAndroid 为当前的网页创建用于绘制的 SurfaceLayer,同时将该 Layer 加入到 WebContents 的 ViewAndroid Layer 上,上树上屏

3.jpg

3.1.png

3. Add WebContent Layer To RootLayer

4.jpg

TabImpl 初始化 WebContents 时,将 WebContents 所绑定的窗口 Layer 挂载到 TabImpl 对应的 Layer 上

4. Bind TabLayer To Compositor Layer To Render Content

在合成器工作之前,会通过 CompositorClient 中 UpdateLayerTreeHost 回调通知 CompositorView 更新当前的 LayerTree,CompositorView 在这个回调中完成当前 Tab Layer 向 CompositorView layer 的绑定,CompositorView 在流程 1 中就已经完成了向 Compositor 的绑定

通过以上的 4 个流程拆解,就完成了网页内容上屏绘制的整体前期准备工作,从这里可以看到整个网页的绘制合成都是基于 Layer 的,因此 Chromium 如果想要达到地址栏与网页的无缝联动,也必然也必然是基于 Layer 在合成阶段完成 Layer 偏移的

5.jpg

5. Register BrowserControls OffsetTag For WebContent Layer

将定义好的 BrowserControlsOffsetTagDefinitions 注册给当前的 Layer,BrowserControlsOffsetTagDefinitions 包含了地址栏偏移相关的 OffsetTag,后续在合成绘制时,此处申明好的 OffsetTag 作为 Key 与 Layer 设定的 OffsetTag 相匹配,若匹配上,则取出相应的 Offset 偏移对该 Layer 进行偏移;若匹配不上则忽略该 OffsetTag 对应的偏移值(这么设计是因为一次绘制中是有 N 个 Layer 的,不一一对应那么合成时 Layer 的位置就全乱套了)

6.jpg

6. Sync The OffsetTag To BrowserControlsManager To Set Offset Value

将 5 中申明好的 OffsetTag 设置到 Render 进程中的 BrowserControlsManager,后续 Render 在生成/更新合成器内容帧数据时按照申明好的 OffsetTag 分别填入 BrowserControlsManager 中的地址栏偏移值

7.jpg

向帧数据中根据 OffsetTag 填入 BrowserControlsManager 偏移数据,具体逻辑在 LayerTreeHostImpl.MakeRenderFrameMetadata 中

CompositorFrameMetadata LayerTreeHostImpl::MakeCompositorFrameMetadata() {
    ...
    metadata.offset_tag_values.emplace_back(top_controls_offset_tag, offset2d);
    ...
    metadata.offset_tag_values.emplace_back(content_offset_tag, offset2d);
}

8. Surface Aggregator Offset WebContent Layer To Draw

SurfaceAggregator对最终的绘制数据按照OffsetTag,当与 Layer 所设置的 OffsetTag 一一对应上时对该 Layer 导入 BrowserControls 偏移

8.jpg

从 CompositorFrameMetadata 取出 Render 根据 OffsetTag 设置的地址栏偏移,代码逻辑如下:

9.png

结语

通过以上的步骤拆解基本了解了 Chromium 是在 GPU 合成绘制阶段完成地址栏偏移的流程细节,如何实现那么按照上述的步骤完成关键性的OffsetTag声明定义即可