红帽白皮书新鲜出炉!点击获取,让你的云战略更胜一筹! 了解详情
写点什么

美团 iOS 端开源框架 Graver 在动态化上的探索与实践

  • 2019-11-08
  • 本文字数:6124 字

    阅读完需:约 20 分钟

美团 iOS 端开源框架 Graver 在动态化上的探索与实践

前言

动态化技术指的是不依赖程序安装包,就能进行动态实时更新页面的技术。特别是对于电商、社交等需要快速迭代、实时调整的强运营类业务来说,动态化具有非常重要意义。它的优势主要表现为:提高人效、缩短迭代试错周期、解决版本长尾问题、减少包大小等等。


从 2018 年开始,移动端设备的增长红利不再,整个生态增长趋势开始由高走低,与之对应的开发生态在 Native 技术方向也逐渐开始进入低迷阶段,大方向在向跨平台演进,方案上已经是“百花齐放”。现有的客户端动态化技术主要可以划分为以基于 Webview 的 Web 页面动态化加载、本地内置多个模板支持动态切换、支持动态 DSL 的布局引擎以及基于虚拟机等四类。



图 1 动态化技术分类


动态化方案的渲染引擎多数是基于原生 UI 控件搭建动态化页面。基于 Webview 的 Web 页面动态化,实质是基于浏览器运行网页,页面绘制效率、运行效率相对较低一些。而后三种解决方案,分别通过建立映射表、布局引擎、虚拟机与客户端渲染引擎通讯及调用关系,渲染引擎则都是基于原生 UI 控件搭建动态化页面。由于操作系统提供的 UI 控件布局/绘制仅支持主线程访问,大量原生 UI 控件操作导致 CPU/GPU 负担过重,所以在构建复杂的动态化页面上存在效率和性能瓶颈。因此,渲染性能是动态化技术一直在探索、研究的课题。本文尝试给出一些探索思考与实践经验的分享。



图 2 UI 渲染流程

动态布局框架

如前言部分的图 1 所示,MTFlexbox 是美团点评自研的一款跨平台动态布局框架,它遵循了 CSS3 中提出的 Flexbox 规范来抹平多端差异。美团 App 首页、搜索结果页等业务有一个共同点,就是面向的业务方比较多,承载了流量输送变现的能力。在视图层面呈现轻交互、重展示的特征。频繁变动 UI,快速上线是一个刚需,MTFlexbox 正是满足了这样一个刚需。


由于本文侧重对 MTFlexbox 的渲染性能优化,故仅对 MTFlexbox 做概括介绍。MTFlexbox 首先定义了一份跨平台统一的 DSL 布局描述文件,前端通过编辑器编辑产生布局文件并上传到云端,客户端下载布局文件然后根据布局中的描述信息绑定业务数据,最后基于原生 UI 控件搭建视图并渲染展示。MTFlexbox 的工作原理如下图所示:



图 3 MTFlexbox 工作原理

业务痛点

然而,随着业务的迭代演变,美团 App 首页、搜索结果页等业务视图卡片样式越来越多,展示也越来越复杂。样式种类多意味着视图复用率低,极端场景下甚至无法进行复用。展示复杂,同时也意味着控件数量多、布局复杂、层级深。如果大量复杂操作都发生在主线程,难免造成渲染卡顿等用户体验方面的问题。



图 4 业务痛点


针对上述问题,外卖终端用户研发组、美团终端技术研发组、美团终端业务研发组合作共赢,三方协调资源成立了跨部门、跨事业部的虚拟专项联合项目组,三方精诚合作,在技术上不断追求卓越,力求同时保证稳定性、动态化和高性能。



图 5 合作共赢

思路分析

动态布局框架 MTFlexbox 通过系统 UIKit 搭建视图并渲染展示,其测量、布局、绘制过程均发生在主线程。而作为一款 iOS 端高效的 UI 异步渲染框架 Graver,其布局计算、渲染过程完全异步化,整个过程结束后才通知 UI 线程进行展示。这给我们解决动态化框架的渲染性能问题打开了新思路:关于布局,我们可以采用“画控件”方案替代传统的“拼控件”方式。


