57

美团开源Graver框架:用“雕刻”诠释iOS端UI界面的高效渲染

 5 years ago
source link: https://tech.meituan.com/waimai_graver.html?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Graver 是一款高效的 UI 渲染框架,它以更低的资源消耗来构建十分流畅的 UI 界面。Graver 独创性的采用了基于绘制的视觉元素分解方式来构建界面,得益于此,该框架能让 UI 渲染过程变得更加简单、灵活。目前,该框架已经在美团 App 的外卖频道、独立外卖 App 核心业务场景的大多数业务中进行了应用,同时也得到美团外卖内部技术团队的认可和肯定。

App 渲染性能优化是一个普遍存在的问题,为了惠及更多的前端开发同学,美团外卖 iOS 开发团队将其进行开源,Github 项目地址与使用文档详见: https://github.com/Meituan-Dianping/Graver 。我们希望该框架能够应用到更广阔的业务场景。当然,我们也知道该框架尚有待完善之处,也希望能与更多技术同行一起交流、探讨、共建。

前言

我们为什么需要关注界面的渲染性能?App 使用体验主要包含产品功能、交互视觉、前端性能,而使用体验的好与坏,直接影响用户持续使用还是转而使用其他 App,所以我们非常关注 App 的渲染性能。而且在互联网产品流量竞争愈发激烈的大背景下,优质的使用体验可以为现有用户提供更好的服务,进而提高用户转化和留存,这也意味着创收、盈利。

naI3IvN.png!web

图1 使用体验与转化、留存

背景

美团外卖 App 从2013年成立至今,已经走过了五个春秋,在技术层面先后经历了快速验证、模块化、精细化和平台化四个阶段,产品形态上也日趋成熟。在此期间,我们构建并完善了监控、报警、容灾、备份等各项基础设施,Metrics 即是其中的性能监控系统。

曾经一段时间,我们以外卖 App 首页商家卡片列表为例,通过 Metrics 性能监控系统发现其在 FPS、CPU、Memory 等方面的各项指标并不理想。于是,通过 Xcode 自带的 TimeProfile 等性能检测工具,然后结合代码分析等手段找到了现存性能瓶颈。与此同时,我们梳理其近半年的迭代版本需求发现,UI 往往需要根据不同场景甚至不同用户展示不同的内容。为了不断迎合用户的需求,快速应对市场变化,这种特征还会持续存在。然而,它会带来以下问题:

  • 视图层级愈加复杂、视图数量愈加众多,从版本长期迭代来看是潜在的性能瓶颈点。
  • 如何快速、高效支撑 UI 变化,同时保证不会二次引入性能瓶颈。

f6zu6zF.png!web

图2 影响渲染性能、研发效率的瓶颈点

Graver 介绍

为了解决现存的性能瓶颈以及后续潜在的性能瓶颈,我们期望构建一套解决方案,该方案能在充分满足外卖业务特征的前提下,以标准化、一站式的方式解决 iOS 端 App 的渲染性能问题,并且对研发效率有一定提升, Graver(雕工)框架应运而生。

因为 Graver 独创性地采用了全新的视觉元素分解思路,所以该框架使用起来十分灵活、简单。我们先来看一下 Graver 的主要特点:

性能表现优异

以外卖 App 首页商家列表为例,应用 Graver 之后5分位滚动帧率从满帧的84%提升至96%,50分位几乎满帧;CPU 占用率下降了近6个百分点,有效提升了空闲 CPU 的资源利用率,降低了峰值 CPU 的占用率。如图3所示:

QBBnQ37.png!web

图3 优化前后技术指标对比

“一站式”异步化

Graver 从文本计算、样式排版渲染、图片解码,再到绘制,实现了全程异步化,并且是线程安全的。使用 Graver 可以一站式获得全部性能优化点,可以让我们:

  • 不再担心散点式的“遇见一处改一处”的麻烦。
  • 不再担心离屏渲染等各种可能导致性能瓶颈的问题,以及令人头痛的解决办法。
  • 不再担心优化会有遗漏、优化不到位。
  • 不再担心未来变化可能带来的任何性能瓶颈。

性能消耗的“边际成本”几乎为零

