专栏名称: xiangzhihong
前端跨平台工程师、客户端工程师
目录
相关文章推荐
中国政府网  ·  李强在福建调研时强调 ... ·  昨天  
长安街知事  ·  16岁考入清华的石磊,升任新职 ·  2 天前  
芋道源码  ·  Spring ... ·  2 天前  
51好读  ›  专栏  ›  xiangzhihong

淘宝,天猫Virtualview

xiangzhihong  · 掘金  ·  · 2019-01-21 06:03

正文


前言

  • 淘宝、天猫一直致力于解决 页面动态化 的问题
  • 在2017年的4月发布了 v1.0 解决方案: Tangram 模型 及其对应的 Android vlayout ,该解决方案在手机淘宝、天猫 Android 版 内广泛使用

电商图

若还不了解 Tangram 模型 和 vlayout ,具体请看文章
1. Android Tangram模型:连淘宝、天猫都在用的UI框架模型你一定要懂
2. Android开源库V - Layout:淘宝、天猫都在用的UI框架,赶紧用起来吧!

  • 在同年的12月,阿里团队对此作了重大更新:发布了 Tangram2.0 版本,主要是补充了 Android VirtualView ,也广泛应用于淘宝、天猫客户端
    示意图

  • 今天,我将带大家全面了解 Tangram 2.0 版本的新成员: Virtualview

    Virtualview的Github地址


目录

示意图


1. 为什么要向 Tangram模型 加入 VirtualView

即 为什么要更新 Tangram2.0 版本

  • 结论

    1. 提升组件动态性,实现动态更新
    2. 提升了组件的渲染性能
  • 具体描述
    示意图

而上述解决方案的承载方案,则是 VirtualView

VirtualView的Github地址


2. VirtualView介绍

  • 简介
    示意图

  • 特点

示意图


3. 实现原理

3.1 核心思路

根据 Tangram v1.0 中 出现的问题: UI 组件无法动态更新 & 加载性能低, VirtualView 的具体解决方案如下

示意图

3.2 实现方案

  • 根据其原理, VirtualView 的实现方案是: 虚拟化开发
  • 虚拟化开发的本质:

示意图

之所以称为虚拟化,是因为 Canvas 绘制的视图不存在一一对应的实体 View

3.3 总结

从上可知, VirtualView 的创新在于:
1. 通过 XML 模板实现组件的动态性
2. 通过 虚拟化技术 (本质 = 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 内容,从而创建虚拟化组件

    即 上述则是虚拟化创建组件的过程

  • 具体过程

    1. 实现基础组件需遵循一个接口的规范:定义了渲染过程中所需的3个流程:测量尺寸阶段、布局阶段 & 绘制阶段

      a. 定义这3个阶段是为了符合 Android 系统的使用,即 View 绘制的三大流程: measure 过程、 layout 过程、 draw 过程。若不了解,请看文章
      (2)自定义View Measure过程 - 最易懂的自定义View原理系列
      (3)自定义View Layout过程 - 最易懂的自定义View原理系列
      (4)自定义View Draw过程- 最易懂的自定义View原理系列
      b. 在 iOS 平台下也需按照本方案的规范去处理

    2. 这3个过程具体如下:(与 Android View 绘制的三大流程相似)
      示意图

      不论是虚拟 / 原生组件,都采用上述模型 & 流程定义
      a. 对于虚拟组件:在这些接口里 实现相关逻辑 / 通过封装原生组件实现
      b. 对于原生组件:在这些接口的实现里 调用原生组件的对应逻辑
      结论:可混合使用虚拟控件 & 实体控件

至此,对于宿主的布局容器来说, 包装在内部的组件不分虚拟化 /原生,暴露在外的接口相同,只要将宿主容器像普通的 View 一样添加到的视图界面上,就可在后续的渲染过程中显示出来。


  • 特别注意
    此处即可解释 为何渲染性能高 :因虚拟组件使用得越多, View 个数就越少,即层级越扁平

如下所示的组件:
a. 普通的原生开发:2层(宿主容器层 + 图片组件层)
b. 虚拟化开发:采用虚拟化开发后,最终呈现的 View层级只有一个宿主容器(实际上,图片组件被绘制在 Canvas 里了)
示意图

1.3 总结

创建 UI 组件有2种方式:
1. 直接使用框架内置的 UI 组件
2. 自定义组件:通过封装好的 Canvas 流程,按照指定接口协议实现绘制逻辑 / 封装原生组件


流程2:创建界面模板 & 下发

  • 该步骤包括多个步骤:创建 XML 界面模板、编译成二进制数据、下发等
  • 具体如下

示意图

2.1 创建XML界面模板

  • 具体描述 根据业务需求,使用XML编写模板

    注:需使用专门的工具 virtualview_tools 编写,其
    使用说明见文章 virtualview_tools使用指南

  • 此方式类似: Android 平台上通过 XML 搭建界面的方式

  • 区别在于:
    1. 脱离了平台限制,即一套模板可同时在 Android iOS 上使用
    2. 运行时动态加载 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"
}

2.2 编译成二进制数据

2.2.1 具体描述

使用专门的工具 virtualview_tools 将编写好的 XML 界面模板编译成二进制数据,编译后的文件的后缀名是 .out
示意图

使用说明见文章 virtualview_tools使用指南

注:为什么通过 XML 编写的业务组件 不直接在客户端里运行使用,而是先进行一次二进制序列化操作?

示意图

2.2.2 二进制文件描述

借鉴了 Android 系统编译模板文件的思路,格式 & 描述具体如下

示意图

2.2.2 编译流程

  • 一个业务组件对应着一份 XML 模板 = 单独编译成二进制数据

    编译数据 含除内置字符串资源外 它依赖的所有字符串、表达式资源

  • 编译规则
    编译时,模板里涉及的资源包括颜色值、各种枚举、基础组件的类型等都会被序列化映射成整数;不能序列化成整数的资源如字符串,就分配一个索引 Id 指向它 & 将它们单独存储到一块区域里

    1. 原因:当模板在线发布、字符串有变动的情况下,能够不影响原来的字符串资源索引;否则若按照带有顺序约定的协议来分配资源索引,很容易在模板变更时 同一索引值在变更前后指向的资源内容是不一样,影响稳定性和动态性
    2. 序列化的规则如下:

示意图

  • 编译流程

示意图

2.3 模板数据 下发到客户端

即 客户端获取编译后的二进制数据

获取有2种路径:
1. 直接将编译后的模板打包到客户端里,开发者通过代码加载
2. 框架先发布到模板管理后台,客户端在线更新到模板数据(即实现了动态更新)


流程3:客户端加载界面

  • 客户端获取到编译后的界面模板后,进行加载 & 解析,最终渲染出视图界面
  • 步骤流程如下图

示意图

3.1 解析模板数据

  • 具体描述 客户端获得编译后的模板数据(二进制数据)后,立即 进行解析

    1. 如校验版本号,合法性,读取头信息等
    2. 客户端渲染组件 从解析 编译后的模板数据开始
  • 流程解析 解析过程 = 二进制编译的逆过程

    但解析流程只负责提取原始数据 & 组织格式,并无构建出组件对象

示意图

3.2 加载组件视图

  • 具体描述 当用户传入一个模板名称,框架内部就会根据名称去之前解析XML界面模板的数据里找到 与此名称匹配的模板数据,然后加载 & 创建出真正的组件

  • 流程解析







请到「今天看啥」查看全文