Graver 已经在美团 App 的外卖频道、独立外卖 App 核心业务场景的多个业务中经历了一年多的实践检验。良好的稳定性和出色的渲染性能,也得到了美团外卖内部技术团队的认可和肯定。关于 Graver 更多的内容这里不再赘述,详细介绍请参考另一篇技术博客:《美团开源 Graver 框架:用“雕刻”诠释 iOS 端 UI 界面的高效渲染》 。如何构建基于 Graver 进行异步渲染的动态化框架(MTFlexbox),成为首先需要解决的问题。



图 6-1 拼控件



图 6-2 画控件

“拼控件”到“画控件”

通过对系统 UI 渲染流程分析不难发现:唯一确定一个视图展示仅需要确定视图布局信息、内容信息、渲染信息三个要素。含义如下:


  • 布局信息:UI 控件的大小、位置和展示层级。

  • 内容信息:UI 展示的文本、图片等。

  • 渲染信息:包括 UI 控件的背景色、展示字体字号、透明度、边框等控件视觉属性。


Graver 的每个绘制元素通过 WMMutableAttributedItem 来表达内容信息、渲染信息,CGRect 表达绘制元素的大小和位置。渲染整个过程除画板视图外,完全没有使用 UIKit 控件,最终产出的结果是一张位图(Bitmap)。如果能通过一棵树形结构组织所有的绘制元素即绘制结点树,即可按照递归遍历的方式“画控件”来转义“拼控件”构建视图。


接下来,我们需要思考如何建立 MTFlexbox 的数据结构与绘制结点树之间的关系,并且保证该转化过程完全异步化。

构建虚拟结点树

如开篇动态布局框架章节 MTFlexbox 的原理所描述:在相继完成模板树构建、数据绑定之后即进行了视图树构建。然而,出于功能划分考虑、兼顾保留 MTFlexbox 的系统 UI 渲染引擎能力以及构建绘制结点树需要的必要信息考虑,需要构建一个中间数据结构:虚拟结点树。它应包含树形结构的层级信息、Flex 属性信息、数据解析处理后的内容信息以及基本的渲染信息。虚拟结点树是既能构建 UI 控件树也能构建绘制结点树的“桥梁”。



图 7 MTFlexbox 改造

设计数据流

通过上述思路分析,确定了关键数据结构:虚拟结点树、绘制结点树。接下来,我们需要思考如何构建虚拟结点树到绘制结点树的数据流。


在前端有两个重要的概念:回流重绘


  • 回流:当我们对结点树的修改引发了几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,需要重新计算元素的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再将计算的结果绘制出来,这个过程就是回流(也叫重排)。

  • 重绘:当我们对结点树的修改导致了样式的变化,却并未影响其几何属性(比如修改了颜色或背景色)时,不需重新计算元素的几何属性,可以直接为该元素绘制新的样式。


参考前端技术思想以及考虑单一职责原则,在虚拟结点树与绘制结点树中间构建 Fat 型渲染结点树和 Thin 型渲染结点树。Fat 型渲染结点树负责保存原始数据以便做逻辑处理,Thin 型渲染结点树负责保存位置、大小和内容信息。当有修改不影响几何尺寸变化的情况下,仅重新生成 Thin 型渲染结点树的内容信息即可。



图 8 虚拟结点树到位图的转换

线程安全考虑

提高渲染性能的关键,即是全力保证主线程的最小资源开销。因此,需要思考如何保证虚拟结点树到绘制结点树的转换过程是线程安全的。Facebook 开源的跨平台布局引擎 Yoga,提供了通过 UI 视图树中 Flex 属性计算得出每个 UI 控件的位置和大小。然而,提供给 iOS 平台的插头类是基于 UIView 的,即布局计算过程必须在主线程。需要基于 Yoga 核心逻辑重新封装基于渲染结点树的计算逻辑,以保证布局计算是线程安全的。如下图所示:



图 9 基于 Yoga 的布局计算线程安全式改造

架构设计

有了上述的思路分析,接下来我们开始着手 Graver 接入 MTFlexbox 的架构设计。Graver 需要作为独立渲染引擎存在,并保留接入多种动态化框架的可能,这是出于架构设计的灵活性和扩展性的考虑。接入层命名为 M-Graver,其上层基于 MTFlexbox 进行扩展但可灵活插拔,下层基于 Graver 渲染引擎,如下图 10 所示:



图 10 M-Graver 架构


