知乎移動(dòng)端動(dòng)態(tài)化探索與實(shí)踐!
?BATcoder技術(shù)群,讓一部分人先進(jìn)大廠
大家好,我是劉望舒,騰訊TVP,著有三本業(yè)內(nèi)知名暢銷書,連續(xù)四年蟬聯(lián)電子工業(yè)出版社年度優(yōu)秀作者,谷歌開發(fā)者社區(qū)特邀講師,百度百科收錄的高級技術(shù)專家,CSDN 2018年度博客之星。
前華為架構(gòu)師,現(xiàn)大廠技術(shù)總監(jiān)。
想要加入?BATcoder技術(shù)群,公號回復(fù)BAT?即可。
作者: 于天航
https://zhuanlan.zhihu.com/p/64968076
移動(dòng)端需求的用戶觸達(dá)效率低,新的產(chǎn)品樣式上線不夠快。想要快速上線新樣式有兩個(gè)思路:要么縮短現(xiàn)有客戶端發(fā)版周期;要么通過云端下發(fā)的方式實(shí)現(xiàn)樣式的動(dòng)態(tài)更新。知乎 App 在發(fā)版周期上已經(jīng)做了大量嘗試,但由于 App 端版本發(fā)布方式的天然限制,在某些業(yè)務(wù)場景下無法很好的滿足要求;
廣告樣式實(shí)驗(yàn)粒度粗放,實(shí)驗(yàn)相關(guān)代碼管理復(fù)雜。這一痛點(diǎn)可以看作是痛點(diǎn)一的延續(xù)。AB 實(shí)驗(yàn)是產(chǎn)品效果提升的重要手段,為了支持實(shí)驗(yàn),要在 App 發(fā)版時(shí)預(yù)埋多套代碼,進(jìn)行復(fù)雜的實(shí)驗(yàn)開關(guān)及樣式代碼管理,同時(shí)還要考慮實(shí)驗(yàn)下線邏輯。如果實(shí)驗(yàn)粒度很細(xì),邏輯維護(hù)成本會(huì)指數(shù)增加;
廣告落地頁(Landing Page)的性能和轉(zhuǎn)化效果亟待提升。非原生廣告落地頁多為非知乎域的 H5 落地頁,App 端對其控制能力極其有限,在對 H5 類型落地頁本身進(jìn)行優(yōu)化的同時(shí),也希望探索新的方向。(第三方 H5 落地頁的打開速度和數(shù)據(jù)采集,可以采用「CDN 加速」、「XPath」等方案優(yōu)化,這兩個(gè)不是本文的重點(diǎn),在這里就不展開討論了。)
基于此商業(yè)移動(dòng)端團(tuán)隊(duì)開始著手搭建移動(dòng)端動(dòng)態(tài)化基礎(chǔ)能力,并一步步實(shí)現(xiàn)了 App 內(nèi)各個(gè)位置廣告卡片的動(dòng)態(tài)化開發(fā)和上線,我們給這個(gè)方案起了個(gè)比較「動(dòng)態(tài)」的名字叫 Morph。
下面從「技術(shù)選型」、「架構(gòu)設(shè)計(jì)」、「動(dòng)態(tài)化基礎(chǔ)能力建設(shè)」三個(gè)方面介紹一下此次的技術(shù)方案改造升級。
1. 進(jìn)行了哪些技術(shù)選型?
在方案設(shè)計(jì)之初,我們就確立了實(shí)現(xiàn)商業(yè)廣告全面動(dòng)態(tài)化的技術(shù)愿景,覆蓋從信息流廣告卡片到廣告主落地頁的所有功能場景。近些年,移動(dòng)端動(dòng)態(tài)化方案可謂百花齊放,如何選擇一個(gè)適合知乎商業(yè)化業(yè)務(wù)場景的動(dòng)態(tài)化方案成為我們第一個(gè)要解決的問題。
1.1 為什么是 DSL Native+?
從「支持動(dòng)態(tài)化的程度」、「與原生體驗(yàn)的差異」、「方案集成與功能開發(fā)成本的高低」三個(gè)維度出發(fā),將市面上的移動(dòng)端動(dòng)態(tài)化方案分成三個(gè)方向:

新的動(dòng)態(tài)化方案需要滿足「跨平臺(tái)」、「熱更新」兩個(gè)基本訴求,因此沒有考慮 Android 插件化這個(gè)大類下的方案。
Web 增強(qiáng)(Web+) : 主要基于 WebView 實(shí)現(xiàn),能夠進(jìn)行快速的迭代,在知乎已經(jīng)有了對應(yīng)的解決方案;GPL 和 DSL 可以看作「Native 增強(qiáng)(Native+)」方向的子方向,Native+ 方向基于 App 內(nèi)自帶的語言解析器,獨(dú)立于 WebView 實(shí)現(xiàn)動(dòng)態(tài)化邏輯的解析、布局和渲染。
基于 GPL 的 Native 增強(qiáng)(GPL) : 這個(gè)方向是移動(dòng)端動(dòng)態(tài)化的熱門方向,React Native、Flutter、NativeScript 等等,實(shí)現(xiàn)原理各不相同,共同的特點(diǎn)是利用了通用編程語言器,可以是系統(tǒng)提供的(如 Javascript),或者是集成到 App 的(如 Dart),再輔以某個(gè)布局系統(tǒng)和渲染方案,由于語言是圖靈完備的,可以提供完美的動(dòng)態(tài)化解決方案;
基于 DSL 的 Native 增強(qiáng)(DSL): GPL 方案雖然美好但始終需要在動(dòng)態(tài)化能力、渲染性能、Framework 體積、學(xué)習(xí)和開發(fā)成本等方面取舍,難以達(dá)到完美。于是產(chǎn)生了基于領(lǐng)域?qū)S谜Z言的解決方案,此類方案通過采用適合專業(yè)場景的的 DSL 解析云端下發(fā)的邏輯結(jié)構(gòu),舍棄一部分動(dòng)態(tài)化靈活性,換取其他方面的優(yōu)勢。
動(dòng)態(tài)化方案選擇要考量的因素很多,如渲染效率、動(dòng)態(tài)化靈活程度與用戶體驗(yàn)的取舍、方案實(shí)現(xiàn)成本等等。其中重點(diǎn)強(qiáng)調(diào)一下「方案實(shí)現(xiàn)成本」,這個(gè)因素應(yīng)該基于開發(fā)團(tuán)隊(duì)的現(xiàn)狀進(jìn)行考量,可以包括開發(fā)成本、學(xué)習(xí)成本和人才培養(yǎng)成本,開發(fā)成本很好理解,即該方案從調(diào)研到落地的工作成本;學(xué)習(xí)成本和人才培養(yǎng)成本往往是我們?nèi)菀缀雎缘模趫F(tuán)隊(duì)已經(jīng)掌握的技術(shù)棧,不同的方案需要的需要掌握的知識有所不同,學(xué)習(xí)成本也不同。而人才培養(yǎng)成本強(qiáng)調(diào)的是市場上是否有對應(yīng)的專業(yè)人才,不是很熟悉或者沒有親身實(shí)踐過這個(gè)技術(shù)方案的人多久能夠熟練掌握這項(xiàng)技能?比如 RN 為代表的基于 Javascript 的 GPL 方案通常需要前端開發(fā)技能和移動(dòng)端開發(fā)技能的配合使用,對人力培養(yǎng)成本比 Native+ 和 Web + 的成本要高。
討論了這么多,我們應(yīng)該選哪個(gè)方向呢?針對公司現(xiàn)有的業(yè)務(wù)場景、工程現(xiàn)狀,我們梳理出需要優(yōu)先考量的原則:
接近 Native 的高性能: 廣告動(dòng)態(tài)化的核心要求。相比于 Web 的靈活性,接近 Native 性能需求更強(qiáng)烈。滑動(dòng)流暢是信息流產(chǎn)品的基礎(chǔ)需求,而對于落地頁,打開速度和使用體驗(yàn)也是重中之重; 項(xiàng)目能夠快速啟動(dòng): 高速發(fā)展的業(yè)務(wù)訴求。項(xiàng)目需要能夠快速啟動(dòng),快速上線,本文所講的動(dòng)態(tài)化方案僅每端投入一人月就完成了第一個(gè)版本的上線; 低人力成本 : 在業(yè)務(wù)快速迭代的過程中,支撐業(yè)務(wù)開發(fā)的同時(shí),技術(shù)方案上的投入資源有限,前后端都很難有充足的人力投入,新方案應(yīng)該占用盡可能少的人力,并且對現(xiàn)有工程的改造降至最低; App 包體積影響小: 知乎 App 重視用戶體驗(yàn),新技術(shù)對 App 性能、包體積不能產(chǎn)生負(fù)面影響。包體積是影響應(yīng)用增長的重要因素,App Store 應(yīng)用如果超過 150M,則無法在 4G 環(huán)境下下載,將會(huì)極大的影響 App 的增長,所以新的方案需要盡可能少的影響包體積; 平滑遷移: 基于技術(shù)架構(gòu)現(xiàn)狀。商業(yè)廣告卡片是基于信息流的,不能因?yàn)閺V告動(dòng)態(tài)化而要求整個(gè)信息流重構(gòu),所以需要保證新的方案能夠和現(xiàn)有信息流方案完全兼容; 持續(xù)演進(jìn): 快速啟動(dòng)后的發(fā)展方案。整體框架需要支持高擴(kuò)展性,可以在上線后進(jìn)行持續(xù)的需求迭代。

