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…