M-Graver 是线程安全的,其主要分为解析层、聚合层、布局层和预备层。下面对各层分别做简单的介绍:


  • 解析层:关于输入定义了一个不依赖于上层 DSL 描述语言的标准化虚拟结点树结构;解析层主要进行虚拟结点树到渲染结点树的转换,涉及标签解析、渲染信息解析、统计信息解析以及 Flex 属性解析。

  • 聚合层:完成渲染结点树的最后构造,涉及可见性、交互性等处理;关于事件绑定也是在聚合层完成,每个事件都以行为参数化的形式绑定到相应渲染结点上。

  • 布局层:利用 Yoga 完成每个结点的 Frame 计算以及层级信息处理,同时将渲染信息等内容转换到 Graver 框架可识别的数据。

  • 预备层:进行坐标系转换,并拍平带有视图层级结构信息的渲染结点树,剔除无效渲染结点(如可见性、size 为 0 等),屏蔽掉由于视图层级原因导致被完全遮挡的渲染结点,最后根据渲染结点生成绘制结点构建绘制结点树,交由 Graver 提供的画板视图进行绘制。

技术难点

按照上述思路分析完成架构设计,但在实施部署的过程中也遇到了不少的技术难点和问题。如:动态布局框架 MTFlexbox 创建至今已两年有余,因业务的快速发展而产生了一些技术“负债”。为了保证不影响线上原有的业务逻辑,所以在进行 MTFlexbox 的模板树到虚拟结点树,再到 UI 视图树的技术升级改造过程时,尤其需要关注各种“蛛丝马迹”式的细微逻辑。


另外,在将异步渲染引擎 Graver 接入 MTFlexbox 的过程中也遇到了诸多问题,包括如何构建基于位图的事件处理系统,跨渲染引擎的技术融合,一些极端场景下的绘制效率瓶颈等等。下面将逐一展开阐述。

1. 如何基于位图进行事件处理

由于视图最终通过渲染位图来呈现,这就需要建立基于位图的事件处理系统。如前文所述,渲染结点树记录了每个控件的位置、大小信息以及层级结构,基于此可仿照系统事件处理逻辑进行基于位图的事件处理系统设计。


在视图展示期间,画板视图收到事件响应通知后(如点击了画板视图中标号为 5 的红色按钮),根据位图对应的渲染结点树存储的各控件布局、层级和渲染信息,逐层遍历找到需要响应的渲染结点,如果涉及信息修改则变更其在渲染结点树中的渲染信息,触发再次渲染的同时执行该渲染结点绑定的事件方法。遍历渲染结点树的输入是系统基于画板视图返回的点击位置,其遍历过程与系统 UI 事件查找过程比较相似。事件处理过程如下图 11 所示:



图 11

2. 系统 UI 控件与绘制元素的融合问题

从美团业务特征出发,图文组合占据多数 UI 场景。然而也存在诸如动效等无法依托 Graver 进行图文渲染的情况。因此,需要考虑跨渲染引擎的渲染融合问题。在 M-Graver 的预备层遍历渲染结点树时,可以根据当前结点是否为原生结点决议树拆分,如果是原生结点,将该结点连同其子树“直系”绘制结点从渲染结点树中拆分下来,以该结点为根结点的子渲染结点树,生成对应的绘制结点树,多个子渲染结点树的根结点,构成了以画板视图为单元的画板视图树。如下图 12 所示:



图 12 结点融合


为了便于理解,我们给出以下几个名词的解释说明:


  • 绘制结点:渲染结点树中的结点如果可以转化成绘制结点树中的结点,则称之为绘制结点,最后通过 Graver 进行渲染。

  • 原生结点:渲染结点树中的结点如果不能转化成绘制结点树中的结点,只能转化成系统 UI 控件则称之为原生结点。

  • 结点变异:以一个二叉渲染结点树为例,左子结点是原生结点,右子结点是绘制结点的情况下,由于左子结点先于右子结点添加到父节点,可能存在层级颠倒问题。这时右子结点需要强制转为原生结点,维持正确的层级顺序,即结点变异。

  • 画板视图:继承自 UIView 的普通视图,其内部封装了一系列的基于 Graver 的渲染逻辑。


树拆分的过程还涉及到兄弟结点层级颠倒以及布局交叉等问题。兄弟结点层级颠倒问题通过结点变异来解决。布局交叉问题存在于判定渲染结点树的结点是绘制结点或原生结点之前,由于布局原因存在视图交叉。布局交叉问题通过新建画板视图插入来保证层级正确以及绘制正确。由于篇幅有限,这里不再赘述。

3. 极端场景下的绘制效率瓶颈问题

从产品交互层面看,为了提高屏效往往存在多向滑动的视图组件场景。如横滑 Scroll 组件,其特点是需要通过滑动才能逐渐看到所有的视图内容。通过异步渲染绘制位图来实现的情况下,存在单一并发渲染任务计算逻辑繁重的问题,从用户体验层面看容易造成“白屏”现象。为解决该问题,将视图卡片渲染过程分解,进行增量渲染,采用渐进式的方式减少空白页面等待时间。根据待展示区域在屏幕中相对位置进行区块划分,通过队列集中控制绘制操作。以此进一步提高并发效率,并减少渲染过程的非必要系统资源消耗。

区块划分

区块划分策略的实质是绘制结点树的拆分,将绘制结点树中不存在布局交叉的子结点树进行逐一拆分,每个拆分下来的绘制结点子树即为一个区块,同时要设置最小块策略,否则拆分粒度太小反而会因为过多的线程并发造成性能瓶颈。



图 13 分块

分块绘制

以下图为例说明分块绘制逻辑。在滑动过程中,若本地缓存有此区域绘制结果,读取缓存并直接通知主线程展示,如例 4 中 X4’。否则,将该区域加入队列,以块为单元进行并发绘制,绘制完成后更新缓存,再通知主线程展示,如例 1 中 X1’,例 2 中 X2‘,例 3 中 X3’。对划到屏幕外的区域,从队列中清除,终止绘制操作;若此区域已绘制完成,则通知主线程清除此区域的展示,如例 2 中 X2,例 3 中 X3,例 4 中 X4。



图 14 分块绘制示意图

业务应用

在完成“拼控件”到“画控件”的思路探索与技术落地之后,需要发挥其价值,将其部署到线上进行业务实践应用。动态布局框架 MTFlexbox 的跨平台代码复用能力对业务开发效率有了大幅提升。从产品层面看,在原有资源不变的情况下,达到了高效支撑业务迭代的效果。MTFlexbox 动态布局框架在经历了一次联合共建的“洗礼”后渲染性能得到大幅提高,变得愈加成熟、完善。在过去的半年多期间,我们采用异步渲染引擎 Graver 的 MTFlexbox 已先后应用在搜索结果页、美团首页等核心流量区业务。下面列举部分应用案例:



图 15 美团首页、搜索结果页


采用异步渲染引擎 Graver 的 MTFlexbox 绝大多数应用场景为列表级应用。如上图所示,所有视图卡片均为采用 M-Graver 实现的动态模板。截止到发稿,覆盖搜索结果页 36 个动态模板,覆盖首页 42 个动态模板,业务应用累计覆盖 78 个动态模板。

数据指标

以业务应用美团 App 新版首页为例,分类频道卡片以下全部为 MTFlexbox 实现的动态模板视图卡片。由于采用异步渲染引擎 Graver 的 MTFlexbox 具备了在线程安全条件下进行测量、布局、渲染,美团首页接入后滚动 FPS 提升明显,对于上拉加载过程的 FPS 提升更为明显。因此,列表使用体验变动更加顺畅。美团首页的 50 分位滚动 FPS 接近 59,上拉加载 FPS 接近满帧。详细数据如下图所示:



图 16 优化前后 FPS 对比

总结复盘

从业务场景作为出发点和原始驱动力,如何改善动态布局框架的渲染性能问题,从本质上讲是解决业务迭代演变时带来的用户体验问题。这里有以下几点经验可供大家参考:


  • 在项目设计阶段要权衡考虑技术方案全景,作为技术方向规划,不做临时方案;架构设计要兼顾合理性、灵活性、扩展性。

  • 期间也会遇到诸如原生结点和绘制结点如何融合、事件处理系统怎样建设、如何分区绘制等一系列问题。保持开放心态,作为探索性项目在方案细节上有很多可行性,充分讨论、盯紧目标,不走极端。

  • 在跨部门协作项目中,尤其要关注项目管理、会议记录、里程碑等,同时保持高频的沟通。


最后,借用朱光潜先生在《艺文杂谈·谈对话体》中提到的一句话作为结尾:“疑难是思想的起点与核心。”

参考资料


作者介绍


洋洋,美团点评资深工程师。


柏泉,美团点评高级技术专家。


晓宇,美团点评研发工程师。


本文转载自公众号美团技术团队(ID:meituantech)


原文链接


