知乎畫報移動端動態化工程實踐

商業化是互聯網公司發展的重要階段,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 中的樣式服務接口單獨下發;每個基礎樣式都有默認樣式屬性值,會被殼樣式中的屬性覆蓋。

  • 殼樣式

落地頁的最終樣式結構,像「殼」一樣將基礎樣式包裹起來;決定落地頁的視圖佈局,隨廣告數據下發;支持對基礎樣式屬性進行覆蓋更改。

「知乎畫報」的業務流程設計

從用戶視角上看,「知乎畫報」的總體業務流程如下圖:

總體流程下可以分爲兩個子流程:

一、基礎樣式下發流程

  1. 基礎樣式存儲在雲端基礎樣式數據庫
  2. App 啓動時,從樣式服務獲取當前版本的最新基礎樣式集合;
  3. 數據校驗通過後,存入本地基礎樣式數據庫

二、落地頁樣式展現流程

  1. 以信息流爲例,用戶刷新信息流展現廣告卡片,廣告數據中包含了對應的落地頁殼樣式信息;
  2. 與基礎樣式數據合併解析,綁定數據,生成最終的樣式信息;
  3. 根據樣式信息,在用戶瀏覽信息流時預先渲染原生落地頁;
  4. 點擊廣告卡片,展現原生落地頁。

同時,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 支持兩種整頁佈局方式,使用者可以根據具體訴求選擇其一進行頁面內容展示:

  1. 滑動佈局:使用一個不分割的完整長頁面,如下圖 page one 所示;
  2. 翻頁佈局:使用多屏頁面進行翻頁顯示,如下圖 page two 所示。

  • 滑動佈局方式

如果廣告主選擇只使用一個 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 的數據綁定方案:

  1. 數據體節點的佔位符使用 ** 作爲開始標識,**?> 作爲結束標識,數組直接使用數字下標指定其具體位置;例如:
  2. App 在數據綁定時,直接與廣告 JSON 數據中的字段映射,無需經過 Native Model 解析,實現 數據 Model 動態化。因此上述數據佔位符對應的 JSON 數據子結構爲:ads[0].creatives.asset.brand_name
  • 事件處理與控件聯動

Morph 方案中的事件處理都包含在 action 字段中,如果有需要額外添加的參數,可以添加在 extra 參數上。

Canvas 落地頁在此處沿用了 Morph 的結構,但是在不同組件之間的事件聯動上做了升級。以表單組件爲例:
部分表單信息支持用戶授權後由系統自動填充,當用戶勾選授權框時,需要在表單輸入框中自動填充相關信息。實現過程要保證兩個關鍵點:

  1. 確保在頁面中能夠獲取到聯動的組件。一種可行的辦法是設置局部唯一標識,添加 stringTag 標籤的方式對控件進行「標記」,接收到的殼樣式數據中指定需要聯動的控件標識;

  2. 確保事件能夠進行正確完整的傳遞。當某一控件接收到點擊事件時,將這個點擊事件消息通過廣播出來,監聽了這一消息的組件會對事件進行響應。

  • 轉場動畫的實現

對於一個廣告落地頁來說,「顯示什麼」固然是很重要的,而除此之外,「怎麼顯示」也是一門學問。尤其對於原生廣告落地頁,要想做到「提升用戶體驗」,酷炫且恰當的轉場動畫不失爲一個討巧的選擇。Canvas 原生落地頁從廣告卡片爲起點,最終展示爲全屏頁面。因此,我們選擇了一種「卡片展開式」的轉場動畫作爲頁面過渡方式。

具體的實現方式是:在用戶點擊廣告卡片時,獲取到當前卡片相對於整個手機屏幕的位置座標,並將該位置作爲廣告落地頁的初始座標和初始大小,落地頁的最終位置座標以及尺寸則等於手機屏幕。因此能夠執行一個從起始位置到最終位置的展開動畫。與此同時,在廣告卡片當前位置覆蓋一個與卡片樣式完全一樣的截圖並執行縮小動畫,從初始卡片位置向中心縮小直到消失,就得到了視頻中的演示效果。

因爲 Canvas 落地頁是預渲染的,在執行動畫過程中,不需要額外考慮頁面的生成及生命週期。落地頁的展開動畫與卡片的縮小消失動畫同時執行,也讓原本枯燥的廣告頁面打開過程顯得耳目一新。

寫在最後

Canvas 動態化能力升級 是一次很有意義的移動端動態化方案實踐,通過這次升級不僅將基於頁面組件的動態化能力打造的更加完善,覆蓋更全面的應用場景;也很好的滿足了「知乎畫報」的業務要求,用戶體驗大幅提升。

落地頁預渲染後實現秒開,到達率基本達到 100%,同時,相比於 H5 落地頁對於 App 的黑盒,用戶在落地頁內的操作均屬於對原生控件操作,轉化打點追蹤更加準確,能夠細化到廣告創意粒度,更好的支撐 CVR 提升、oCPC 等工作的開展。

我們的動態化方案開發人員不多,現有系統功能和技術方案一定還有很多不足之處,歡迎對移動端動態化方案感興趣的同學在文章評論區一起交流分享知識、經驗、見解。後續我們還會對動態化方案的基礎能力建設部分進行更多的介紹,最新的動態化方案相關技術文章會在知乎技術專欄和預覽小程序的「資訊」頁面持續更新。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章