v1.0
解决方案:Tangram
模型 及其对应的 Android
库 vlayout
,该解决方案在手机淘宝、天猫 Android
版 内普遍使用若还不了解
Tangram
模型 和vlayout
,具体请看文章前端
在同年的12月,阿里团队对此做了重大更新:发布了Tangram2.0
版本,主要是补充了Android
库VirtualView
,也普遍应用于淘宝、天猫客户端 git
今天,我将带你们全面了解Tangram 2.0
版本的新成员:Virtualview
github
即 为何要更新
Tangram2.0
版本数组
而上述解决方案的承载方案,则是 VirtualView
缓存
简介 微信
特色架构
根据Tangram v1.0
中 出现的问题:UI
组件没法动态更新 & 加载性能低,VirtualView
的具体解决方案以下框架
VirtualView
的实现方案是:虚拟化开发之因此称为虚拟化,是由于
Canvas
绘制的视图不存在一一对应的实体View
从上可知,VirtualView
的创新在于:
XML
模板实现组件的动态性Canvas
)开发组件,提高了组件的渲染性能VirtualView
的本质原理 & 总体架构后VirtualView
的工做流程VirtualView
的工做流程分为3大部分:建立UI组件、建立界面模板 & 客户端加载界面下面我将对每一个流程的原理 & 过程详细分析
UI
组件有2种建立方式:使用框架内置(封装好)的UI
组件 / 自定义
UI
组件UI
组件而不需自身建立注: a. 自定义组件应继承基础组件 b. 系统封装
UI
组件的原理 同 “自定义UI
组件,下面将具体讲解
###1.2 自定义UI组件 若框架内置的UI
组件没法知足需求,则开发者可自定义UI
组件
VirtualView
抽象 & 封装了 Canvas
绘制视图的流程,使得开发者只需按指定的接口协议实现1个组件的绘制逻辑:测量、绘制 & 绘制,即能实如今宿主容器经过 Canvas
直接绘制 UI
内容,从而建立虚拟化组件即 上述则是虚拟化建立组件的过程
a. 定义这3个阶段是为了符合
Android
系统的使用,即View
绘制的三大流程:measure
过程、layout
过程、draw
过程。若不了解,请看文章 (2)自定义View Measure过程 - 最易懂的自定义View原理系列 (3)自定义View Layout过程 - 最易懂的自定义View原理系列 (4)自定义View Draw过程- 最易懂的自定义View原理系列 b. 在iOS
平台下也需按照本方案的规范去处理
Android View
绘制的三大流程类似)
不管是虚拟 / 原生组件,都采用上述模型 & 流程定义 a. 对于虚拟组件:在这些接口里实现相关逻辑 / 经过封装原生组件实现 b. 对于原生组件:在这些接口的实现里 调用原生组件的对应逻辑 结论:可混合使用虚拟控件 & 实体控件
至此,对于宿主的布局容器来讲,包装在内部的组件不分虚拟化 / 原生,暴露在外的接口相同,只要将宿主容器像普通的 View
同样添加到的视图界面上,就可在后续的渲染过程当中显示出来。
View
个数就越少,即层级越扁平以下所示的组件: a. 普通的原生开发:2层(宿主容器层 + 图片组件层) b. 虚拟化开发:采用虚拟化开发后,最终呈现的 View层级只有一个宿主容器(实际上,图片组件被绘制在
Canvas
里了)
建立UI
组件有2种方式:
UI
组件Canvas
流程,按照指定接口协议实现绘制逻辑 / 封装原生组件XML
界面模板、编译成二进制数据、下发等注:需使用专门的工具
virtualview_tools
编写,其 使用说明见文章virtualview_tools使用指南
Android
平台上经过 XML
搭建界面的方式Android
、iOS
上使用XML
模板数据,动态更新界面结构// 引用的组件经过流程1中获取
// 动态数据经过表达式从 JSON 数据里获取
<?xml version="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"/>
</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"
}
复制代码
使用专门的工具virtualview_tools
将编写好的XML
界面模板编译成二进制数据,编译后的文件的后缀名是.out
使用说明见文章virtualview_tools使用指南
注:为何经过 XML 编写的业务组件 不直接在客户端里运行使用,而是先进行一次二进制序列化操做?
借鉴了 Android
系统编译模板文件的思路,格式 & 描述具体以下
XML
模板 = 单独编译成二进制数据编译数据 含除内置字符串资源外 它依赖的全部字符串、表达式资源
Id
指向它 & 将它们单独存储到一块区域里
- 缘由:当模板在线发布、字符串有变更的状况下,可以不影响原来的字符串资源索引;不然若按照带有顺序约定的协议来分配资源索引,很容易在模板变动时 同一索引值在变动先后指向的资源内容是不同,影响稳定性和动态性
- 序列化的规则以下:
即 客户端获取编译后的二进制数据
获取有2种路径:
- 如校验版本号,合法性,读取头信息等
- 客户端渲染组件 从解析 编译后的模板数据开始
但解析流程只负责提取原始数据 & 组织格式,并没有构建出组件对象
具体描述 当用户传入一个模板名称,框架内部就会根据名称去以前解析XML界面模板的数据里找到 与此名称匹配的模板数据,而后加载 & 建立出真正的组件
流程解析
因业务数据是动态的,故从模板建立的组件不含业务数据
- 经过表达式解析、访问获得的属性值,会缓存起来,当原始数据引用不变时,每次访问都会获取到缓存值
- 此处接收的数据是
JSON
格式
VirtualView
的总体框架分为2部分:核心功能模块(5个模块) + 配套工具 & 服务。具体以下:示意图
说明
示意图
说明
此处详细分析 基础组件模型 & 虚拟组件
含基础组件 & 基础属性,具体以下
注:自定义的基础组件应继承基础定义 & 扩展
示意图
说明
特别注意:引入用户数据绑定的表达式的缘由 开发业务组件时,基础属性 / 样式不能在模板里直接写死,而是需从数据里动态获取
/**
* 访问数据属性的表达式
* 语法说明
* 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 };
复制代码
示意图
说明
示意图
说明
示意图
说明
UI
组件、建立界面模板 & 客户端加载界面从一文可知,建立UI
组件有2种方式:
UI
组件Canvas
流程,按照指定接口协议实现绘制逻辑 / 封装原生组件此处为方便讲解,直接使用框架内置的UI
组件
此步骤包括:建立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"
}
复制代码
使用专门的工具virtualview_tools
将编写好的XML
界面模板编译成二进制数据,编译后的文件的后缀名是.out
使用说明见文章virtualview_tools使用指南
有2种路径:
此处选择方式1
具体使用以下
// 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
public boolean process(EventData data) {
//handle here
return true;
}
});
vafContext.getEventManager().register(EventManager.TYPE_Exposure, new IEventProcessor() {
@Override
public boolean process(EventData data) {
//handle here
return true;
}
});
// 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的使用文档
对于我的的见解,VirtualView
的补充其重大意义在于2个方面:对于 阿里Tangram
模型 & 整个原生开发技术(Android、iOS)
VirtualView
的解决的问题 在于:
XML
界面模板 & 数据Canvas
)开发组件VirtualView
的创新在于:解决了 原生开发中一直被诟病 而 常被叫喧会被 前端、RN
技术取代的问题:
开发周期长 & 成本大 VirtualView
采用XML
描述视图,XML
界面模板具有跨平台使用的特性
没法热更新 VirtualView
可在端上绑定动态下发的 XML
界面模板 & 数据,从而实现热更新
相比于前几年产品开发的一味求快,现在互联网行业发展暂缓、用户需求基本知足的状况下,更加 讲求的是用户体验
因此,实际上对比于 前端、RN
技术在客户端的实现,VirtualView
的优点或许会更明显:在解决了原生开发效率慢、周期长的前提下,保证了原生开发的优点:性能好
VirtualView
推进了原生开发的发展,但目前来讲,VirtualView
仍是存在很多问题VirtualView
的使用 & 原理Tangram
的使用,建议看文章:Android
其余优秀的开源库 进行详细分析,有兴趣能够继续关注Carson_Ho的安卓开发笔记参考文章: juejin.im/post/5a2a71… tangram.pingguohe.net/docs/virtua…