https://mp.weixin.qq.com/s?__biz=MjM5NjQ5MTI5OA==&mid=2651750886&idx=1&sn=5f0f2fa88b6de13d51e9eaf83c4f8022&chksm=bd1258ab8a65d1bdb2e70a2d42b20d68c3a5c91f377d7decc3526fd3623114c23661e179f8e8&scene=27#wechat_redirect


2019-11-08 08:002150

评论

发布
暂无评论
发现更多内容

探讨 LLM 的潜在风险 (偏见与毒性等),是否存在解决之道?

Baihai IDP

人工智能 程序员 AI LLM 白海科技

万万字图解| 深入揭秘Golang锁结构:Mutex(上)

云舒编程

golang 设计 mutex 字节

想在 Mac 里装 Windows ?试试 Parallels Desktop虚拟机!

Rose

Windows系统 Mac双系统安装 Parallels Desktop

万字图解 | 深入揭秘IP层工作原理

云舒编程

IP MTU 路由表 子网划分 图解网络

《Hive编程指南》读书笔记

京东科技开发者

NineData和Klustron完成产品兼容互认证

NineData

数据库 数据管理 NineData Klustron 泽拓昆仑

当当网按关键字搜索dangdang商品 API (item_search-按关键字搜索dangdang商品-dangdang.item_search)在电商中的应用

技术冰糖葫芦

API

010 Editor v14.0激活版 Mac十六进制编辑器 含010 editor注册码

Rose

010 Editor破解版 010 Editor注册码 Mac文件编辑器 苹果电脑软件下载

“纯血”鸿蒙到来,对开发者是机会吗?

云舒编程

华为 鸿蒙 开发者 HarmonyOS 生态

苹果电脑游戏:以撒的结合:重生+忏悔+胞衣 Mac中文版下载

Rose

mac游戏 以撒的结合:忏悔

Axure RP 8使用技巧分享 含axure rp8汉化授权码

Rose

axure rp9下载 Axure RP 8汉化包 Axure破解版 Axure使用教程

开发人员是怎么失去成就感的

云舒编程

程序员 发展 职业生涯 开发. #最有成就感的事

WorkPlus移动应用管理平台,助力企业实现高效移动办公

WorkPlus

两千字讲明白java中instanceof关键字的使用!

EquatorCoco

Java 软件开发 开发语言 关键词

foobar2000 for mac多功能音频播放器 v2.6.1免激活版

Rose

mac音乐播放器 foobar2000中文版 foobar2000破解版

【技术探讨】如何选择一款距离远的无线通信模块?

Geek_ab1536

大规模集群下,如何快速实现无死角网络连通性的主动巡检

ii2day

云原生 压力测试 Cloud Native kubernetes 运维 自动巡检

bean的一生

京东科技开发者

TCP close_wait 引发的血案

云舒编程

TCP 压测 Wait 连接池

WorkPlus构建便捷高效的企业移动门户平台

WorkPlus

得物从零构建亿级消息推送系统的送达稳定性监控体系技术实践

JackJiang

网络编程 即时通讯 IM

NLP国内外大模型汇总列表[文心一言、智谱、百川、星火、通义千问、盘古等等]

汀丶人工智能

nlp 大模型

WorkPlus Meet私有化视频会议内网部署

WorkPlus

马斯克接手Twitter一年后的成果-工作量化的重要性

云舒编程

twitter 马斯克 推特

Mac游戏星露谷物语 - 创造梦想中的田园生活

Rose

苹果电脑 模拟经营游戏 星露谷物语游戏下载 macos游戏推荐

QSpace Pro 一款简洁高效的多窗格文件管理器,灵活且实用!

Rose

Mac软件 QSpace 多窗格文件管理器

苹果macos效率神器alfred5新功能介绍 及alfred 5汉化包下载

Rose

mac软件下载 Alfred 5破解版 Alfred 中文 Mac效率办公软件

SnapGene 5最新补丁版 生物分子学软件snapgene5 for Mac安装教程

Rose

Mac软件 SnapGene 5破解版 SnapGene 5下载 DNA序列分析

非常火爆“太空种田”游戏Slipways mac破解版

Rose

mac游戏 苹果电脑游戏推荐

万万字图解| 深入揭秘Golang锁结构:Mutex(下)

云舒编程

golang 面试 mutex 字节

美团 iOS 端开源框架 Graver 在动态化上的探索与实践_文化 & 方法_洋洋_InfoQ精选文章