「Web+」方向在性能和穩(wěn)定性上略差,而且使用場景有限,不適合在信息流這種流暢性要求高的場景中使用;「GPL」方向提供一套完全不同于 Native 的開發(fā)模式,可以在其基礎(chǔ)上實(shí)現(xiàn)動(dòng)態(tài)化功能,且能夠保證較好的性能,但是目前的解決方案接入成本和學(xué)習(xí)成本都比較高,并且與復(fù)雜業(yè)務(wù)場景結(jié)合時(shí)均有難以解決的問題,不適合在已有業(yè)務(wù)中接入。
綜上考慮,最終我們決定采用基于 Flexbox 的 DSL Native+ 動(dòng)態(tài)化方案即 Morph。Morph 方案在業(yè)務(wù)上具有如下優(yōu)勢:
和現(xiàn)有技術(shù)棧完美融合。知乎 App 信息流基于 ComponentKit 實(shí)現(xiàn)的,幾乎幾乎不會(huì)產(chǎn)生額外的學(xué)習(xí)成本; 快速上線,后期維護(hù)成本較低:整體方案的上手難度低,不需要學(xué)習(xí)新的技術(shù),能夠快速進(jìn)行功能迭代開發(fā); 對包體積影響小:整個(gè)方案只是增加了 Google-FlexboxLayout、Yoga 兩個(gè)輕量級開源布局框架,對體積影響可以忽略不計(jì); 這個(gè)方案還帶來了一個(gè)額外的好處:支持現(xiàn)有實(shí)驗(yàn)系統(tǒng),很好的滿足樣式實(shí)驗(yàn)、數(shù)據(jù)采集的需求。
1.2 為什么是 Flexbox?
動(dòng)態(tài)布局方案基于 Flexbox,帶來的好處是:
Flexbox 為盒狀模型提供最大的靈活性,是目前布局系統(tǒng)的首選; 跨平臺(tái)方案,雙端統(tǒng)一; 和知乎 App 現(xiàn)有技術(shù)棧匹配度高,開源環(huán)境中有優(yōu)秀開源庫的本地化。 iOS 端 Facebook 開源的 Yoga 已經(jīng)經(jīng)過 10+ release 迭代,有 1.1w+ 的 Star,(ComponentKit 底層也是依賴于 Yoga),在生產(chǎn)環(huán)境經(jīng)過長時(shí)間驗(yàn)證; Android 端的 Yoga 庫有一些難以解決的問題,最終選擇 Google 的 FlexboxLayout 框架來解析,該框架也非常成熟,在和 iOS Yoga 配合中僅需要很少的雙端適配。
1.3 為什么是 JSON?
布局描述語言上,由于各種描述語言的描述能力差異不大,在這種情況下,開發(fā)成本成為我們選型描述語言的重點(diǎn)。

開發(fā)成本考慮的是基于知乎 App 現(xiàn)有客戶端和后端的交互結(jié)構(gòu),目前前后端數(shù)據(jù)傳輸采用的是 JSON 格式,如果要換成其他格式的數(shù)據(jù),客戶端和后端都需要進(jìn)行額外的工作處理,最終我們選擇了 JSON 格式來描述布局樣式。
2. Morph 動(dòng)態(tài)化方案架構(gòu)如何設(shè)計(jì)?
我們將一個(gè)使用 Morph DSL 系統(tǒng),以 JSON 格式組織的描述一個(gè)視圖樹結(jié)構(gòu)的配置稱為樣式。實(shí)現(xiàn)動(dòng)態(tài)化的核心業(yè)務(wù)流程是實(shí)現(xiàn)布局文件云端下發(fā)和解析:編寫樣式文件 -> 上傳到服務(wù)端 -> 云端下發(fā)樣式到 App 端 -> App 端收到廣告數(shù)據(jù)后解析樣式文件展示廣告。
商業(yè)廣告業(yè)務(wù)數(shù)據(jù)存放在業(yè)務(wù)后端,和 App 端直接交互的是引擎后端,所以整個(gè)系統(tǒng)中涉及到三個(gè)端的調(diào)整。業(yè)務(wù)端相當(dāng)于商業(yè)廣告數(shù)據(jù)的生產(chǎn)者,業(yè)務(wù)端分業(yè)務(wù)前端和業(yè)務(wù)后端,業(yè)務(wù)前端提供面向廣告主投放廣告的平臺(tái)(投放平臺(tái)、建站工具),業(yè)務(wù)后端負(fù)責(zé)存儲(chǔ)和處理廣告相關(guān)的數(shù)據(jù)(廣告模版、廣告訂單、排期等)。廣告引擎則是從業(yè)務(wù)端獲取廣告數(shù)據(jù)進(jìn)行過濾和篩選,最后選取符合規(guī)則的廣告下發(fā)給 App 端,同時(shí)引擎端實(shí)驗(yàn)平臺(tái)負(fù)責(zé)對 App 端流量進(jìn)行分流。
結(jié)合現(xiàn)有業(yè)務(wù)場景,Morph 系統(tǒng)結(jié)構(gòu)設(shè)計(jì)如下圖所示:

廣告請求接口 :以信息流為例,用戶刷新信息流展現(xiàn)廣告卡片,引擎端從業(yè)務(wù)后端廣告數(shù)據(jù)庫中讀取廣告數(shù)據(jù)下發(fā)給 App 端,廣告數(shù)據(jù)中包含了對應(yīng)廣告卡片的樣式名稱。 樣式下發(fā)接口 : 樣式文件較多,因此與數(shù)據(jù)接口分開,通過獨(dú)立的樣式服務(wù)接口進(jìn)行下發(fā)。
隨著產(chǎn)品的演進(jìn),動(dòng)態(tài)化樣式數(shù)量持續(xù)增加,為保證研發(fā)效率的規(guī)避風(fēng)險(xiǎn),上線了「樣式管理平臺(tái)」,提供可視化界面進(jìn)行樣式的增刪改查等工作。
Morph 的業(yè)務(wù)數(shù)據(jù)流向如下圖所示:

針對產(chǎn)品提出樣式需求,由 App 端和業(yè)務(wù)端協(xié)商出新的廣告模版和樣式映射關(guān)系,客戶端通過樣式管理平臺(tái)提交新的樣式,存儲(chǔ)到云端樣式數(shù)據(jù)庫,業(yè)務(wù)端將模版和樣式名稱映射存入廣告數(shù)據(jù)庫;
App 啟動(dòng)時(shí),訪問樣式服務(wù)接口,樣式服務(wù)根據(jù)請求 Header 中的:App 平臺(tái)、App 版本,以及 App 端現(xiàn)有基礎(chǔ)樣式及版本信息集合,增量下發(fā)廣告樣式數(shù)據(jù)。App 接收到新的廣告樣式數(shù)據(jù)后,會(huì)依次進(jìn)行:Hash 校驗(yàn)、JSON 格式校驗(yàn)、實(shí)驗(yàn)信息的校驗(yàn),校驗(yàn)通過后將接收到新的樣式文件更新到本地樣式數(shù)據(jù)庫,同時(shí)進(jìn)行對舊版本樣式數(shù)據(jù)進(jìn)行清理刪除;
App 端信息流刷新,向引擎請求帶有樣式名稱及實(shí)驗(yàn)信息的廣告數(shù)據(jù),App 端根據(jù)數(shù)據(jù)中的樣式名稱選擇對應(yīng)的樣式文件生成布局,展示廣告卡片。
3. 動(dòng)態(tài)化能力建設(shè)中最核心的4個(gè)部分
這個(gè)部分和大家探討 Morph 動(dòng)態(tài)化基礎(chǔ)能力搭建的核心思路。

上圖展示了動(dòng)態(tài)化能力建設(shè)中的4個(gè)核心部分,下文將逐個(gè)闡述。
3.1 DSL 定義
基于 DSL Native+ 的動(dòng)態(tài)化方案設(shè)計(jì)首先要解決的一個(gè)問題是 DSL 的選擇,Morph DSL 包含三個(gè)核心設(shè)計(jì)思路:
基于前端 Flexbox 布局系統(tǒng)進(jìn)行視圖布局; 使用 JSON 數(shù)據(jù)交互語言描述視圖結(jié)構(gòu); JSON 節(jié)點(diǎn)使用屬性「type」區(qū)分視圖控件類型。
下圖是 Morph DSL 屬性集中的一個(gè)子集:

屬性集包含六種屬性:布局屬性,視圖屬性,數(shù)據(jù)屬性,交互屬性,條件屬性,容器屬性。
1. 布局屬性
決定視圖樹結(jié)構(gòu),規(guī)劃視圖布局位置的相關(guān)屬性,如上圖中的FlexStyle,F(xiàn)lexStyle 中各屬性命名及含義,與 Flexbox 定義一一對應(yīng)。得益于 Flexbox 布局系統(tǒng)簡潔、強(qiáng)大的彈性布局能力,Morph DSL 幾乎可以實(shí)現(xiàn)任何形式的布局;
2. 視圖屬性
與視圖 UI 樣式相關(guān)的屬性,如背景色、透明度、圓角等,如 ViewStyle。基于 Flexbox 帶來的另一個(gè)好處是解決了屏幕適配的問題,而在尺寸換算問題上,我們規(guī)定:Morph DSL 布局中,使用 2 倍圖下的尺寸,單位為 px,Android 和 iOS 端解析具體的數(shù)值時(shí),將分別換算成本地的、與設(shè)備無關(guān)的 dp 或 pt 來使用;
3. 數(shù)據(jù)屬性
表達(dá)節(jié)點(diǎn)與數(shù)據(jù)綁定關(guān)系的屬性,在不同的視圖控件中有所區(qū)別,具體細(xì)節(jié)將在下文「數(shù)據(jù)綁定」中闡述;
4. 交互屬性
設(shè)置視圖節(jié)點(diǎn)的點(diǎn)擊等事件的屬性,主要由 ViewAction 指定。ViewAction 描述一個(gè)視圖節(jié)點(diǎn)的交互類型,結(jié)構(gòu)如下:

action 指明該交互要完成的動(dòng)作,extra 攜帶交互需要的信息和數(shù)據(jù)。上圖展示的布局片段,描述了一個(gè) CLICK 事件,觸發(fā)后,將從 extra 中取出 URL 信息,并進(jìn)行跳轉(zhuǎn)。目前 Morph 已定義的通用 action 類型及規(guī)則舉例:

5. 條件屬性
Morph 定義了一套 condition 機(jī)制,支持在樣式文件中根據(jù)業(yè)務(wù)邏輯做條件判斷,如互動(dòng)數(shù)據(jù)的展示等。condition 定義如下:

目前 condition 支持主要的邏輯運(yùn)算:or(邏輯或)、and(邏輯與)、not(邏輯非)、notEmpty(非空)、empty(空)、valueOf(取 bool 值)。
以下是一個(gè)使用了 condition 的一個(gè)文本節(jié)點(diǎn)示例,其中,visibility 決定了該控件最終是否會(huì)顯示,其類型就是一個(gè) condition 類型:

6. 容器屬性:在 Morph DSL 中規(guī)定,樣式的根節(jié)點(diǎn)必須是一個(gè) container,以下是一個(gè)根節(jié)點(diǎn)示例:

container 對應(yīng)于 App 端支持了 Flexbox 布局系統(tǒng)的容器控件。container 使用屬性 children 來描述容器內(nèi)嵌套的子視圖,children 是一個(gè)數(shù)組,可以包含任意多個(gè)控件節(jié)點(diǎn)。通過這種方式,Morph DSL 可以描述任意結(jié)構(gòu)的視圖樹。
下例中,使用了 height 屬性來指定控件的高度。Morph 設(shè)計(jì)之初,我們將控件的寬高定義為一個(gè)復(fù)合屬性:

在后期技術(shù)演進(jìn)過程中,我們使用了更簡潔的字符串類型的 layoutHeight、layoutWidth 來描述高和寬:

3.2 動(dòng)態(tài)視圖管理
我們把前文提過的樣式文件從開發(fā)到寫入 App 端本地樣式數(shù)據(jù)庫的完整流程畫成時(shí)序圖:

基于自定義 DSL 進(jìn)行編寫樣式; 樣式文件經(jīng)由樣式管理平臺(tái)存入云端樣式數(shù)據(jù)庫: App 端請求樣式服務(wù)接口; 樣式服務(wù)接口收到請求后,根據(jù)一定的策略進(jìn)行樣式下發(fā); App 端將接收到的樣式進(jìn)行校驗(yàn)后,寫入本地樣式數(shù)據(jù)庫,以備后續(xù)使用。
云端/本地樣式數(shù)據(jù)庫的設(shè)計(jì)如下:

樣式下發(fā)接口的參數(shù)設(shè)計(jì)如下:

值得注意的是:
App 端請求樣式下發(fā)時(shí),會(huì)帶上當(dāng)前 App 端已支持的樣式信息,樣式服務(wù)據(jù)此判斷進(jìn)行增量而非全量的樣式下發(fā),節(jié)省傳輸成本; 某樣式單個(gè)平臺(tái)版本號為空時(shí),不向該平臺(tái)下發(fā)該樣式; 某樣式支持的最低 App 端版本大于當(dāng)前 App 端版本時(shí),不下發(fā)該樣式。
3.3 構(gòu)建視圖
客戶端為每一個(gè) UI 組件創(chuàng)建專門的解析器。對于每一種樣式,使用單獨(dú)的樣式名稱 style 來標(biāo)識,樣式文件中引用的每種組件會(huì)一一對應(yīng)到字段 type 表明該樣式所使用的控件類型。因此當(dāng) App 端獲取到樣式文件后,通過樣式中每個(gè)元素的 type,由解析器決定使用哪種原生控件進(jìn)行承載。
解析器主要承擔(dān) 2 項(xiàng)職責(zé):
根據(jù) JSON 節(jié)點(diǎn)的屬性配置創(chuàng)建一個(gè)本地對應(yīng)的控件,解析控件綁定的具體數(shù)據(jù)并設(shè)置到控件上; 通過每個(gè)節(jié)點(diǎn)中的 type 字段,將每一個(gè)節(jié)點(diǎn)交由對應(yīng)的組件解析器去解析,解析的結(jié)果是輸出一個(gè)對應(yīng)的 UI 控件。
由于所有節(jié)點(diǎn)已按照樹形結(jié)構(gòu)存儲(chǔ),最終輸出所有 UI 控件時(shí),按照樹形結(jié)構(gòu)進(jìn)行組織,就可以生成最終的目標(biāo)視圖卡片。

以 Android 端為例,簡要闡述視圖解析器實(shí)現(xiàn)的技術(shù)要點(diǎn):
每一類型的視圖控件,都在 App 端有唯一對應(yīng)的 ViewModel(樣式文件的布局屬性 Model 化的產(chǎn)物)和 ViewParser(視圖解析器,利用 ViewModel 創(chuàng)建本地View并設(shè)置屬性) ViewModel 及 ViewParser 的注冊,由自定義注解 @ViewModel("type") 及 @ViewParser("type") 完成,在編譯期將通過插件自動(dòng)收集并插入注冊代碼; 構(gòu)建視圖時(shí),遍歷 model 樹,通過 model 的 type 字段,獲取已注冊的 ViewParser,反射構(gòu)造一個(gè)解析器實(shí)例,傳入 ViewModel,完成單個(gè)視圖控件的創(chuàng)建; 由于使用遞歸遍歷,某個(gè)節(jié)點(diǎn)的視圖控件構(gòu)建完成后,其父節(jié)點(diǎn)必定已構(gòu)建完成,因此直接將當(dāng)前視圖控件,加入父控件即可。
3.4 綁定數(shù)據(jù)
樣式文件的主要功能是設(shè)置視圖顯示約束,其中包含了與視圖顯示屬性相關(guān)的設(shè)置,以及對應(yīng)顯示的內(nèi)容數(shù)據(jù)字段。因此在樣式解析后,還需要進(jìn)行顯示數(shù)據(jù)的綁定。下圖是一個(gè)圖片控件節(jié)點(diǎn)的布局示例。

「url」為該控件的一個(gè)數(shù)據(jù)屬性,表示該圖片控件要顯示的圖片鏈接的地址,其值 ?指向了一個(gè) JSON 數(shù)據(jù)體中的一個(gè)具體節(jié)點(diǎn):

我們規(guī)定指向數(shù)據(jù)體節(jié)點(diǎn)的引用使用 ?作為結(jié)束標(biāo)識,數(shù)組直接使用數(shù)字下標(biāo)指定其具體位置。如上面的引用 ?中,ads 指數(shù)據(jù)體中的一個(gè)數(shù)組節(jié)點(diǎn),而0標(biāo)識取數(shù)組的第一個(gè)元素,creative 表示一個(gè) JSONObject 節(jié)點(diǎn),image 表示 creative 節(jié)點(diǎn)下的一個(gè)具體字段值。
以上文的圖片控件為例,數(shù)據(jù)綁定過程如下:
調(diào)用數(shù)據(jù)綁定接口,將數(shù)據(jù)體傳入接口; 遍歷已構(gòu)建好的視圖樹,遍歷每一個(gè)節(jié)點(diǎn)時(shí),將從該節(jié)點(diǎn)控件中取出對應(yīng)的 ViewModel 對象(視圖構(gòu)建時(shí),會(huì)作為 tag 與控件綁定),再獲取對應(yīng)的控件解析器;3.調(diào)用解析器,解析器中將使用數(shù)據(jù)引用和數(shù)據(jù)體,共同完成解析,拿到最終的數(shù)據(jù)(如圖片鏈接 url); 解析器按照控件自身具體的規(guī)則,完成數(shù)據(jù)設(shè)置。
4. 寫在最后
Morph 移動(dòng)端動(dòng)態(tài)化技術(shù)方案自上線以來,經(jīng)過多輪迭代,支持了大量控件并覆蓋了多數(shù)業(yè)務(wù)場景。
雖然項(xiàng)目尚未開源,且文章中的一些技術(shù)可能時(shí)至今日已經(jīng)有了新的方案(文章完成于?2?年前),但是文中分享的關(guān)于技術(shù)選型的考量以及具體實(shí)現(xiàn)思路,對大家仍然具有參考意義。
推薦閱讀
? 耗時(shí)2年,Android進(jìn)階三部曲第三部《Android進(jìn)階指北》出版!
為了防止失聯(lián),歡迎關(guān)注我的小號
??微信改了推送機(jī)制,真愛請星標(biāo)本公號??