Graver 渲染整个过程除画板视图外完全没有使用 UIKit 控件,最终产出的结果是一张位图(Bitmap),视图层级、数量大幅降低。以外卖 App 首页铂金展位视图为例,原有方案由58个控件、12层级拼接而成;而应用 Graver 后仅需1个视图、1级层级绘制而成。 伴随着需求迭代、视觉元素变化,性能消耗恒属常数级。如图4所示:

2AjUvmV.png!web

图4 外卖 App 铂金展位应用 Graver 前后对比

渲染速度快

Graver 并发进行多个画板视图的渲染、显示工作。得益于图文混排技术的应用,达到了内存占用低,渲染速度快的效果。由于排版数据是不变的,所以内部会进行缓存、复用,这又进一步促进了整体渲染效率。Graver 既做到了高效渲染,又保证了低时延页面加载。

Rjaaimr.png!web

图5 渲染效率说明

以“少”胜“繁”

Graver 重新抽象封装 CoreText、CoreGraphic 等系统基础能力,通过少量系统标准图形绘制接口即可实现复杂界面展示。

基于位图(Bitmap)的轻量事件交互系统

如上述所说,界面展示从传统的视图树转变为一张位图,而位图不能响应、区分内部具体位置的点击事件。Graver 提供了基于位图的轻量事件交互系统,可以准确识别点击位置发生在位图的哪一块“绘制单元”内。该“绘制单元”可以理解为与我们一贯使用的某个具体 UI 控件相对应的视觉展示。使用 Graver 为某一视觉展示添加事件如同使用系统 UIButton 添加事件一样简单。

全新的视觉元素分解思路

Graver 一改界面编程思路,与传统的通过控件“拼接”、“添加”,视图排列组合方式构建界面不同,它提供了灵活、便捷的接口让我们以“视觉所见”的方式构建界面。这一特点在下文 Graver使用 中详细阐述,正是因为该特点实现了研发效率的提升。

Graver 使用

Graver 引入了全新的视觉元素分解的思路。借助该思路可以实现通过一种对象来表达任一视觉元素、甚至是任一视觉元素的组合,从而消除界面布局的复杂性。

我们先来回顾下传统界面的构建方式,以外卖 App 商家卡片其中一种样式为例,如图6所示:

zU7rmmz.png!web

图6 外卖 App 商家卡片

在实现商家卡片的界面样式时,通常会根据视觉上的识别、交互要求来建立界面展示与系统提供的 UI 控件间的映射关系。以标号②位置的样式为例,在考虑复用的情况下通常这部分会使用三个系统控件来完成,分别是左侧蓝底的“预订”使用 UILabel 控件、右侧的蓝色边框“2.26.21:30起送”使用 UILabel 控件、把左右两侧 UILabel 控件装起来的 UIView 控件;在确定好采用的 UI 控件之后,需要针对展示样式分门别类的设置各个控件的渲染属性来实现图示 UI 效果,渲染属性通常一部分预设,一部分根据业务数据的不同再进行二次设置;其次,设置各个控件的内容属性实现业务数据内容的展示,展示的内容一般是网络业务数据经逻辑处理、加工后的数据。如果涉及到点击事件,还需要添加手势或者更换成 UIButton 控件。接下来,需要根据视觉要求实现排版逻辑,以标号⑧、⑨为例,当标号⑧位置的数据没有的情况下,需要上提标号⑨位置的“美团专送”到图示标号⑧位置。诸如类似的排版逻辑随处可见。对于图示任一位置的展示内容都存在上述的循环思考、编写工作。随着界面元素的增加、变化,问题会变得更加复杂。

传统的界面构建方式其实是在 UI控件的维度去分解视觉元素,具体是做以下四方面的编写工作:

  • 控件选择 :根据展示内容、样式、交互要求确定采用哪种系统控件。
  • 布局信息 :UI 控件的大小、位置,即 Frame。
  • 内容信息 :UI 控件展示出来的业务数据,如标号①位置的“星巴克咖啡店”。
  • 渲染信息 :UI 控件展示出来的效果,如字体、字号、透明度、边框、颜色等。

最后,将各个控件以排列组合方式合成为一棵视图树。

Graver 框架提供了以画板视图为基础,通过对更底层的 CoreText、CoreGraphic 框架封装,以更贴近“视觉所见”的角度定义了全新视觉元素分解、界面展示构建的过程。

