扣丁書屋

百度 Android 直播秒開體驗優化

導讀 introduction

網絡直播功能作為一項互聯網基本能力已經越來越重要,手機中的直播功能也越來越完善,電商直播、新聞直播、娛樂直播等多種直播類型為用戶提供了豐富的直播內容。隨著直播的普及,為用戶提供極速、流暢的直播觀看體驗也越來越重要。

01 背景

百度 APP 作為百度的航母級應用為用戶提供了完善的移動端服務,直播也作為其中一個必要功能為用戶提供內容。隨著直播間架構、業務能力逐漸成熟,直播間播放指標優化也越來越重要。用戶點擊直播資源時,可以快速的看到直播畫面是其中一個核心體驗,起播速度也就成了直播間優化中的一個關鍵指標。

02 現狀

由于包體積等原因,百度 APP 的 Android 版中直播功能使用插件方式接入,在用戶真正使用直播功能時才會將直播模塊加載。為解決用戶點擊直播功能時需要等待插件下載、安裝、加載等階段及兼容插件下載失敗的情況,直播團隊將播放、IM 等核心能力抽到了一個獨立的體積較小的一級插件并內置在百度 APP 中,直播間的掛件、禮物、關注、點贊等業務能力在另外一個體積較大的二級插件中。特殊的插件邏輯和復雜的業務場景使得 Android 版整體起播時長指標表現的不盡人意。

2022 年 Q1 直播間整體起播時長指標 80 分位在 3s 左右,其中二跳(直播間內上下滑)場景在 1s 左右,插件拆分上線后通過觀察起播數據發現隨著版本收斂,一跳進入直播間攜帶流地址(頁面啟動后會使用該地址預起播,與直播列表加載同步執行)場景起播時有明顯的增長,從發版本初期 1.5s 左右,隨版本收斂兩周內會逐步增長到 2.5s+。也就是線上在直播間外點擊直播資源進直播間時有很大一部分用戶在點擊后還需要等待 3s 甚至更長時間才能真正看到直播畫面。這個時長對用戶使用直播功能有非常大的負向影響,起播時長指標急需優化。

03 目標

△起播鏈路

起播過程簡單描述就是用戶點擊直播資源,打開直播頁面,請求起播地址,調用內核起播,內核起播完成,內核通知業務,業務起播完成打點。從對內核起播時長監控來看,直播資源的在內核中起播耗時大約為 600-700ms,考慮鏈路中其他階段損耗以及二跳(直播間內上下滑)場景可以在滑動時提前起播,整體起播時長目標定位為1.5 秒;考慮到有些進入直播間的位置已經有了起播流地址,可以在某些場景省去 “請求起播地址” 這一個階段,在這種直播間外已經獲取到起播地址場景,起播時長目標定為 1.1 秒。

04 難點

特殊的插件邏輯和復雜的業務場景使得 Android 版每一次進入直播的起播鏈路都不會完全一樣。只有一級插件且二級插件還未就緒時在一級插件中請求直播數據并起播,一二級插件都已加載時使用二級插件請求直播數據并處理起播,進直播間攜帶流地址時為實現秒開在 Activity 啟動后就創建播放器使用直播間外攜帶的流地址起播。除了這幾種鏈路,還有一些其他情況。復雜的起播鏈路就導致了,雖然在起播過程中主要節點間都有時間戳打點,也有天級別相鄰兩個節點耗時 80 分位報表,但線上不同場景上報的起播鏈路無法窮舉,使用現有報表無法分析直播大盤起播鏈路中真正耗時位置。需要建立新的監控方案,找到耗時點,才能設計針對性方案將各個耗時位置進行優化。

05 解決方案

5.1 設計新報表,定位耗時點

△一跳有起播地址時起播鏈路簡圖

由于現有報表無法滿足起播鏈路耗時階段定位,需要設計新的監控方案。觀察在打開直播間時有流地址場景的流程圖(上圖),進入直播間后就會同步創建直播間列表及創建播放器預起播,當直播間列表創建完畢且播放器收到首幀通知時起播流程結束。雖然用戶點擊到頁面 Activity 的 onCreate 中可能有多個節點(一級插件安裝、加載等),頁面 onCreate 調用播放器預起播中可能多個節點,內核完成到直播業務收到通知中有多個節點,導致整個起播鏈路無法窮舉。但是我們可以發現,從用戶點擊到 onCreate 這個路徑是肯定會有的,onCreate 到創建播放器路徑也是肯定有的。這樣就說明雖然兩個關鍵節點間的節點數量和鏈路無法確定,但是兩個關鍵節點的先后順序是一定的,也是必定會有的。由此,我們可以設計一個自定義鏈路起點和自定義鏈路終點的查詢報表,通過終點和起點時間戳求差得到兩個任意節點間耗時,將線上這兩個節點所有差值求 80 分位,就可以得到線上起播耗時中這兩個節點間耗時。將起播鏈路中所有核心關鍵節點計算耗時,就可以找到整個起播鏈路中有異常耗時的分段。

按照上面的思路開發新報表后,上面的鏈路各階段耗時也就比較清晰了,見下圖,這樣我們就可以針對不同階段逐個擊破。

△關鍵節點間耗時

5.2 一跳使用一級插件起播

使用新報表統計的重點節點間耗時觀察到,直播間列表創建(模版組件創建)到真正調用起播(業務視圖就緒)中間耗時較長,且這個耗時隨著版本收斂會逐步增加,兩周內大約增加 1000ms,首先我們解決這兩個節點間耗時增加問題。

經過起播鏈路觀察和分析后,發現隨版本收斂,這部分起播鏈路有較大變化,主要是因為隨版本收斂,在二級插件中觸發 “業務調用起播” 這個節點的占比增加。版本收斂期,進入直播間時大概率二級插件還未下載就緒或未安裝,此時一級插件中可以很快的進行列表創建并創建業務視圖,一級插件中在 RecyclerView 的 item attach 到視圖樹時就會觸發起播,這個鏈路主要是等待內核完成首幀數據的拉取和解析。當二級插件逐漸收斂,進入直播間后一級插件就不再創建業務視圖,而是有二級插件創建業務視圖。由于二級插件中業務組件較多逐個加載需要耗時還有一級到二級中逐層調用或事件分發也存在一定耗時,這樣二級插件起播場景就大大增加了直播間列表創建(模版組件創建)到真正調用起播(業務視圖就緒)中間耗時。

5.2.1 一跳全部使用一級插件起播

基于上面的問題分析,我們修改了一跳場景起播邏輯,一跳全部使用一級插件起播。一級插件和二級插件創建的播放器父容器 id 是相同的,這樣在一級插件中初始化播放器父容器后,當內核首幀回調時起播過程就可以結束了。二級插件中在初始化播放器父容器時也會通過 id 判斷是否已經添加到視圖樹,只有在未添加的情況(二跳場景或一跳時出現異常)才會在二級中進行兜底處理。在一級插件中處理時速度可以更快,一級優先二級兜底邏輯保證了進入直播間后一定可以順利初始化視圖。

5.2.2 提前請求接口

使用由一起插件處理起播優化了二級插件鏈路層級較多問題,還有一個耗時點就是進直播間時只傳入了房間 room_id 未攜帶流地址場景,此時需要通過接口請求獲取起播數據后才能創建播放器和起播。為優化這部分耗時,我們設計了一個直播間數據請求管理器,提供了緩存數據和超時清理邏輯。在頁面 onCreate 時就會觸發管理器進行接口請求,直播間模版創建完成后會通過管理器獲取已經請求到的直播數據,如果管理器接口請求還未結束,則會復用進行中請求,待請求結束后立刻返回數據。這樣在進直播間未攜帶流數據時我們可以充分利用圖中這 300ms 時間做更多必要的邏輯。

5.3 播放器Activity外預起播

通過進直播間播放器預創建、預起播、一跳使用一級插件起播等方案來優化進入直播間業務鏈路耗時后,業務鏈路耗時逐漸低于內核部分耗時,播放器內核耗時逐漸成為一跳起播耗時優化瓶頸。除了在內核內部探索優化方案,繼續優化業務整個起播鏈路也是一個重要方向。通過節點間耗時可以發現,用戶點擊到 Activity 頁面 onCrete 中間也是有 300ms 左右耗時的。當無法將這部分耗時縮到更短時,我們可以嘗試在這段時間并行處理一些事情,減少頁面啟動后的部分邏輯。

一級插件在百度 APP 中內置后,設計并上線了插件預加載功能,上線后用戶通過點擊直播資源進入直播間的場景中,有 99%+ 占比都是直播一級插件已加載情況,一級插件加載這里就沒有了更多可以的操作空間。但將預起播時機提前到用戶點擊處,可以將內核數據加載和直播間啟動更大程度并行,這樣來降低內核耗時對整個起播耗時影響。

△播放器在直播間外起播示意圖

如上圖,新增一個提前起播模塊,在用戶點擊后與頁面啟動并行創建播放器起播并緩存,頁面啟動后創建播放器時會先從提前起播模塊的緩存中嘗試取已起播播放器,如果未獲取到則走正常播放器創建起播邏輯,如果獲取到緩存的播放器且播放器未發生錯誤,則只需要等待內核首幀即可。

播放器提前起播后首幀事件大概率在 Activity 啟動后到達,但仍有幾率會早于直播業務中設置首幀監聽前到達,所以在直播間中使用復用內核的播放器時需要判斷是否起播成功,如果已經起播成功需要馬上分發已起播成功事件(含義區別于首幀事件,防止與首幀事件混淆)。

提前起播模塊中還設計了超時回收邏輯,如果提前起播失敗或 5s (暫定)內沒有被業務復用(Activity 啟動異?;蚱渌麡I務異常),則主動回收緩存的播放器,防止直播間沒有復用成功時提前創建的播放器占用較多內存及避免泄漏;超時時間是根據線上大盤起播時間決定,使用一個較大盤起播時間 80 分位稍高的值,防止起播還未完成時被回收,但也不能設置較長,防止不會被復用時內存占用較多。

通過提前起播功能,實驗期命中提前起播邏輯較不進行提前起播邏輯,整體起播耗時 80 分位優化均值:450ms+。

5.4直播間任務打散

△內核首幀分發耗時

業務鏈路和內核鏈路耗時都有一定優化后,我們繼續拆解重點節點間耗時。內核內部標記首幀通知到直播業務真正收到首幀通知之間耗時較長,如上圖,線上內核首幀分發耗時 80 分位均值超過 1s,該分段對整體起播耗時優化影響較大。內核首幀是在子線程進行標記,通知業務時會通過主線程 Handler 分發消息,通過系統的消息分發機制將事件轉到主線程。

通過排查內核標記首幀時間點到業務收到首幀通知事件時間點之間所有主線程任務,發現在首幀分發任務開始排隊時,主線程任務隊列中已有較多其他任務,其他事件處理時間較長,導致首幀分發排隊時間較久,分發任務整體耗時也就較長。直播業務復雜度較高,如果內核首幀分發任務排隊時直播間其他任務已在隊列中或正在執行,首幀分發任務需要等直播任務執行完成后才能執行。

通過將直播間啟動過程中所有主線程任務進行篩查,發現二級插件的中業務功能較多,整體加載任務執行時間較長,為驗證線上也是由于二級業務任務阻塞了首幀分發任務,我們設計了一個二級組件加載需要等待內核首幀后才能進行的實驗,通過實驗組與對照組數據對比,在命中實驗時首幀分發耗時和起播整體耗時全部都有明顯下降,整體耗時有 500ms 左右優化。

通過實驗驗證及本地對起播階段業務邏輯分析,定位到直播間各業務組件及對應視圖的預加載數量較多且耗時比較明顯,這個功能是二級插件為充分利用直播間接口數據返回前時間,二級插件加載后會與接口請求并行提前創建業務視圖,提起初始化組件及視圖為接口完成后組件渲染節省時間。如果不預創建,接口數據回來后初始化業務組件也會主動創建后設置數據。但將所有預創建任務全部串行執行耗時較長,會阻塞主線程,頁面一幀中執行太多任務,也會造成頁面明顯卡頓。

發現這個阻塞問題后,我們設計了將預創建視圖任務進行拆分打散,將一起執行的大任務拆分成多個小任務,每個組件的初始化都作為一個單獨任務在主線程任務隊列中進行排隊等待執行。避免了一個大任務耗時特別長的問題。該功能上線后,整個二級插件中的組件加載大任務耗時降低了 40%+。

5.5 內核子線程分發首幀

由于主線程消息隊列中任務是排隊執行的,將阻塞首幀分發事件的大任務拆分成較多小任務后,還是無法解決首幀事件開始排隊時這些小任務已經在主線程任務隊列中排隊問題。除了降低直播業務影響,還可以通過加快內核任務分發速度,使首幀分發耗時降低。需要設計一個在不影響內核穩定性與業務邏輯情況下內核首幀事件如何避免主線程排隊或快速排隊后被執行的方案。

為解決上面的問題, 我們推動內核,單獨增加了一個子線程通知業務首幀事件能力。業務收到子線程中首幀回調后通過 Handler 的 postAtFrontOfQueue() 方法將一個新任務插到主線程任務隊列最前面,這樣主線程處理完當前任務后就可以馬上處理我們新建的這個任務,在這個新任務中可以馬上處理播放器上屏邏輯。無需等待播放內核原本的主線程消息。

主線程任務前插無法打斷新任務排隊時主線程中已經開始執行的任務,需要正在執行任務結束后才會被執行。為優化這個場景,內核通過子線程通知首幀后,播放器中需要記錄這個狀態,在一級插件及二級插件中的直播間業務任務執行開始前后,增加判斷播放器中是否已經收到首幀邏輯,如果已經收到,就可以先處理上屏后再繼續當前任務。

通過直播內核首幀消息在主線程任務隊列前插和業務關鍵節點增加是否可上屏判斷,就可以較快處理首幀通知,降低首幀分發對起播時長影響。

5.6 起播與完載指標平衡

直播間起播優化過程中,完載時長指標(完載時長:用戶點擊到直播間核心功能全部出現的時間,其中經歷頁面啟動,直播間列表創建,二級插件下載、安裝、加載,直播間接口數據請求,初始化直播間功能組件視圖及渲染數據,核心業務組件顯示等階段)的優化也在持續進行。直播間二級插件是在使用二級插件中的功能時才會觸發下載安裝及加載邏輯,完載鏈路中也注意到了用戶點擊到頁面 onCreate 這段耗時,見下圖。

△頁面啟動耗時示意圖

為優化直播間完載指標,直播團隊考慮如果將插件加載與頁面啟動并行,那么完載耗時也會有一定的優化。直播團隊繼續設計了二級插件預加載方案,將二級插件加載位置提前到了用戶點擊的時候(該功能上線在 5.4、5.5 章節對應功能前)。該功能上線后試驗組與對照組數據顯示,實驗組完載耗時較對照組確實有 300ms+ 優化。但起播耗時卻出現了異常,實驗組的起播耗時明顯比對照組增長了 500ms+,且隨版本收斂這個起播劣化還在增加。我們馬上很快發現了這個異常,并通過數據分析確定了這個數據是正確的。完載的優化時如何引起起播變化的?

經過數據分析,我們發現起播受影響的主要位置還是內核首幀消息分發到主線程這個分段引起,也就是二級插件加載越早,內核首幀分發與二級組件加載時的耗時任務沖突可能性越大。確認問題原因后,我們做了 5.4、5.5 章節的功能來降低二級組件加載任務對起播影響。由于二級插件中的耗時任務完全拆分打散來緩解二級插件預下載帶來的起播劣化方案復雜度較高,對直播間邏輯侵入太大,二級插件提前加載沒有完全上線,完載的優化我們設計了其他方案來實現目標。

雖然不能在進入直播間時直接加載二級插件,但我們可以在進入直播間前盡量將二級插件下載下來,使用時直接加載即可,這個耗時相對下載耗時是非常小的。我們優化了插件預下載模塊,在直播間外展示直播資源時觸發該模塊預下載插件。該模塊會通過對當前設備網絡、帶寬、下載頻次等條件綜合判斷,在合適的時機將匹配的二級插件進行下載,插件提前下載后對完載指標有較大優化。除了插件預下載,直播間內通過 5.4 章節直播間二級組件初始化拆分,也將全部組件初始化對主線程阻塞進行了優化,這樣接口數據請求成功后可以優先處理影響完載統計的組件,其他組件可以在完載結束后再進行初始化,這個方案也對直播完載指標有明顯優化。

除了以上兩個優化方案,直播團隊還在其他多個方向對完載指標進行了優化,同時也處理了完載時長與起播時長的指標平衡,沒有因為一個指標優化而對其他指標造成劣化影響。最終實現了起播、完載指標全部達到目標。

06 收益

△2022 Android 端起播耗時走勢

經過以上多個優化方案逐步迭代,目前 Android 端最新版本數據,大盤起播時間已經由 3s+ 降到 1.3s 左右;一跳帶流地址時起播時長由 2.5s+ 左右降低到 1s 以內;二跳起播時長由 1s+ 降低到 700ms 以內,成功完成了預定目標。

07 展望

起播時長作為直播功能一個核心指標,還需要不斷打磨和優化。除了業務架構上的優化,還有優化拉流協議、優化緩沖配置、自適應網速起播、優化 gop 配置、邊緣節點加速等多個方向可以探索。百度直播團隊也會持續深耕直播技術,為用戶帶來越來越好的直播體驗。

END


https://mp.weixin.qq.com/s/F6HjtfLenijurC7HNfUdKA

最多閱讀

簡化Android的UI開發 3年以前  |  515859次閱讀
Android 深色模式適配原理分析 2年以前  |  27474次閱讀
Android 樣式系統 | 主題背景覆蓋 2年以前  |  8727次閱讀
Android Studio 生成so文件 及調用 2年以前  |  6742次閱讀
30分鐘搭建一個android的私有Maven倉庫 4年以前  |  5508次閱讀
Android設計與開發工作流 3年以前  |  5225次閱讀
移動端常見崩潰指標 2年以前  |  5081次閱讀
Android陰影實現的幾種方案 5月以前  |  5058次閱讀
Google Enjarify:可代替dex2jar的dex反編譯 4年以前  |  5018次閱讀
Android內存異常機制(用戶空間)_NE 2年以前  |  4766次閱讀
Android-模塊化-面向接口編程 2年以前  |  4667次閱讀
Android多渠道打包工具:apptools 4年以前  |  4570次閱讀
Google Java編程風格規范(中文版) 4年以前  |  4423次閱讀
Android死鎖初探 2年以前  |  4391次閱讀

手機掃碼閱讀
18禁止午夜福利体验区,人与动人物xxxx毛片人与狍,色男人窝网站聚色窝
<蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>