Android | Tangram動態頁面之路(七)硬核的Virtualview

何爲Virtualview,簡單來講,就是經過xml來描述視圖,而後壓縮成二進制格式,客戶端經過解析並渲染成原生view或交由Canvas繪製的過程。html

系列文章:java

  1. 需求背景
  2. Tangram和vlayout介紹
  3. Tangram的使用
  4. vlayout原理
  5. Tangram原理
  6. json模板和數據分離

GitHub地址:android

本文基於最新源碼分析。git

VirtualView

需求背景一文介紹了模塊化搭建頁面的由來,那有沒有想過這樣一種場景,有天產品靈光一閃,想要不發版把上圖下文換成上文下圖,又或者想要在每一個圖片右上角加個雙11大促角標來營造氛圍,因爲客戶端只預埋了上圖下文的樣式(如下簡稱cell),即ImageTextView,因此只能延期到下一班車,github

很顯然,即使咱們根據當下的業務抽象了一些經常使用的Cell,好比上圖下文純文本單圖等,並且還支持了一些通用的屬性配置如文本大小顏色等,也沒法知足多變的業務需求,也即cell不夠用了,咱們要有線上生產cell並下發的能力。因此,VirtualView誕生了。json

VirtualView的核心思想是,編寫xml樣式文件,編譯壓縮成二進制文件,下發到客戶端,客戶端解析,轉成native view,或者用canvas繪製。引用官方的一張圖片,canvas

所以,當UI有細節變更時,只須要修改xml,而後編譯好下發給客戶端替換便可。不過,咱們的生產環境用的是另一套基於flexbox-layout的方案而非VirtualView,本文是站在學習的角度進行調研。數組

框架名字積木七巧板,可見,類似的業務場景,衍生出了類似的技術方案。服務器

VirtualView很讚的兩點是,他的二進制壓縮實時預覽,接下來進行詳細分析。網絡

二進制壓縮

經過 XML 編寫的業務組件,若是直接加載解析,會有幾個問題:一是原始文件相對較大,由於 XML 裏會有冗餘信息,如空格、換行、還有重複出現的字符串等,文件體積比較大;二是解析 XML 會有必定開銷,相對於二進制數據直接解析,XML 解析會比較重,例如節點遍歷、屬性訪問等都顯得有些臃腫。經過提早將 XML 模板處理成二進制格式,能夠將繁重的解析工做從客戶端運行時中剝離出來,而經過將一些重複的資源作合併處理並創建索引,能夠減小冗餘信息,減小模板文件大小,一般狀況下,處理成二進制格式的模板比原始模板可減小 50% - 60% 的大小。

引用自蘋果核 - VirtualView Android實現詳解(一)—— 文件格式與模板編譯

先來看一個簡單的xml樣式文件,直接把他下發到客戶端存在兩個問題,一是冗餘字符引發的帶寬浪費,二是客戶端解析耗時和內存,在用戶手機內存吃緊時,面對一個樣式繁多的RecyclerView時,即使存在複用機制也可能因解析引發oom(來自電商的痛),每每須要在編譯期就把xml轉成view類,

<VHLayout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:noNamespaceSchemaLocation="../../vv.xsd"
          orientation="V"
          layoutWidth="match_parent"
          layoutHeight="200"
          background="#11000000">

  <NText text="${text}"
         textSize="30"
         textColor="@{${items[0].info.textColor} ? ${items[1].subItems[0].info.textColor} : ${items[2].subItems[0].info.textColor}}"
         background="#fffff0"
         layoutWidth="match_parent"
         layoutHeight="200"
         gravity="h_center|v_center" />

</VHLayout>

連Android自帶的XmlPullParser解析都足夠重了,那咱們能不能避開這個思路呢?來看看VirtualView的思路,首先看到virtualview_tools工程,在virtualview_tools/compiler-tools/RealtimePreview/config.properties文件中,

// 把內置支持的view映射成int,1000之內
VIEW_ID_FrameLayout=1
VIEW_ID_NImage=9
VIEW_ID_VImage=10
// 1000以上給外部自定義的view
VIEW_ID_TotalContainer=1010

// 定義枚舉映射,即xml裏寫的row、row-reverse也會被轉成int
flexDirection=Enum<row:0,row-reverse:1,column:2,column-reverse:3>
orientation=Enum<H:1,V:0>

// 定義一些屬性值的類型
borderWidth=Float
itemWidth=Number

在進行類型的簡化後,約定一種數據格式,每一塊分別展現什麼信息,以下,

好比,開頭有版本區,後面有組件區組件長度區字符串區字符串長度區表達式區表達式長度區...這有點像JVM校驗解析字節碼的過程。一些資源的映射處理,以下,

  • 顏色:轉換成4字節整型顏色值,格式 AARRGGBB;
  • 枚舉:按照預約義的整數轉換,好比 gravity 的類型,orientation 的類型;
  • 字符串:以 hashCode 值做爲它的序列化後整數,並在字符串資源區創建以 hashCode 爲索引的列表,在解析的時候從中獲取原始的字符串值;
  • 邏輯表達式:與字符串的處理相似;
  • 數字:直接轉換成 4 字節的整型或者浮點型,並支持帶單位的類型;