通常“视觉所见”可划分为两部分:静态展示、动态展示。静态展示包含图片、文本;动态展示包含视频、动画等。在视觉展示全部为静态内容的时候,一个 Cell 即是一个画布,除此以外没有任何 UI 控件;否则,可以按需灵活的进行画布拆分来满足动画、视频等需要。

mqeAjuj.png!web

图7 画板和传统视图树

以图6商家卡片中标号②、⑧为例,新实现方式的伪代码是这样的:

WMMutableAttributedItem *item = [[WMMutableAttributedItem alloc] init];
[[[[item appendImage:[[UIImage wmg_imageWithColor:"blue"] wmg_drawText:"预订"]] 
         appendImage:[[UIImage wmg_imageWithColor:"clear" borderWidth:1 borderColor:"blue"] wmg_drawText:"2.26.21:30起送"] 
         appendWhiteSpaceWithWidth:"width"]//总体宽度减去②和⑧的宽度总和剩余部分
         apendText:"50分钟|2.5km"];

上述实现方式即是把标号②、⑧部分作为一个整体来实现,任何单一系统控件都无法做到这一点。

Graver 渲染原理

Y3iiQbb.png!web

图8 Graver 工作时序

如图8所示,Graver 涉及多个队列间的交互,以外卖 App 商家列表为例,整体流程如下:

  • 主线程构建请求参数,创建请求任务并放入网络线程队列中,发起网络请求。
  • 网络线程向后端服务发起请求,获得对应的业务模型数据(如包含了店铺名称,商家头图,评分,配送时长,客单价,优惠活动等店铺属性的商家卡片列表)。
  • 网络线程创建包含业务模型数据(如商家卡片列表)的排版任务,提交到预排版线程处理,进入预排版流程。预排版队列取出排版任务,交由布局引擎计算 UI 布局,将业务模型解析成可被渲染引擎直接处理的,包含布局、层级、渲染信息的排版模型。解析结束后,通知主线程排版完成。
  • 主线程获取排版模型后,随即触发内容显示。根据相对屏幕位置及出现的先后顺序,创建包含将需要显示区域信息的绘制任务,放入异步绘制线程队列中,发起绘制流程。
  • 异步绘制线程队列取出绘制任务,进行图文绘制,最终输出一张包含了图文内容(如商家卡片)的图片。绘制任务结束后,通知主线程队绘制完成,主线程随后展示绘制区域。

整体按照队列间串行、队列内并行的方式执行。

业务应用

Graver 在外卖内部发布之后,我们也将其推广到更多的业务线,并希望 Graver 能够成为对业务开展有重要保障的一项基础服务。经过半年多的内部试用,Graver 的可靠性、渲染性能、业务适应能力也受到外卖内部的肯定和认可。截止发稿时,Graver 已经基本覆盖了美团 App 的外卖频道、独立外卖 App 核心业务场景的大多数业务。下面列举 Graver 在外卖业务的部分应用案例:

Bb2UnaE.png!web

经验总结

总结一下,对于界面渲染性能优化而言,要站在一个更高角度来思考问题的解决方案。横向上,从普适性角度解决性能瓶颈点,避免其他人遇到类似问题的重复工作;纵向上,从长远考虑问题做到防微杜渐,一次优化,长期受益。基于此,我们提出一站式、标准化的渲染性能解决方案。诚然,这会遇到很多难点。面对界面样式构建的问题,系统 UIKit 框架着实为我们提供了便利,然而有时候我们需要跳出固有思维,尝试建立一套全新界面构建、视觉元素分解的思路。

参考资料

前端感官性能的衡量和优化实践

作者简介

洋洋,美团高级工程师。2018年加入美团,目前负责【美团外卖】和【美团外卖频道】的 iOS 客户端首页业务,以及支撑首页业务的技术架构、工具和系统的开发和维护工作。

招聘

美团外卖长期招聘 Android、iOS、FE 高级/资深工程师和技术专家,Base 北京、上海、成都,欢迎有兴趣的同学投递简历到chenhang03#meituan.com。

发现文章有错误、对内容有疑问,都可以关注美团技术团队微信公众号(meituantech),在后台给我们留言。我们每周会挑选出一位热心小伙伴,送上一份精美的小礼品。快来扫码关注我们吧!

uqummey.png!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK