淘宝、天猫又开源了一个动态化、高性能的UI框架
前言
淘宝、天猫一直致力于解决 页面动态化的问题
在2017年的4月发布了
v1.0解决方案:Tangram模型 及其对应的Android库vlayout,该解决方案在手机淘宝、天猫Android版 内广泛使用
电商图
在同年的12月,阿里团队对此作了重大更新:发布了
Tangram2.0版本,主要是补充了Android库VirtualView,也广泛应用于淘宝、天猫客户端示意图
今天,我将带大家全面了解
Tangram 2.0版本的新成员:Virtualview
Virtualview的Github地址:
https://github.com/alibaba/Virtualview-Android
目录
示意图
1. 为什么要向 Tangram模型 加入 VirtualView
即 为什么要更新
Tangram2.0版本
结论
提升组件动态性,实现动态更新
提升了组件的渲染性能
具体描述
示意图
而上述解决方案的承载方案,则是 VirtualView
VirtualView的Github地址:
https://github.com/alibaba/Virtualview-Android
2. VirtualView介绍
简介
示意图
特点
示意图
3. 实现原理
3.1 核心思路
根据Tangram v1.0中 出现的问题:UI组件无法动态更新 & 加载性能低,VirtualView的具体解决方案如下
示意图
3.2 实现方案
根据其原理,
VirtualView的实现方案是:虚拟化开发虚拟化开发的本质:
示意图
之所以称为虚拟化,是因为
Canvas绘制的视图不存在一一对应的实体View
3.3 总结
从上可知,VirtualView的创新在于:
通过
XML模板实现组件的动态性通过 虚拟化技术(本质 =
Canvas)开发组件,提升了组件的渲染性能
4. 工作流程
在了解了
VirtualView的本质原理 & 整体架构后下面,我将开始讲解
VirtualView的工作流程
4.1 流程概述
根据上述方案,
VirtualView的工作流程分为3大部分:创建UI组件、创建界面模板 & 客户端加载界面具体如下
示意图
4.2 流程详细分析
下面我将对每个流程的原理 & 过程详细分析
流程1:创建UI组件
具体描述
根据业务需求,创建所需要的
UI组件
示意图
有2种创建方式:使用框架内置(封装好)的UI组件 / 自定义
1.1 使用框架内置(封装好)的UI组件
即 可直接使用封装好的
UI组件而不需自身创建具体如下(含组件基础属性)
注:
a. 自定义组件应继承基础组件
b. 系统封装
UI组件的原理 同 “自定义UI组件,下面将具体讲解
示意图
1.2 自定义UI组件
若框架内置的UI组件无法满足需求,则开发者可自定义UI组件
自定义流程
VirtualView抽象 & 封装了Canvas绘制视图的流程,使得开发者只需按指定的接口协议实现1个组件的绘制逻辑:测量、绘制 & 绘制,即能实现在宿主容器通过Canvas直接绘制UI内容,从而创建虚拟化组件
即 上述则是虚拟化创建组件的过程
具体过程
实现基础组件需遵循一个接口的规范:定义了渲染过程中所需的3个流程:测量尺寸阶段、布局阶段 & 绘制阶段
a. 定义这3个阶段是为了符合
Android系统的使用,即View绘制的三大流程:measure过程、layout过程、draw过程。若不了解,请看文章(2)自定义View Measure过程 - 最易懂的自定义View原理系列
(3)自定义View Layout过程 - 最易懂的自定义View原理系列
(4)自定义View Draw过程- 最易懂的自定义View原理系列
b. 在
iOS平台下也需按照本方案的规范去处理
这3个过程具体如下:(与
Android View绘制的三大流程相似)示意图
不论是虚拟 / 原生组件,都采用上述模型 & 流程定义
a. 对于虚拟组件:在这些接口里实现相关逻辑 / 通过封装原生组件实现
b. 对于原生组件:在这些接口的实现里 调用原生组件的对应逻辑
结论:可混合使用虚拟控件 & 实体控件
至此,对于宿主的布局容器来说,包装在内部的组件不分虚拟化 /
原生,暴露在外的接口相同,只要将宿主容器像普通的 View 一样添加到的视图界面上,就可在后续的渲染过程中显示出来。
特别注意
此处即可解释 为何渲染性能高:因虚拟组件使用得越多,
View个数就越少,即层级越扁平
如下所示的组件:
a. 普通的原生开发:2层(宿主容器层 + 图片组件层)
b. 虚拟化开发:采用虚拟化开发后,最终呈现的 View层级只有一个宿主容器(实际上,图片组件被绘制在
Canvas里了)
示意图
1.3 总结
创建UI组件有2种方式:
直接使用框架内置的
UI组件自定义组件:通过封装好的
Canvas流程,按照指定接口协议实现绘制逻辑 / 封装原生组件
流程2:创建界面模板 & 下发
该步骤包括多个步骤:创建
XML界面模板、编译成二进制数据、下发等具体如下
示意图
2.1 创建XML界面模板
具体描述
根据业务需求,使用XML编写模板
注:需使用专门的工具
virtualview_tools编写,其使用说明见文章virtualview_tools使用指南
此方式类似:
Android平台上通过XML搭建界面的方式区别在于:
脱离了平台限制,即一套模板可同时在
Android、iOS上使用运行时动态加载
XML模板数据,动态更新界面结构
// 引用的组件通过流程1中获取
// 动态数据通过表达式从 JSON 数据里获取
"1.0" encoding="utf-8"?>
<vhlayout
flag="flag_exposure|flag_clickable"
orientation="H"
layoutWidth="match_parent"
layoutHeight="wrap_content">
<nimage
id="1"
src="${logoUrl}"
layoutMarginLeft="8"
layoutMarginRight="8"
layoutMarginTop="8"
layoutMarginBottom="8"
layoutWidth="32"
layoutHeight="32"/>
<ntext
id="2"
text="${title}"
layoutGravity="v_center"
gravity="${style.text-align}"
textSize="${style.font-size}"
textColor="${style.color}"
layoutWidth="match_parent"
layoutHeight="wrap_content"/>
// JSON数据
{ "style": { "text-align": "h_center", "font-size": "20", "color": "#FF5000"
}, "title": "超高性 99.9% 的用户觉得很快", "logoUrl": "https://gw.alicdn.com/tfs/TB1yGIdkb_I8KJjy1xaxxbsxpxa-72-72.png"
2.2 编译成二进制数据
2.2.1 具体描述
使用专门的工具virtualview_tools将编写好的XML界面模板编译成二进制数据,编译后的文件的后缀名是.out
示意图
使用说明见文章virtualview_tools使用指南
注:为什么通过 XML 编写的业务组件 不直接在客户端里运行使用,而是先进行一次二进制序列化操作?
示意图
2.2.2 二进制文件描述
借鉴了 Android 系统编译模板文件的思路,格式 & 描述具体如下
示意图
2.2.2 编译流程
一个业务组件对应着一份
XML模板 = 单独编译成二进制数据
编译数据 含除内置字符串资源外 它依赖的所有字符串、表达式资源
编译规则
编译时,模板里涉及的资源包括颜色值、各种枚举、基础组件的类型等都会被序列化映射成整数;不能序列化成整数的资源如字符串,就分配一个索引
Id指向它 & 将它们单独存储到一块区域里
原因:当模板在线发布、字符串有变动的情况下,能够不影响原来的字符串资源索引;否则若按照带有顺序约定的协议来分配资源索引,很容易在模板变更时 同一索引值在变更前后指向的资源内容是不一样,影响稳定性和动态性
序列化的规则如下:
示意图
编译流程
示意图
2.3 模板数据 下发到客户端
即 客户端获取编译后的二进制数据
获取有2种路径:
直接将编译后的模板打包到客户端里,开发者通过代码加载
框架先发布到模板管理后台,客户端在线更新到模板数据(即实现了动
态更新)
流程3:客户端加载界面
客户端获取到编译后的界面模板后,进行加载 & 解析,最终渲染出视图界面
步骤流程如下图
示意图
3.1 解析模板数据
具体描述
客户端获得编译后的模板数据(二进制数据)后,立即 进行解析
如校验版本号,合法性,读取头信息等
客户端渲染组件 从解析 编译后的模板数据开始
流程解析
解析过程 = 二进制编译的逆过程
但解析流程只负责提取原始数据 & 组织格式,并无构建出组件对象
示意图
3.2 加载组件视图
具体描述
当用户传入一个模板名称,框架内部就会根据名称去之前解析XML界面模板的数据里找到 与此名称匹配的模板数据,然后加载 & 创建出真正的组件
流程解析
示意图
3.3 绑定业务数据
具体描述
开发者在组件属性里可通过 表达式 指定使用哪个数据字段,即将业务数据绑定到组件上
因业务数据是动态的,故从模板创建的组件不含业务数据
流程解析
在创建组件的过程中,当解析属性碰到表达式时,会将该属性的key、表达式值、所属的基础组件等关系存储起来,等真实数据到达后再通过 表达式里的定义 访问数据 & 将真实值设置给组件的属性,即将真实的数据绑定到基础组件的属性上
通过表达式解析、访问得到的属性值,会缓存起来,当原始数据引用不变时,每次访问都会获取到缓存值
此处接收的数据是
JSON格式
示意图
4.3 总结
示意图
5. 整体架构设计
根据上述方案 & 工作流程,
VirtualView的整体框架分为2部分:核心功能模块(5个模块) + 配套工具 & 服务。具体如下:
示意图
下面,我将对每部分进行详细分析
模块1:加载模块
示意图
示意图
说明
示意图
模块2:构造模块
示意图
示意图
说明
示意图
此处详细分析 基础组件模型 & 虚拟组件
a. 基础组件模型
含基础组件 & 基础属性,具体如下
注:自定义的基础组件应继承基础定义 & 扩展
示意图
模块3:辅助模块
示意图
示意图
说明
示意图
特别注意:引入用户数据绑定的表达式的原因
开发业务组件时,基础属性 / 样式不能在模板里直接写死,而是需从数据里动态获取
/**
* 访问数据属性的表达式
* 语法说明
* a. 以 “${” 开头、以 “}” 结束
* b. 对于Map,通过“.”操作符访问
* c. 对于 Array / List,通过 “[]” 操作符访问
* 示例如下
*/ ${benefitImgUrl}; ${data[0].benefitImgUrl};
/**
* 条件表达式
* 作用:根据数据中某个字段 来设置值的属性
* 语法说明
* a. 以 “@{” 开头、以 “}” 结束,
* b. 中间部分 = 表达式的具体内容: 条件表达式 ? 结果表达式[1] : 结果表达式[2]
* 注:1. 当条件表达式成立的时,使用结果表达式[1],否则使用结果表达式[2]
* 2. 条件表达式支持布尔类型、字符串类型、JSONObject、JSONArray
* c. 对于 Array / List,通过 “[]” 操作符访问
* 示例如下
*/
@{${logoUrl} ? visible : invisible };
模块4:管理模块
示意图
示意图
说明
示意图
模块5:更新模块
示意图
示意图
说明
示意图
配套使用的工具 & 服务
示意图
示意图
说明
示意图
总结
示意图
6. 使用教程
根据上述工作流程,其使用流程同样分为3步:创建
UI组件、创建界面模板 & 客户端加载界面下面,我将根据上述3个步骤进行详细解析
6.1 创建UI组件
从一文可知,创建UI组件有2种方式:
直接使用框架内置的
UI组件自定义组件:通过封装好的
Canvas流程,按照指定接口协议实现绘制逻辑 / 封装原生组件
此处为方便讲解,直接使用框架内置的UI组件
6.2 创建界面模板
此步骤包括:创建XML界面模板、编译成二进制数据、模板下发
6.2.1 创建XML界面模板
根据业务需求,使用XML编写模板
注:需使用专门的工具
virtualview_tools编写,其使用说明见文章virtualview_tools使用指南
示例布局
/**
* 使用说明:
* 1. 控件引用:通过XML引用控件为方便讲解,XML内引用的VHLayout、NImage、NText 都是框架内置的控件:2个横向线性布局;每个布局 = 1个图 + 1个文本
* 2. 属性设置:可写死 / 通过表达式绑定一个数据字段(JSON)引用
* 布局说明:
* 1. 引用的控件VHLayout、NImage、NText等都是框架内置的控件
* 2. 整个布局 = 2个横向线性布局,每个布局 = 1个图 + 1个文本
*/xml version="1.0" encoding="utf-8"?><VHLayout
flag="flag_exposure|flag_clickable"
orientation="V"
layoutWidth="match_parent"
layoutHeight="wrap_content">
<VHLayout
flag="flag_exposure|flag_clickable"
orientation="H"
layoutWidth="match_parent"
layoutHeight="wrap_content">
<NImage
id="1"
src="${logoUrl}"
layoutMarginLeft="8"
layoutMarginRight="8"
layoutMarginTop="8"
layoutMarginBottom="8"
layoutWidth="32"
layoutHeight="32"/>
<NText
id="2"
text="${title}"
layoutGravity="v_center"
gravity="${style.text-align}"
textSize="${style.font-size}"
textColor="${style.color}"
layoutWidth="match_parent"
layoutHeight="wrap_content"/>
VHLayout>
<VHLayout
flag="flag_exposure|flag_clickable"
orientation="H"
layoutWidth="match_parent"
layoutHeight="wrap_content">
<VImage
id="1"
src="${logoUrl}"
layoutMarginLeft="8"
layoutMarginRight="8"
layoutMarginTop="8"
layoutMarginBottom="8"
layoutWidth="32"
layoutHeight="32"/>
<VText
id="2"
text="${title}"
layoutGravity="v_center"
gravity="${style.text-align}"
textSize="${style.font-size}"
textColor="${style.color}"
layoutWidth="match_parent"
layoutHeight="wrap_content"/>
VHLayout>VHLayout>
属性数据来源:
JSON
{ "style": { "text-align": "h_center", "font-size": "20", "color": "#FF5000"
}, "title": "超高性 99.9% 的用户觉得很快", "logoUrl": "https://gw.alicdn.com/tfs/TB1yGIdkb_I8KJjy1XaXXbsxpXa-72-72.png"}
6.2.2 编译成二进制数据
使用专门的工具virtualview_tools将编写好的XML界面模板编译成二进制数据,编译后的文件的后缀名是.out
示意图
使用说明见文章virtualview_tools使用指南
6.2.3 模板下发到客户端
有2种路径:
直接将编译后的模板打包到客户端里,开发者通过代码加载
框架先发布到模板管理后台,客户端在线更新到模板数据(即实现了动态更新)
6.3 客户端解析 & 加载界面模板
具体使用如下
// 1. 初始化图片加载器
VafContext.loadImageLoader(mContext.getApplicationContext());// 2. 初始化 ViewManager 对象
ViewManager viewManager = vafContext.getViewManager();
viewManager.init(mContext.getApplicationContext());// 3. 加载编译后的模板数据(二进制文件)
// 方式1:直接加载二进制字节数组(推荐使用)
viewManager.loadBinBufferSync(TMALLCOMPONENT1.BIN);
viewManager.loadBinBufferSync(TMALLCOMPONENT2.BIN); // 方式2:通过二进制文件路径加载
viewManager.loadBinFileSync(TMALLCOMPONENT1_PATH);
viewManager.loadBinFileSync(TMALLCOMPONENT2_PATH);// 4. 注册事件处理器,如常用的点击、曝光处理
vafContext.getEventManager().register(EventManager.TYPE_Click, new IEventProcessor() { @Override
publicbooleanprocess(EventData data){ //handle here
returntrue;
}
});
vafContext.getEventManager().register(EventManager.TYPE_Exposure, new IEventProcessor() { @Override
publicbooleanprocess(EventData data){ //handle here
returntrue;
}
});// 5. 通过组件名参数 name 生成组件实例
View container = vafContext.getContainerService().getContainer(name, true);
mLinearLayout.addView(container);// 6. 为组件绑定真实的数据
// 假若您在组件模板里写了数据绑定的表达式
IContainer iContainer = (IContainer)container;
JSONObject json = getJSONDataFromAsset(data); if (json != null) {
iContainer.getVirtualView().setVData(json);
}
测试结果
下图展示的“超高性 99.9% 的用户觉得很快”即为
VirtualView的展示效果
示意图
至此,关于VirtualView的使用讲解完毕
更加详细使用,请参考文章VirtualView的使用文档
7.VirtualView 的意义
对于个人的看法,VirtualView的补充其重大意义在于2个方面:对于 阿里Tangram 模型 & 整个原生开发技术(Android、iOS)
7.1 对于 Tangram 模型
VirtualView的解决的问题 在于:
实现组件的动态性:可在端上绑定动态下发的
XML界面模板 & 数据提升了组件的渲染性能:通过 虚拟化技术(本质 =
Canvas)开发组件
7.2 对于整个原生开发技术(Android、iOS)
VirtualView的创新在于:解决了 原生开发中一直被诟病 而 常被叫喧会被 前端、RN技术取代的问题:
开发周期长 & 成本大
VirtualView采用XML描述视图,XML界面模板具备跨平台使用的特性无法热更新
VirtualView可在端上绑定动态下发的XML界面模板 & 数据,从而实现热更新
相比于前几年产品开发的一味求快,如今互联网行业发展暂缓、用户需求基本满足的情况下,更加 讲求的是用户体验
所以,实际上对比于 前端、
RN技术在客户端的实现,VirtualView的优势或许会更明显:在解决了原生开发效率慢、周期长的前提下,保证了原生开发的优势:性能好
7.3 呼吁
虽然
VirtualView推动了原生开发的发展,但目前来说,VirtualView还是存在不少问题希望大家能一起在Github - alibaba - VirtualView 上进行完善,共同为开源事业做贡献吧!
8. 总结
看完本文,你应该非常了解阿里出品的
VirtualView的使用 & 原理
编辑:千锋UI设计
来源:简书
链接:https://www.jianshu.com/p/5bd7a210b800
淘宝、天猫又开源了一个动态化、高性能的UI框架的更多相关文章
- Python网页信息采集:使用PhantomJS采集淘宝天猫商品内容
1,引言 最近一直在看Scrapy 爬虫框架,并尝试使用Scrapy框架写一个可以实现网页信息采集的简单的小程序.尝试过程中遇到了很多小问题,希望大家多多指教. 本文主要介绍如何使用Scrapy结合P ...
- 简单的抓取淘宝关键字信息、图片的Python爬虫|Python3中级玩家:淘宝天猫商品搜索爬虫自动化工具(第一篇)
Python3中级玩家:淘宝天猫商品搜索爬虫自动化工具(第一篇) 淘宝改字段,Bugfix,查看https://github.com/hunterhug/taobaoscrapy.git 由于Gith ...
- 淘宝天猫关键词SEO优化
淘宝天猫的网站完全像是一个成熟的搜索引擎,只是从google.bing.baidu改成了淘宝天猫而已,普通搜索引擎有品专,有皇冠,有PC,有无线:淘宝天猫里面有钻展,有直通车,也有PC,无线.搜索引擎 ...
- 【淘宝客】根据淘客联盟精选清单(淘宝天猫内部优惠券)随机显示淘宝天猫优惠券dome
也许大家在生活中经常淘宝看到[淘宝天猫内部优惠券]的网站,或者在微博中经常有博主发券,让大家生活中购物便宜许多,作为一个站长,我们也希望自己的网站也能有这样的一个功能,现在就分享给大家,还是免后台哦. ...
- 浅析 阿里 OceanBase 双十一 淘宝天猫 天量交易 承载能力 原理
我们先看看 这 2 篇文章: <秘诀!支付宝支撑双十一4200万次/秒的数据库请求峰值的技术实现> https://mp.weixin.qq.com/s?__biz=MzI3MzEzMD ...
- [淘宝客技术篇008](无需登录)淘宝天猫优惠券JSON接口1
今天,小星给大家分享的是一个非常重要,非常有意义的接口:获取淘宝天猫优惠券的JSON接口. 先上个链接: http://uland.taobao.com/cp/coupon_list?pid=mm_2 ...
- 漫谈单点登录(SSO)(淘宝天猫)
1. 摘要 ( 注意:请仔细看下摘要,留心此文是否是您的菜,若浪费宝贵时间,深感歉意!!!) SSO这一概念由来已久,网络上对应不同场景的成熟SSO解决方案比比皆是,从简单到复杂,各式各样应有尽有!开 ...
- 漫谈单点登录(SSO)(淘宝天猫)(转载)
1. 摘要 ( 注意:请仔细看下摘要,留心此文是否是您的菜,若浪费宝贵时间,深感歉意!!!) SSO这一概念由来已久,网络上对应不同场景的成熟SSO解决方案比比皆是,从简单到复杂,各式各样应有尽有!开 ...
- php单点登录之模拟淘宝天猫同步登录
说到单点登录大家都很了解,一个站点登录其他域会自动登录. 单点登录SSO(Single Sign On)的方法有很多,比如:p3p.共享session.共享cookice.第三方OAuth认证. 这里 ...
随机推荐
- Python3 引入sqlite3时出现错误:ModuleNotFoundError: No module named '_sqlite3'
在Python3 中内置了SQLite3,但是在编译安装完之后执行: import sqlite3 出现错误: ModuleNotFoundError: No module named '_sqlit ...
- python拓展4 数据结构
内容: 1.数组 2.链表 3.字典 4.二叉树(搜索树) 5.set集合实现 1.数组 数组在python中是以列表的形式存在,基本上每一个语言中都有数组形式的数据结构存在 数组一般存储在连续的一块 ...
- uva-10050-模拟水题
一个社会研究组织决定通过一组简单的参数来模拟国家政党的行为.第一个参数一个正整数h(叫做罢工参数),用于指示在对应的政党在俩个连续休假之间的平均天数.虽然这个参数太简单了,它不是最完美的参数.但是它还 ...
- zabbix_get无法执行agent端的脚本文件解决办法
一,无法执行脚本参考网站:http://blog.51cto.com/13589448/2070180 权限不足时提示: server端提示: [root@yao local]# zabbix_get ...
- HTML5 Canvas ( 绘制一轮弯月, 星空中的弯月 )
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- TDictionary 是delphi用的,c++builder用起来太吃力。
TDictionary 是delphi用的,c++builder用起来太吃力.c++还是用std::map代替.c++d map很好用啊.https://blog.csdn.net/ddkxddkx/ ...
- Spring MVC 自定义类型转换器
新建一个自定义转换器 import org.springframework.core.convert.converter.Converter; import org.springframework.s ...
- egret -纹理集的制作
1. 理集的使用 :http://www.codeandweb.com/ 下载软件: TexturePackergithub: 相关工具:https://github.com/ping-chen/eg ...
- Asp.Net MVC参考资料
Every day up!!!!!! 1.无废话MVC入门教程 2.MVC快速入门 3.MVC小牛之路 4.Web API强势入门指南 5.全网最全的mvc汇总 6.MVC5+EF6+Bootstra ...
- 程序员教程-11章-Java程序设计
自己是学java的,先看第十一章java吧. 列出章节目录,便于自己回忆内容. 11.1 Java语言概述 1 Java语言的特点 2 Java开发环境 11.2 Java语言基础 11.2.1 基本 ...