如何动态的生成某种类型的集合呢_知乎画报」的移动端动态化工程实践
本文基于移動端動態化方案在知乎原生推廣落地頁「知乎畫報」上的實踐經驗,對該方案技術升級過程中的思考以及技術關鍵細節做了詳盡的解讀。
商業化是互聯網公司發展的重要階段,App 端的商業廣告業務對移動端動態化能力的需求很強烈,一方面需要靈活的樣式調整和 AB 實驗提升廣告 CTR;另一方面提升功能的上線效率,新的產品功能可以快速的觸達用戶。基于此商業移動端團隊從 2018 年 Q2 開始搭建動態化基礎能力,逐步實現了 App 內各個位置廣告卡片的動態化開發和上線(為了便于溝通和描述,我們給這個方案起了個比較「動態」的名字:Morph)。
廣告推廣落地頁「Landing Page」是商業化廣告的重要組成部分。做商業化廣告的同學可能都處理過落地頁優化的問題,「落地頁打開速度」、「落地頁到達率」、「落地頁轉化效果」幾個指標的提升始終是 App 端落地頁體驗優化的重要挑戰。
為解決上述業務痛點,提供更好的客戶體驗, 2018 年末商業產品提出在知乎 App 中支持原生推廣落地頁——「知乎畫報」的業務需求,落地頁內可使用圖片、文字、視頻等富媒體形式承載原生內容。此時動態化基礎能力(Morph)已經基本成熟,對于當時提出的業務需要,我們自然而然的想到了復用 Morph 方案,同時在其基礎能力上進行技術升級,將原有的信息流廣告動態化擴展為全面的廣告動態化。
從業務角度分析,不同于信息流廣告卡片入口,落地頁作為廣告主體內容的承載容器,在呈現方式上更加開放,內容也更豐富;同時,用戶對于廣告內容的瀏覽與操作,廣告主對于廣告效果的獲知、轉化信息的采集,也都需要依托廣告內容落地頁來實現。在此過程中,相比于目前第三方 H5 落地頁,采用原生落地頁在工程能力上需要有如下提升:
1. 「加載速度更快,到達率接近 100%」。「到達率」在這里特指用戶點擊廣告卡片到落地頁完全加載的比例,是衡量落地頁性能的重要指標。H5 落地頁加載速度緩慢,「加載菊花」常常被人詬病,在網絡不穩定或服務器異常的情況下有很大幾率會導致頁面加載緩慢甚至加載失敗。除了嚴重影響用戶體驗之外,也極大的降低了廣告轉化率,對廣告資源造成浪費;
2. 「支持預加載,實現秒開」。在第三方 H5 落地頁,很難進行可靠的內容預加載。第三方服務器性能不一,性能較差的服務器很難承載大規模頁面訪問帶來的服務壓力;
3. 「轉化信息的全面采集,用戶體驗升級」。對于很多廣告主來說,由于沒有自建站平臺,只能使用第三方建站平臺生成網頁,顯示效果參差不齊,內容質量難以把控,甚至相關數據的統計與分析也不得不依賴第三方平臺,無論是數據準確程度還是學習成本都難以令人滿意。對此,新方案在提供全面的轉化信息同時,需要提供更低的學習成本與更好的用戶使用體驗;
4. 「支持樣式實驗,轉化效果可提升」。和廣告卡片提升 CTR 一樣,通過進行落地頁樣式實驗可以有效并持續地提升轉化效果。
因此,對于原生推廣落地頁的動態化能力升級,成為了整個動態化進程中不可或缺的一環。(同樣,為了便于溝通和描述,我們給這個方案起了個名字:Canvas)。
下面從「技術升級選型」、「知乎畫報業務流程」、「技術實現細節」三個方面介紹一下此次技術方案改造升級。
掃描下方廣告預覽小程序碼,可以交互式查看本文介紹的「知乎畫報」原生推廣落地頁的效果。
有哪些技術選型上的升級,如何考量的?
移動端已實現的動態化基礎能力(Morph)主要面向信息流廣告卡片的展現,基于 Flexbox 布局編寫的樣式文件集合保存在云端樣式數據庫,通過獨立的樣式服務接口下發。信息流廣告數據下發時會根據 App 本地已支持的樣式文件子集匹配下發可展示的廣告數據,App 端解析樣式文件,綁定數據最終呈現廣告卡片。
Morph 方案實現中進行了一系列的技術考量,在 Canvas 中針對「布局解析系統」和「樣式文件」進行了改造升級,其他部分就不在本文中展開討論了。
升級布局解析系統,匹配落地頁的復雜場景
Morph 方案中從信息流場景出發,iOS 布局解析和視圖渲染均基于 ComponentKit 實現,Android 布局解析則基于 google/flexbox-layout 實現,視圖渲染基于系統原生控件。Canvas 落地頁場景中,頁面樣式會更加復雜多變,同時需要容納更豐富的、定制程度高的控件。因此在視圖渲染上,直接使用系統原生方案是比較合適的選擇。
Android 端布局解析框架 google/flexbox-layout 可以兼容系統原生控件,已滿足升級要求。iOS 端既然選擇了使用原生 UIKit 框架,那么我們在選擇 Flexbox 解析方案時,可以只關注于 Flexbox 布局約束的實現,而無需要求第三方庫對基礎屬性,例如背景顏色的支持。 尤其是想要在較短時間內實現對整體頁面約束布局設置,學習成本不宜過高。同時,包體積是知乎 App 特別關注的基礎體驗,也需要考慮在內。
綜合考慮以上幾點,我們選擇了更輕量級、業務吻合度更高的 YogaKit。
樣式文件拆分,支持云端自定義視圖布局
樣式文件是云端控制 App 端樣式展現的關鍵紐帶。在 Morph 中,廣告卡片的樣式文件是由工程師預先編寫,由使用方(廣告主)選擇后下發到 App 端,換句話說,Morph 的布局能力在廣告卡片樣式文件生成的時候就確定了。Canvas 在 Morph 的基礎上將樣式的布局能力進一步暴露給使用方,支持對落地頁布局的個性化定制。
因此,與 Morph 中所有樣式都寫在同一個樣式文件中不同,Canvas 樣式文件分為:基礎組件樣式文件(基礎樣式)和布局樣式文件(殼樣式)。
- 基礎樣式
與 Morph 不同之處在于:基礎樣式不是最終的樣式,只是落地頁視圖中基礎組件的樣式,需要結合殼樣式共同生效;沿用 Morph 中的樣式服務接口單獨下發;每個基礎樣式都有默認樣式屬性值,會被殼樣式中的屬性覆蓋。
- 殼樣式
落地頁的最終樣式結構,像「殼」一樣將基礎樣式包裹起來;決定落地頁的視圖布局,隨廣告數據下發;支持對基礎樣式屬性進行覆蓋更改。
「知乎畫報」的業務流程設計
從用戶視角上看,「知乎畫報」的總體業務流程如下圖:
總體流程下可以分為兩個子流程:一、基礎樣式下發流程
二、落地頁樣式展現流程
同時,Canvas 與廣告實驗系統深度結合,支持對落地頁樣式進行細粒度的 AB 測試實驗和數據收集,用于 CTR、CVR 提升實驗。
下面我們來看一下為了實現上述業務流程,各參與角色做了哪些事情。
廣告主需要做什么
廣告業務前端為廣告主提供專門的落地頁建站工具平臺。廣告主在該平臺將已支持的云端基礎樣式數據庫中的基礎組件,按照自己的需求進行簡單的選擇、拼接,組合成完整的落地頁樣式。然后上傳對應的廣告素材即可,操作過程非常簡單。具體介紹可訪問:這里。
后端做了什么
業務前端將制作好的頁面解析生成 App 端可理解的殼樣式數據,與廣告素材關聯后同步存儲到廣告業務數據庫。當 App 端請求廣告數據時,廣告引擎根據 App 基本信息進行匹配,將滿足條件的落地頁殼樣式數據封裝在廣告數據中一并下發。
App 端做了什么
App 在安裝時會自帶一個預埋了最新基礎組件樣式的本地基礎樣式數據庫,如果云端更新了基礎組件樣式,可以通過樣式服務進行更新。當用戶刷新頁面并請求到新的廣告數據后,取出隨廣告同時下發的殼樣式數據,與之前存儲在本地的基礎樣式合并,生成落地頁視圖。(樣式合并的細節將在后文詳述。)
特別的,App 在廣告卡片展現的同時,會提前預渲染廣告落地頁視圖,在點擊廣告之后,落地頁打開過程中不需要再次等待頁面生成,實現秒開顯示,提升落地頁到達率。
相比于 H5 落地頁對于 App 的黑盒,用戶在落地頁內的操作均屬于對原生控件操作,瀏覽、填寫信息、點擊等行為均可收集用于支持 CVR 提升、oCPC 等工作。
有哪些值得關注的技術細節?
通過上文的敘述,我們已經對 Canvas 的技術基礎和「知乎畫報」的業務有了比較完整的認識。在 Canvas 動態化方案技術升級的過程中,移動端研發解決了很多工程設計和實現的問題,積累了寶貴的經驗,接下來就把我們認為值得關注的技術細節做一個分享。
基礎樣式文件與基礎組件生成
Canvas 廣告落地頁中,目前支持的所有組件類型包括:文本、圖片、視頻、勾選框、文字選擇控件、圖片選擇控件、表單,其中表單內子組件包括文字輸入框、下拉選擇框。此外,還有兩個容器組件:場景和分頁。
對于每一種基礎組件樣式,使用單獨的樣式名稱 canvas_style 來標識,每一種樣式名稱會一一對應到字段 type 表明該樣式所使用的控件類型。因此當 App 端獲取到殼樣式文件后,通過殼樣式中每個元素的樣式名稱,在數據庫中查找到對應的控件類型,從而決定使用哪種基礎樣式及原生控件進行承載。以 iOS 為例,控件類型映射示意如下:
基礎樣式文件的數據存儲分為兩個部分:
- 云端基礎樣式數據庫
基礎樣式以 JSON 的形式,與其對應的 Canvas 版本、客戶端版本一并存儲到云端數據庫中。
- 本地基礎樣式數據庫
App 啟動時,訪問樣式服務接口,樣式服務根據請求 Header 中的: App 平臺、App 版本、 Canvas 版本,以及 App 端現有基礎樣式及版本信息集合,增量下發基礎樣式數據;
App 接收到新的基礎樣式數據后,會依次進行:Hash 校驗、JSON 格式校驗、實驗信息的校驗,校驗通過后將接收到新的基礎樣式更新到本地數據庫,同時進行對舊版本樣式數據進行清理刪除。
此外,為減小數據傳輸量,App 新版本發布時,會將已支持的基礎樣式數據預埋在數據庫中。
最終生成的「知乎畫報」頁面與基礎樣式組件的對應關系如下圖所示:
殼樣式文件與樣式合并
在上文的介紹中我們已經接觸過基礎樣式和殼樣式的概念。布局樣式(即殼樣式)隨廣告數據一起下發,和基礎樣式一起決定了落地頁展現的最終樣式。
對于一個控件來說,相關屬性是通過樣式模型進行配置的。本地基礎樣式數據庫存儲了每個組件最新的默認樣式,在客戶端收到隨廣告數據一起下發的殼樣式數據后,會將服務器傳回的殼樣式數據與本地基礎樣式數據進行對比合并,最終形成包含了各組件相關屬性配置、視圖層級設置,以及 layout 約束設置的樣式模型。
樣式合并的過程示意如下:
在樣式的合并過程中,遵循以下幾點規則:
- 如果某個屬性值,殼樣式中沒有設置而基礎樣式中有,則以基礎樣式中的值為準;
- 如果某個屬性值,殼樣式中和基礎樣式中都有設置,則以殼樣式中的值為準;
- 如果樣式中某個組件包含子組件,則依次遍歷其中的每個子組件,對其執行該合并過程。
下圖中我們將一個本地基礎樣式屬性和殼樣式屬性合并成了最終樣式模型:
整頁布局與翻頁處理
一個廣告落地頁的完整顯示內容通常會包含圖片、文字、表單等多種元素,很多場景下廣告落地頁全部顯示內容會超過一屏。
上文中我們介紹過容器組件場景和分頁。在 Canvas 落地頁的視圖層級中,最底層為一個總視圖容器 canvas_scene,用來承載整個落地頁內容, canvas_scene 中包含了至少一個 canvas_page,canvas_page 中包含一個 scrollView 視圖,支持頁面內組件的滾動展示。
canvas_page 的 Flex 布局規則如下:
Page { display: flex; flex-direction: column; justify-content: flex-start; align-items: center; }
canvas_page 支持兩種整頁布局方式,使用者可以根據具體訴求選擇其一進行頁面內容展示:
- 滑動布局方式
如果廣告主選擇只使用一個 canvas_page 來顯示內容,canvas_page 生成過程中,會循環遍歷其中包含的所有子組件,對其進行數據綁定及約束設置,然后根據所有子組件的約束設置來適配自身包含的滑動視圖的內容尺寸大小。以 iOS 為例,在 canvas_page 中包含了一個 UIScrollView,因此在計算出所有子組件約束后,將 scrollView 的 contentSize 設置成完整包括了所有子組件的容器的 size。這樣,在這個頁面上就可以通過上下滑動來瀏覽完整內容。
- 翻頁布局方式
既然是「翻頁」,也就代表底層容器中會包含多個 canvas_page,而每一個 canvas_page 的大小,可以等于或大于屏幕尺寸。(為了適配設備屏幕高度我們規定:頁面內容大于一屏的情況下,分頁組件根據內容高度自適應;當頁面內容不足一屏時,按照一屏的高度展示,底部留白)。
在翻頁方式布局中,當用戶在滑動過程中即將切換到下一頁時,會有一個 bounce 彈性動畫來提示切換到下一頁。
- 如何選擇兩種布局方式
通常對于廣告主建站過程來說,如果所有落地頁內容為一個整體,則建議使用滑動布局的方式將所有內容放置在同一頁中;而如果整個內容分為數個部分,則可以分別將每個部分設置為一頁,然后進行多頁組合。
Canvas 的版本控制
從業務發展的角度來說,落地頁中展示的組件不可能一次性全部支持,而是在業務迭代中對 Canvas 的基礎樣式集合進行動態更新和擴展的。因此,不同時期新增的組件,會對應不同的版本支持。一條廣告數據也必須通過版本控制以確保下發到滿足落地頁展示條件的用戶端。
Canvas 的版本規則可以分為三個部分來解釋:
- 基礎組件支持的 Canvas 版本
每個 Canvas 基礎組件都會對應一個 Canvas 版本號(canvas_version),這個版本號寫在基礎樣式云端數據庫中,每當一個 Canvas 基礎組件發生不向下兼容的修改時,提升對應的 canvas_version 。因此,一個基礎組件在基礎樣式云端數據庫可能有多條記錄,對應多個 canvas_version。
- Canvas 版本兼容的最低 App 版本
每個 canvas_version 版本都會對應一個最低 App 版本,樣式服務接口調用時,對于每個基礎樣式組件,會選擇?持當前客戶端版本的最新一條基礎樣式數據記錄下發到 App。
- 當前 App 支持的 Canvas 版本
用戶手機上的 App 以當前預埋或已下發的所有基礎樣式中,最高的 canvas_version 最為當前 App 支持的 canvas_version。調用廣告數據下發接口時,廣告引擎端選取落地頁殼樣式中調用的基礎組件 canvas_version 小于等于 App 支持的 canvas_version 的廣告數據下發。
其他技術細節
- 數據綁定
基礎樣式的主要功能是設置視圖顯示約束,是與廣告內容數據完全獨立的存在,其中只包含了與視圖顯示屬性相關的設置,而沒有添加真正要顯示的內容。因此在殼樣式中,還需要進行顯示數據的綁定。
數據綁定部分的邏輯沿用了 Morph 的數據綁定方案:
- 事件處理與控件聯動
Morph 方案中的事件處理都包含在 action 字段中,如果有需要額外添加的參數,可以添加在 extra 參數上。
Canvas 落地頁在此處沿用了 Morph 的結構,但是在不同組件之間的事件聯動上做了升級。以表單組件為例:?部分表單信息支持用戶授權后由系統自動填充,當用戶勾選授權框時,需要在表單輸入框中自動填充相關信息。實現過程要保證兩個關鍵點:
1. 確保在頁面中能夠獲取到聯動的組件。一種可行的辦法是設置局部唯一標識,添加 stringTag 標簽的方式對控件進行「標記」,接收到的殼樣式數據中指定需要聯動的控件標識;
2. 確保事件能夠進行正確完整的傳遞。當某一控件接收到點擊事件時,將這個點擊事件消息通過廣播出來,監聽了這一消息的組件會對事件進行響應。
- 轉場動畫的實現
對于一個廣告落地頁來說,「顯示什么」固然是很重要的,而除此之外,「怎么顯示」也是一門學問。尤其對于原生廣告落地頁,要想做到「提升用戶體驗」,酷炫且恰當的轉場動畫不失為一個討巧的選擇。Canvas 原生落地頁從廣告卡片為起點,最終展示為全屏頁面。因此,我們選擇了一種「卡片展開式」的轉場動畫作為頁面過渡方式。
具體的實現方式是:在用戶點擊廣告卡片時,獲取到當前卡片相對于整個手機屏幕的位置坐標,并將該位置作為廣告落地頁的初始坐標和初始大小,落地頁的最終位置坐標以及尺寸則等于手機屏幕。因此能夠執行一個從起始位置到最終位置的展開動畫。與此同時,在廣告卡片當前位置覆蓋一個與卡片樣式完全一樣的截圖并執行縮小動畫,從初始卡片位置向中心縮小直到消失,就得到了視頻中的演示效果。
因為 Canvas 落地頁是預渲染的,在執行動畫過程中,不需要額外考慮頁面的生成及生命周期。落地頁的展開動畫與卡片的縮小消失動畫同時執行,也讓原本枯燥的廣告頁面打開過程顯得耳目一新。
寫在最后Canvas 動態化能力升級 是一次很有意義的移動端動態化方案實踐,通過這次升級不僅將基于頁面組件的動態化能力打造的更加完善,覆蓋更全面的應用場景;也很好的滿足了「知乎畫報」的業務要求,用戶體驗大幅提升。
落地頁預渲染后實現秒開,到達率基本達到 100%,同時,相比于 H5 落地頁對于 App 的黑盒,用戶在落地頁內的操作均屬于對原生控件操作,轉化打點追蹤更加準確,能夠細化到廣告創意粒度,更好的支撐 CVR 提升、oCPC 等工作的開展。
我們的動態化方案開發人員不多,現有系統功能和技術方案一定還有很多不足之處,歡迎對移動端動態化方案感興趣的同學在文章評論區一起交流分享知識、經驗、見解。后續我們還會對動態化方案的基礎能力建設部分進行更多的介紹,最新的動態化方案相關技術文章會在知乎技術專欄和預覽小程序的「資訊」頁面持續更新。
本文作者: @于鵬洋 @紙西 @二二三三 @于天航
總結
以上是生活随笔為你收集整理的如何动态的生成某种类型的集合呢_知乎画报」的移动端动态化工程实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深交所向思美传媒下发关注函,要求说明与抖
- 下一篇: Tumblr 宣布取消 Post Plu