引用自蘋果核 - VirtualView Android實現詳解(一)—— 文件格式與模板編譯

字符串用hashCode值爲索引的列表方案,能夠節省重複字符串的空間,表達式是用來綁定動態數據如${text}

獲得二進制數據,

把二進制數據下發到客戶端,在Virtualview-Android工程中,能夠看到一個BinaryLoader類,

//BinaryLoader.java

//二進制數據,轉成byte數組進行讀取
public int loadFromBuffer(byte[] buf, boolean override) {
    CodeReader reader = new CodeReader();

    reader.setCode(buf);
    reader.seekBy(Common.TAG.length());

    // check version
    int majorVersion = reader.readShort();  //讀取主版本號
    int minorVersion = reader.readShort();  //讀取副版本號
    int patchVersion = reader.readShort();  //讀取修訂版本號
    reader.setPatchVersion(patchVersion);

    int uiStartPos = reader.readInt();  //讀取UI開始位置
    reader.seekBy(4);

    int strStartPos = reader.readInt();  //讀取字符串開始位置
    reader.seekBy(4);

    int exprCodeStartPos = reader.readInt();  //讀取表達式開始位置
    reader.seekBy(4);
}

這樣,把xml樣式文件壓縮成二進制文件,既節省了帶寬,又免去了客戶端比較重的XmlPullParser解析,真是快樂Double~

原生控件和虛擬控件

VirtualView翻譯成中文就是虛擬視圖,由於他裏邊有個虛擬控件的概念。能夠看到它裏邊有些控件有兩份,分別是V和N開頭的,如VImageNImageVTextNText

V開頭指的是Virtual View虛擬視圖,即不須要實際的ImageViewTextView,而是在一個Container(如ViewGroup)內,直接拿他的畫布canvas進行內容繪製,如drawTextdrawBitmap等操做;

N開頭指的是Native View即原生視圖,須要實際的ImageViewTextView來承載。

看下截圖更直觀,

Virtual View

Native View

虛擬視圖跟原生視圖相比會更輕量,固然具體還得結合業務使用,目前支持兩種視圖的混用,這樣就須要去避免一個問題,虛擬視圖畫在宿主上做爲」背景「,原生視圖放在宿主上有可能會遮擋虛擬視圖。

實時預覽

安裝fswatch監聽文件修改,

brew install fswatch

安裝qrencode生成二維碼(可選),

brew install qrencode

virtualview_tools項目virtualview_tools/compiler-tools/RealtimePreview目錄下,執行./run.sh啓動服務器,手機和電腦連同一網絡,手機運行Virtualview-Android項目(記得把HttpUtil類中的ip地址改爲電腦的ip),進入模板實時預覽,能夠加載服務器下發的HelloWorld,點進去就能夠看樣式了,

接着修改文件保存,fswatch監聽到修改,觸發服務器從新編譯HelloWorld

合併結果data.json以下,

{
    "templates": [  //樣式:xml -> 二進制 -> Base64.encode ,客戶端拿到後decode回二進制進行解析
        "QUxJVlYAAQAAOMQAAAAvAAAAkAAAAL8AAAD1AAABuAAAAAAAAAG8AAAAAAABAAAAAAABAApIZWxsb1dvcmxkAH4AAAIEqjL10AAAAABc1fDxAAAAyLCYVS4RAAAAd3CsvP////8AAAACfREwBNF35jvOOvRwYx6r5gAAAAAHBcQtOs4AAAAUXNXw8QAAAMiwmFUu////8BC4ck4AAAAkd3CsvP////8AAAACADZFLUjWynnAmy42tiGl5gAAAQEAAAAGfREwBAAdeHNpOm5vTmFtZXNwYWNlU2NoZW1hTG9jYXRpb262IaXmAG9AeyR7aXRlbXNbMF0uaW5mby50ZXh0Q29sb3J9ID8gJHtpdGVtc1sxXS5zdWJJdGVtc1swXS5pbmZvLnRleHRDb2xvcn0gOiAke2l0ZW1zWzJdLnN1Ykl0ZW1zWzBdLmluZm8udGV4dENvbG9yfX1jHqvmAClodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZc469HAACXhtbG5zOnhzadF35jsADC4uLy4uL3Z2LnhzZEjWynkAByR7dGV4dH0AAAAA"
    ], 
    "data": {  //數據
        "text": "Hello World!"
    }
}

可見實時預覽時,服務端把二進制數據進行了Base64編碼(真實的業務場景也能夠參考),客戶端點擊Refresh按鈕從新加載http://127.0.0.1:7788/helloworld/data.json,在PreviewActivity中,

//PreviewActivity.java
//獲取網絡數據data.json
PreviewData previewData = new Gson().fromJson(string, PreviewData.class);
//取出templates字段
loadTemplates(previewData.templates);

//進行Base64解碼,而後讀取二進制數據進行解析
sViewManager.loadBinBufferSync(Base64.decode(temp, Base64.DEFAULT));

VirtualView的加持下,Tangram的動態能力獲得進一步提高,實現了線上生產cell並下發替換。

一些案例

Tangram

官方show-case

內部Lego

參考文章


相關文章
相關標籤/搜索