面向万物智联的应用框架的思考和探索(上)
原文:https://mp.weixin.qq.com/s/G6o6xSAWroz0fJK7oShYLA,点击链接查看更多技术内容。
应用框架,是操作系统连接开发者生态,实现用户体验的关键基础设施。其中,开发效率和运行体验是永恒的诉求,业界也在持续不断的发展和演进。
本文重点围绕移动应用框架,梳理其关键发展脉络,并分析其背后的技术演进思路以及目前的局限;同时,进一步结合万物智联的新场景和新生态,围绕相应的应用框架的设计和演进,分享个人在这个领域的思考,实践,以及下一步探索。
“凡是过往,皆为序章”- 威廉 · 莎士比亚
1、应用框架概览
1.1 应用,以及应用框架的基本组成
应用是用户使用操作系统/设备的入口,应用框架则是应用开发和运行的基础设施。用户通过各种各样的应用来和操作系统/设备交互,来满足相应的需求。以移动平台为例,一个典型的应用以及相对应的应用框架的基础组成大致如下所示:
Figure 1 典型的应用结构以及相应的系统运行环境
一个典型的应用结构主要包括以下几个部分:
1)用户界面以及相应的业务处理逻辑。这里主要包括构建用户界面所需的 UI(User Interface)组件,布局,动效,事件交互响应处理,所需的资源(图片/字体等),以及结合 UI 呈现的业务逻辑处理等。从运行时的维度来看,它主要对应了系统运行环境中的 UI 框架(含语言运行时),以及部分的系统能力 API (Application Programming Interface);
2)共享库。这里主要包括开发者封装好的 SDK(Software Development Kit),以及使用的三方库等。从运行时维度来看,它主要对应了系统能力 API 以及语言运行时,如果共享库涉及 UI 的话,还对应了 UI 框架;
3)包清单文件。这里主要包括应用包的结构描述,权限声明等,它主要对应了系统运行环境中的包管理,应用生命周期/权限管理/进程管理等。
其中, UI 框架的主要组成如下图所示:
Figure 2 UI 框架的主要组成
开发模型:对开发者提供的开发范式、UI 组件/API 能力、编程语言等,重点体现的是开发效率与难易程度;
运行框架:UI 界面渲染及交互的基础能力框架,将开发者的程序运行在具体系统平台上,包括应用整体渲染处理流程,语言逻辑执行流程,以及平台能力扩展机制,重点体现的是应用运行的性能体验;
平台适配:承载框架的具体操作系统或平台的适配层。
一般而言,应用框架中的包管理、生命周期/权限管理,和具体的操作系统关联较紧,并相对稳定;能力 API 则是操作系统对设备能力的封装,主要影响应用使用设备的能力。UI 框架以及相应的编程语言则是影响用户体验(包括开发和运行体验)的关键要素,尤其随着移动平台的不断普及以及移动设备的差异,移动平台上的 UI 框架(含编程语言)是业界不断演进的重点领域。
1.2 移动平台上应用框架的演进
纵观十年来的发展,总体而言,移动平台上应用框架围绕着原生框架,三方跨平台框架交织发展,并结合相应语言、工具的配套演进。下图描述了其中的关键框架/语言的演进概览。
Figure 3 移动平台上关键语言/开发框架/工具演进概览
图中有几个关键节点:
1) 2013 年,Facebook 发布的 React.js 第一次综合的将数据绑定,虚拟 DOM(Document Object Model)等机制引入前端开发框架设计中。开发者只需声明好相应的数据和 UI 的绑定,之后由框架来跟踪数据的变化,并通过虚拟 DOM 树的对比找出变化点,从而实现界面的自动更新,而无需开发者手动基于 DOM 编程。
2)2018 年,Google 发布的 Flutter 则是个重要的分界点。Flutter 融合了 Dart 语言,是第一个深度融合了语言的较为完整的声明式开发框架,实现了完全通过数据驱动的 UI 变更。另外,Flutter 通过基于 Skia 的自绘制引擎实现了高性能的跨平台的平台一致性的渲染能力,并提供了 Hot Reload 机制提升开发测试体验。不过,Flutter 的整体设计哲学偏向底层的灵活性 – 主要通过底层的细粒度的能力供开发者自由组合,另外,Google 对 Dart 语言的简洁度的改进较少,整体上开发的简洁度以及对用户的友好度相对不足。
3)2019 年,Apple SwiftUI 的推出,意味着主流 OS 的原生应用框架开始逐步往声明式开发方式迁移。SwiftUI 推动了 Swift 语言特性扩展实现了更加简洁自然的 UI 描述,并通过 XCode 开发工具的所见即所得的高效预览能力进一步提升开发效率。同时,SwiftUI 也是真正意义上开始通过一套框架,逐步统一 Apple 生态中的不同的设备/OS 上的应用开发。
另外,2019 年 Google 将更简洁的 Kotlin 语言升级为 Android 首选的编程语言,并在 2021 年推出基于 Kotlin 的 UI 框架 Jetpack Compose, 同时结合开发工具 Android Studio 逐步往多设备以及跨平台演进。
总体而言,移动端应用框架的演进包含以下几个关键特征:
1)从命令式 UI 开发逐步演进到声明式 UI 开发
2)UI 和编程语言的融合从相对松散演进到逐步紧密
3)开发范围从单设备演进到多设备,从单平台演进到多平台
接下来的章节会分别围绕跨平台框架,以及原生应用框架逐步展开,梳理其具体的演进脉络。
1.2.1 跨平台框架
由于 W3C( World Wide Web Consortium)标准的普及以及 Web 天然的跨平台能力, 跨平台框架主要是基于 Web 或 Web 的衍生技术。
最早试图补齐 Web 跨平台能力是一家叫做 PhoneGap 的公司,后面被 Adobe 收购,2012 年以 Cordova 的项目名开源发布。当时的 W3C 标准更多涵盖的是页面渲染,而设备相关能力的标准非常缺失,PhoneGap 的目标则是围绕着“Phone”补齐这方面能力“Gap” – 它定义了一套移动平台上常用的设备能力的 JS API,并基于 JS 引擎设计了一套扩展方式来实现不同平台上的 API,同时结合系统的 Webview,配套相关系统整合以及打包工具,部署成相应平台上的应用格式在目标操作系统平台上运行。PhoneGap 一定程度上促进了 Web 在移动设备上的发展,但是它没有解决 Web 引擎本身的体验问题。当时的 Web 引擎(系统自带的 Webview),尤其是 Android 平台上,能力较弱,并缺失硬件加速,稍微复杂的应用体验较差。针对这些问题,2014 年 Intel 开源技术中心推出了 Crosswalk 项目,在 Chromium 内核基础上,针对移动平台上 Web 应用,做了进一步解耦和性能优化(包括硬件加速,包体积优化,以及针对游戏专门设计一套渲染路径等),并结合了 Cordova 补齐相关 API 能力,构建了可独立打包演进的 Web 引擎,性能体验得到了较大的提升。而且,通过独立打包,也解决了因为 Android 系统的碎片化,Webview 的版本不一,从而引起的应用体验(包括渲染能力和性能)不一致的问题。
Crosswalk 发布一年左右就吸引了近千款应用基于 Crosswalk 构建,其中有数款应用都达到了超过百万级的下载量。不过后续由于 Intel 在移动平台上的战略调整,没有进一步演进。但这块的关键思路,后续或多或少在业界看到了相关的影子,比如 Google 后续推出的可独立升级的 ChromeWebview,以及国内各互联网公司各自构建的 WebView(腾讯的 X5 内核,阿里巴巴的 UC 内核等)。
Cordova+Crosswalk,一定程度上提升了基于 Web 的跨平台框架所需的 API 能力以及渲染性能,不过还有几个方面,还需要持续提升:
Ⅰ、开发范式/前端框架
标准的 Web 还是一种“半声明式”的开发方式,即通过 HTML(Hyper Text Markup Langua-ge), CSS(Cascading Style Sheets)来描述整体页面结构和样式,但当要改变其中的界面元素时,开发者需要基于 W3C DOM API,通过具体编程,来实现其中相关节点的定位以及增删改。当所面对的界面交互越复杂,所关联的数据越多,开发者负担就越重,也越容易出错。
2013 年 Facebook 发布的 React.js,引入了数据和 UI 自动绑定,以及组件化机制,将声明式的能力较好的融合到前端框架中。这是开发理念的一次比较大的进步。这样方式下,开发者无需去手动更改 UI 的结构,只需要描述好 UI 结构本身,以及数据和 UI 的绑定关系,框架层会自动跟踪数据的变化并更新相应的 UI,这样进一步提升了 UI 框架开发效率。后续个人开发者尤雨溪推出的 Vue.js 框架本质上也是采用了类似的思路,只不过在表达方式以及具体实现上有所不同。另外,通过前端框架层提供的组件能力,本质上是对 W3C HTML/CSS 标准的进行了进一步封装,提供了更加高层的能力,这样有利于聚焦于相对常用的 W3C 标准能力,同时通过类似 Virtual DOM 的机制,对底层的 Web 引擎做了进一步的解耦,从而为后续的进一步演进建立了一定的基础。
Ⅱ、性能体验
由于 W3C 标准本身庞杂的历史积累和复杂性,相对应的 Web 引擎复杂度也非常高。复杂的渲染管线,千万量级的代码,百兆级的二进制大小,以及大几十兆甚至百兆级的基础内存占用,让基于 Web 引擎的应用在整体性能体验,资源占用等相关方面,尤其在移动平台上,和原生应用都有较大的差距,并难以较好解决。
2015 年 Facebook 在 React.js 基础上推出的 RN(React Native),是 Web+Native 的一个典型代表。RN 的基本思路是开发范式上基于 React.js,而具体渲染路径上,则完全绕过 Web 引擎,直接桥接到了操作系统原生的 UI 框架。2016 年阿里巴巴推出的 Weex 也是采用了和 RN 类似的思路。
另外,针对 RN 的关键架构,Facebook 持续做了不少演进:
1)布局引擎(Yoga)。2016 年推出。通过实现 CSS 子集的模式实现高效的一致的跨平台布局体验;
2)JS 引擎(Hermes)。2019 年推出。针对移动平台上体验问题做相应改进,比如通过引入一定的 AOT(Ahead Of Time)机制,主要是中间码预编译,来提升启动速度;
3)高效交互/按需加载等新架构。2022 年推出。支持高效的跨语言交互、数据共享,组件按需加载等相关机制。
通过这些相关的改进, RN 实现了一定的平衡,基于 Web 的开发范式实现了较好的性能体验。不过,由于原生 UI 框架本身的差异,RN 难以达成较好的跨平台一致性;另外由于需要额外一层 Web 到 Native 的桥接转换,和原生相比,RN 在性能体验上天然就有一定差距,难以跨越。
Google 在 2018 年推出的 Flutter,则是借鉴了 React 的思想,走了另外一条较为创新的路。主要特征如下:
1)基于 Dart 语言的全新声明式范式设计,一切皆为 Widget,可灵活组合,并通过数据驱动 UI 渲染
2)基于底层画布的高效自绘制能力
3)Dart 运行时优化,比如高效的小对象分配释放能力,AOT 机制等
再配合相对完整的跨平台能力,以及相应的工具链,Flutter 具备了较好的综合竞争力。和类 RN 的方案相比,Flutter 在以下几个方面具备一定优势:
1)跨平台一致性。通过自绘制机制,具备更好的跨平台一致性的高效的渲染能力。
2)语言执行性能。通过强类型语言 Dart 的引入以及相应的运行时优化(AOT 等),具备更好的语言执行性能,理论上可对标 iOS/Android 原生语言。
不过,Flutter 也有自身的不足:
1)语言生态。Dart 语言生态和 JS 相比,有比较大的差距。而且生态培育过程一般都比较漫长。
2)动态化能力。Flutter 尚不具备可对标 JS 相关框架的动态化部署机制,实现应用界面/业务的动态刷新,而这块又是大多数移动应用的强诉求。当然,这里有技术的因素,也有非技术的因素综合引起的。
另外一个层面,Flutter 声明式 UI 的设计原则上更侧重灵活组合,暴露了较多的细粒度底层的机制(包括各种细粒度的 Widget, 以及需进一步区分 Widget 的有状态和无状态等等),而在整体应用开发的简洁自然度方面,则考虑的相对较少。
受到 Flutter 的启发,业界又衍生出了一些新的方案。比如桥接标准的 Web 前端框架到 Flutter,其目的是复用 Web 的生态优势,以及 Flutter 的高性能跨平台渲染能力。这块的典型代表是阿里巴巴 2021 年开源的 Kraken,以及 2022 年进一步在此基础上演进的 KUN。这种方案有一定程度的优势,不过由于它本质上还是桥接转换,另外引入了两种虚拟机(JS 和 Dart),架构上就决定了这种方案在性能和内存体验存在难以解决的缺陷。还有 JS 前端框架+ C/C++自绘制的方案,比如 Weex2.0,它在一定程度上较好综合了 JS 层的生态和底层自绘制的优势,不过在语言运行时,开发的便捷度等方面,还缺少本质的改进。
总体而言,基于 Web 或通过 Web 衍生出来的跨平台框架,业界一直在持续的演进,包括 Web API 扩展,统一的持续优化的 Web 引擎,声明式前端框架,Web+Native 的融合,自绘制机制,语言和框架深度融合的架构等等。但这些跨平台框架,离真正意义上的极简开发,极致性能,并可对标原生应用体验的设计方面,还缺少系统性的革新和跨越。
1.2.2 原生应用框架
1.2.2.1 iOS 平台
iOS 的原生应用框架是 Cocoa Touch,其中的 UI 框架和语言最早分别是 UIKit 和 OC(Objective-C),提供了传统的命令式(imperative)编程方式。2014 年 Apple 推出了 Swift 语言,在简洁性,安全性,性能等方面都有进一步的提升。2019 年,在 Swift 语言发布了 5 年之后,Apple 推出了新一代的 UI 框架 – SwiftUI,在开发理念上做了一次全面升级:从命令式编程演进到了简洁自然的声明式(declarative)编程,并通过一套 UI 框架可开发 Apple 生态中的不同 OS 上的应用(iOS/iPadOS/watchOS/macOS 等)。以下是几个比较重要的演进点:
a.全面采用声明式来描述 UI,并且后续的 UI 变更都是通过数据驱动来完成。
具体而言,Apple 重新定义了一整套开发范式,涵盖了 UI 的关键组成,UI 和数据的关联等信息。整个 UI 就是一段描述,本质上就是描述界面的一个值,而不是任何实例;另外 UI 变化通过单一数据源来驱动(Apple 称之为“Single Source of Truth”),而不是像之前命令式编程那样,开发者可以任意获取某个实例,定位到某个节点来进行更改。这跟传统的开发方式,有本质的不同。
b.简洁自然的开发范式。尽管在 SwiftUI 之前也有各种声明式编程框架,但 SwiftUI 在简洁性以及类自然语言的易理解性实现了极大的提升。具体而言,Apple 通过语言和框架的深度结合,达成了简洁自然的开发体验。Swift 的一些语言特性基本就是为 SwiftUI 服务,比如
1)Property Delegates(属性代理)
属性代理本质上一种语法糖机制,当访问指定的数据时,包括 Get 或 Set,可以附加插入额外的处理逻辑。这个机制用来实现 @State 这类的装饰器,实现数据和 UI 之间的自动绑定,简化应用开发逻辑。
2)Trailing closure (尾随闭包)
尾随闭包主要用来增强可读性。当一个很长的闭包表达式作为最后一个参数传递给函数时,可以把它放在在函数括号之后。函数支持将其作为最后一个参数来调用。
3)Function Builders(函数构造器)
函数构造器也用来增强可读性,它可以用语句块作为接受参数列表,每行代码返回一个参数。
还有其他一些语法比如链式调用等等,这些共同组成了简洁自然的开发范式。
另外,SwiftUI 中自定义组件的内容,其实是通过 Function Builders 返回的遵守 View 协议的类型组成,这就意味着框架运行时有机会结合编译时的 UI 类型信息做相应优化来实现视图变化的快速更新。
c.配套工具支持。在工具层面,除了传统的调试调测等能力,Apple 的 Xcode 围绕 SwiftUI 预览做了比较重要的增强,简要说明如下:
1)实时双向预览。UI 代码刷新可实时看到相应的界面效果,同时可实现代码-界面的双向预览 – 点击代码可看到的相应的 UI 界面,选择 UI 界面也可同步显示相应的代码。另外预览可以指定到组件级 – 通过加 @Preview 标签来指定相应组件进行预览
2)自定义预览。预览时可支持参数配置来定义预览的设备型号,暗黑模式,语言地区等上下文,并可以设置页面所依赖的入参等。
3)预览时动态交互,并可支持在预览器上调测。预览时可以实现常用的动态交互,比如手势、键盘等输入,音量、Home 键、电源键等按键操作,以及网络、文件、多媒体能力支持等
这些预览相关的功能极大提升了应用的开发效率。本质上,高效的效果一致的预览是需要 SwiftUI 以及相应能力能够直接运行在 PC(Personal Computer)平台上,才能达成较好的效果。
另外,从 2022 年 SwiftUI 4.0 的关键特性来看,Apple 不断增强高级组件,以及自定义等相关能力,持续提升开发效率。比如更多的高级组件(各类图表,分享组件等),布局类型以及自定义布局能力增强(添加 Grid 网格布局,另外增强了自定义布局的能力,以及布局变化相关的动画)等等。
1.2.2.2 Android 平台
Android 的原生应用框架(Application Framework)主要包括应用管理,View 系统,后台服务(Service),内容提供者(Content Provider),广播接受机制(Broadcast Receiver)等。其中 UI 框架和语言最早分别是 Android View 系统以及 Java,主要也是采用了命令式的开发模式(其中界面的初始声明可以通过 XML(eXtensible Markup Language)方式来定义,后续的操作则都是通过相应的命令式 API 来完成)。
2016 年 JetBrains 公司正式发布 Kotlin 1.0 版本, 它是一种静态类型语言,提供了更加简洁,安全的语法特性(比如说解决 Java 的语法冗长以及空指针等问题)。同时,Kotlin 在字节码级别和 Java 兼容。随着 Kotlin 的不断完善,再加上 Java 语言的版权问题等背后的原因,很快, 2019 年 Google 宣布 Kotlin 为 Android 移动开发的首选语言。2021 年,Google 发布了基于 Kotlin 的声明式 UI 框架 – Jetpack Compose 1.0 版本。本质上,Jetpack Compose 和 SwiftUI 类似,都是声明式编程,都是单一可信数据源驱动 UI 变更。不过,也有几点不同:
1)Jetpack Compose 中的界面表示为一切皆为可组合的函数。这些函数通常建议设计为无附带效应(即不会发生在可组合函数作用域之外的应用状态的变化)。从运行时维度,这些无附带效应的组合函数可并行执行。
2) Jetpack Compose 整体开源,架构上则是提供了多层次的 API,从底往上,提供了运行时, 界面,基础,Material 设计系统的实现,每一层都是基于较低层的公共 API 构建。
3)跨平台能力。在 Google 发布 Jebpack Compose 1.0 版本后,Jetbrains 公司紧接着推出 Compose Multiplatform 项目,将 Compose 延伸到桌面平台(Windows, macOS, Linux),同时,配合 Kotlin Multiplatform 项目中的跨平台类库(网络、存储等),进一步简化基于 Jetpack Compose 的跨平台应用的开发。
在开发工具层面,Android Studio 支持 Jetpack Compose 实时预览等特性,但目前成熟度相对弱一些,截至 2022 年,还未支持多设备预览,代码界面的双向预览,预览时调试,以及基础系统能力(比如网络/存储)的模拟等。
Google 在 2022/10/24 给出的 Jetpack Compose 路线图中,Jetpack Compose 接下来重点会围绕性能体验,高级组件,开发工具改进(包括预览和实时编辑),多平台支持(WearOS,大屏设备,主屏幕 Widget)等方面展开。
总体而言,不管是原生框架,还是三方框架,除了性能体验,关键演进方向主要包括以下几点:
1)开发方式从命令式转为声明式;
2)语言和框架和工具结合更加紧密;
3)逐步增加多设备以及跨平台支持。
评论