“道生一,一生二,二生三,三生万物。” —— 《道德经》
Picasso是大众点评移动研发团队自研的高性能跨平台动态化框架,经过两年多的孕育和发展,目前在美团多个事业群已经实现了大规模的应用。
Picasso源自我们对大前端实践的重新思考,以简洁高效的架构达成高性能的页面渲染目标。在实践中,甚至可以把Native技术向Picasso技术的迁移当做一种性能优化手段;与此同时,Picasso在跨越小程序端和Web端方面的工作已经取得了突破性进展,有望在四端(Android、iOS、H5、微信小程序)统一大前端实践的基础之上,达成高性能大前端实践,同时配合Picasso布局DSL强表达能力和Picasso代码生成技术,可以进一步提升生产力。
客户端动态化
2007年,苹果公司第一代iPhone发布,它的出现“重新定义了手机”,并开启了移动互联网蓬勃发展的序幕。Android、iOS等移动技术,打破了Web应用开发技术即将一统江湖的局面,之后海量的应用如雨后春笋般涌现出来。移动开发技术给用户提供了更好的移动端使用和交互体验,但其“静态”的开发模式却给需要快速迭代的互联网团队带来了沉重的负担。
客户端“静态”开发模式
客户端开发技术与Web端开发技术相比,天生带有“静态”的特性,我们可以从空间和时间两个维度来看。
从空间上看需要集成发布,美团App承载业务众多,是跨业务合流,横向涉及开发人员最多的公司,虽然开发人员付出了巨大的心血完成了业务间的组件化解耦拆分,但依然无可避免的造成了以下问题:
- 编译时间过长。 随着代码复杂度的增加,集成编译的时间越来越长。研发力量被等待编译大量消耗,集成检查也变成了一个巨大的挑战。
- App包体增长过快。 这与迅猛发展的互联网势头相符,但与新用户拓展和业务迭代进化形成了尖锐矛盾。
- 运行时耦合严重。 在集成发布的包体内,任何一个功能组件产生的Crash、内存泄漏等异常行为都会导致整个App可用性下降,带来较大的损失。
- 集成难度大。 业务线间代码复用耦合,业务层、框架层、基础服务层错综复杂,需要拆分出相当多的兼容层代码,影响整体开发效率。
从时间上看需要集中发布,线上Bug修复须发版或热修复,成本高昂。新功能的添加也必须等待统一的发版周期,这对快速成长的业务来说是不可接受的。App开发还面临严重的长尾问题,无法为使用老版本的用户提供最新的功能,严重损害了用户和业务方的利益。
这种“静态”的开发模式,会对研发效率和运营质量产生负面影响。对于传统的桌面应用软件开发而言,静态的研发模式也许是相对可以接受的。但对于业务蓬勃发展的移动互联网行业来说,静态开发模式和敏捷迭代发布需求的矛盾日益突出。
客户端动态化的趋势
如何解决客户端“静态”开发模式带来的问题?
业界最早给出的答案是使用Web技术
但Web技术与Native平台相比存在性能和交互体验上的差距。在一些性能和交互体验可以妥协的场景,Web技术可以在定制容器、离线化等技术的支持下,承载运营性质的需要快速迭代试错的页面。
另一个业界给出的思路是优化Web实现
利用移动客户端技术的灵活性与高性能,再造一个“标准Web浏览器”,使得“Web技术”同时具有高性能、良好的交互体验以及Web技术的动态性。这次技术浪潮中Facebook再次成为先驱,推出了React Native技术(简称RN)。不过RN的设计取向有些奇怪,RN不兼容标准Web,甚至不为Android、iOS双端行为对齐做努力。产生的后果就是所有“吃螃蟹”的公司都需要做二次开发才能基本对齐双端的诉求。同时还需要尽最大努力为RN的兼容性问题、稳定性问题甚至是性能问题买单。
而我们给出的答案是Picasso
Picasso另辟蹊径,在实现高性能动态化能力的同时,还以较强的适应能力,以动态页面、动态模块甚至是动态视图的形式融入到业务开发代码体系中,赢得了许多移动研发团队的认同。
Picasso框架跨Web端和小程序端的实践也已经取得了突破性进展,除了达成四端统一的大前端融合目标,Picasso的布局理念有望支持四端的高性能渲染,同时配合Picasso代码生成技术以及Picasso的强表达能力,生产力在大前端统一的基础之上得到了进一步的提升。
Picasso动态化原理
Picasso应用程序开发者使用基于通用编程语言的布局DSL代码编写布局逻辑。布局逻辑根据给定的屏幕宽高和业务数据,计算出精准适配屏幕和业务数据的布局信息、视图结构信息和文本、图片URL等必要的业务渲染信息,我们称这些视图渲染信息为PModel。PModel作为Picasso布局渲染的中间结果,和最终渲染出的视图结构一一对应;Picasso渲染引擎根据PModel的信息,递归构建出Native视图树,并完成业务渲染信息的填充,从而完成Picasso渲染过程。需要指出的是,渲染引擎不做适配计算,使用布局DSL表达布局需求的同时完成布局计算,既所谓“表达即计算”。
从更大的图景上看,Picasso开发人员用TypeScript在VSCode中编写Picasso应用程序;提交代码后可以通过Picasso持续集成系统自动化的完成Lint检查和打包,在Picasso分发系统进行灰度发布,Picasso应用程序最终以JavaScript包的形式下发到客户端,由Picasso SDK解释执行,达成客户端业务逻辑动态化的目的。
在应用程序开发过程中,TypeScript的静态类型系统,搭配VSCode以及Picasso Debug插件,可以获得媲美传统移动客户端开发IDE的智能感知和断点调试的开发体验。Picasso CI系统配合TypeScript的类型系统,可以避免低级错误,助力多端和多团队的配合;同时可以通过“兼容计算”有效的解决能力支持的长尾问题。
Picasso布局DSL
Picasso针对移动端主流的布局引擎和系统做了系统的对比分析,这些系统包括:
- Android开发常用的LinearLayout。
- 前端及Picasso同类动态化框架使用的FlexBox。
- 苹果公司主推的AutoLayout。
其中苹果官方推出的AutoLayout缺乏一个好用的DSL,所以我们直接将移动开发者社区贡献的 AutoLayout DSL方案列入对比。
首先从性能上看,AutoLayout系统是表现最差的,随着需求复杂度的增加“布局计算”耗时成指数级的增长。FlexBox和LinearLayout相比较AutoLayout而言会在性能表现上有较大优势。但是LinearLayout和FlexBox会让开发者为了布局方面需要的概念增加不必要的视图层级,进而带来渲染性能问题。
从灵活性上看,LinearLayout和FlexBox布局有很强的概念约束。一个强调线性排布,一个强调盒子模式、伸缩等概念,这些模型在布局需求和模型概念不匹配时,就不得不借助编程语言进行干预。并且由于布局系统的隔离,这样的干预并不容易做,一定程度上影响了布局的灵活性和表达能力。而配合基于通用编程语言设计的DSL加上AutoLayout的布局逻辑,可以获得理论上最强的灵活性。但是这三个布局系统都在试图解决“用声明式的方式表达布局逻辑的问题”,基于编程语言的DSL的引入让布局计算引擎变得多余。
Picasso布局DSL的核心在于:
- 基于通用编程语言设计。
- 支持锚点概念(如上图)。
使用锚点概念可以简单清晰的设置非同一个坐标轴方向的两个锚点“锚定”好的视图位置。同时锚点可以提供描述“相对”位置关系语义支持。事实上,针对布局的需求更符合人类思维的描述是类似于“B位于A的右边,间距10,顶对齐”,而不应该是“A和B在一个水平布局容器中……”。锚点概念通过极简的实现消除了需求描述和视图系统底层实现之间的语义差距。
下面举几个典型的例子说明锚点的用法:
1 居中对齐:
view.centerX = bgView.width / 2
view.centerY = bgView.height /2
2 右对齐: