返回

Graver异步渲染剖析

IOS

好的,以下是我以初始化指令生成的一篇专业技术博客文章:

Graver 是美团 18 年底开源的 iOS 异步渲染框架,因一些争议最近在 GitHub 上取消开源,不过有 fork 过的仓库我们还是可以看下其实现细节。这篇文章我们将聚焦其异步渲染相关的内容。

Graver框架的异步渲染

异步渲染的核心模块

异步渲染的核心模块,其中视图类从父到子主要为:

  • WMGAsynceDrawLayer
  • WMGAsynceDrawRootLayer
  • WMGAsynceDrawView
  • WMGAsynceDrawContainerView
  • WMGAsynceDrawContentView

其中只有 WMGAsynceDrawLayer 是纯系统原生实现的,父视图只需要直接使用 WMGAsynceDrawLayer 即可。至于其它视图则都是对 WMGAsynceDrawLayer 的进一步封装。

WMGAsynceDrawLayer 异步绘制原理

在分析 WMGAsynceDrawLayer 之前我们有必要先了解 CAEmitterLayer 的一个重要属性 renderInContext,该属性主要的作用是对 CAEmitterLayer 绘制过程进行修改,在默认情况下,renderInContext 为 NO,当给 renderInContext 赋予 YES 以后意味着所有子层都不再执行离屏渲染,而是直接在主线程直接渲染,这样可以减少绘制线程切换,并提升绘制性能。

WMGAsynceDrawLayer 的实现原理正是基于 CAEmitterLayerrenderInContext 属性,其内部通过 CAEmitterLayer 去负责内容绘制,当其绘制的时候实际上是在主线程,这里有个设计很有意思的地方在于:

  • 在调用 displayLayer: 函数时先判断 layer 是否已经添加到一个 layer tree 中,如果没有的话就手动执行一个 layout 阶段,在这个阶段 renderInContext 仍然为 NO,当一个 layer 被添加到一个 layer tree 之后就会开启离屏渲染。

  • 这里之所以在没有被添加到 layer tree 中时不开启离屏渲染主要是因为在创建 layer 的时候往往需要涉及到 display 层面的创建,而 display 层面的创建需要依赖 layout 的信息,所以这时就需要执行一个 layout 阶段。

  • 执行完 layout 阶段之后,此时假设 layer 还是没有被添加到 layer tree 中,WMGAsynceDrawLayer 内部就会自动给 renderInContext 赋值为 YES,以此确保 WMGAsynceDrawLayer 所要呈现的内容在主线程绘制。

其他视图的封装

除了 WMGAsynceDrawLayer 以外,其它的封装视图都是基于 WMGAsynceDrawLayer 来实现的,所以其实 WMGAsynceDrawLayer 是整个框架的核心。

WMGAsynceDrawRootLayer 为 WMGAsynceDrawLayer 的直接父类,其主要起到管理 WMGAsynceDrawLayer 的作用,从设计模式来说类似于一个组合管理者角色。WMGAsynceDrawView 则是一个普通视图,其内部包含了一个 WMGAsynceDrawRootLayer。

WMGAsynceDrawContainerView 相比于 WMGAsynceDrawView 有较大的修改,其内部并没有持有 WMGAsynceDrawRootLayer,而是持有了一个 WMGAsynceDrawContentView。WMGAsynceDrawContentView 为 WMGAsynceDrawLayer 的直接父类,其主要的作用在于维护子视图的绘制顺序,以及提供一个回调函数,以便在绘制开始和结束的时候可以被调用。

结语

以上就是Graver异步渲染框架的核心实现,由此可见,框架的设计是比较严谨和巧妙的,这也是其能够应用于美团众多业务的原因之一。