B站直播間基於檢視互動的架構演進
背景
1. B站的直播間作為整個APP中互動最為複雜的單頁面之一,其承擔的業務量已經不亞於一個小型APP。對比APP的結構會發現許多相同處,但與組成APP的各個獨立Activity不同,直播間由各個獨立的檢視組成。
從APP維度看每個Activity是一個業務單元,類比直播間維度每個直播間中的View是一個業務單元。
2. 業務邏輯基於MVVM的設計分為三層(Service即M層),理想狀態下各個業務間的互動是內聚的,各業務間不會感知到其他業務的存在,View顯示需要的資料和狀態都由各自對應的ViewModel提供。
3. 現實情況中業務互動並沒有那麼理想,直播間中的一個View顯示的資料會受到其他業務的資料、狀態影響,因此一個View除了需要處理自己內聚的邏輯為還需要關心其他View的資料變化。
01 直播間業務互動現狀
1.1 現有互動邏輯
直播間是一種單頁面強互動型業務場景,一個業務就經常需要會關心其他業務的狀態,因此垂直方向擴充業務場景就會很多,直播間中的業務幾乎都是在垂直螢幕方向上進行擴充的,產品在新增業務時往往會將重要的業務放在更顯眼的地方,因此需要儘量使重要的業務不被遮擋。
然而,直播間的檢視又不是一成不變的,與常規頁面面對的業務場景不同,直播間的檢視除了需要響應使用者自己的操作外,還需要根據主播和其他正在看直播的使用者操作展示和改變檢視,為了保證整體展示邏輯沒問題經常會有檢視聯動的需求。
下圖列舉了部分直播間業務的構建位置和層級關係,按層劃分類似的檢視直播間中有60多個:
現存的設計將大部分業務邏輯集中在ViewModel中處理,因為有著LiveData的存在,資料變化的監聽在ViewModel和View間變得容易。
當其他ViewModel已經存在自己View關心的LiveData或呼叫方法時,開發者很自然的會去引用一個現有的其他ViewModel來觀察他持有的LiveData或呼叫其方法。
1.2 現有互動邏輯帶來的問題
不規範的LiveData的使用和糅雜在一起的業務邏輯導致了ViewModel引用的濫用,使得View間的耦合愈發嚴重,下圖是現有直播間ViewModel的引用關係:
直播間內日益複雜的業務會進一步加重View之間的耦合,在分析了上圖中的引用關係後,會發現目前之所以會出現View引用其他業務的ViewModel的場景,主要有兩個原因:
1. 與View對應的ViewModel無法提供需要的資料,其他Viewmodel持有需要的資料
2. View的某個操作或資料改變需要告知其他業務,其他Viewmodel包含需要呼叫的方法
直播間內日益複雜的業務會進一步加重View之間的耦合,特別是在新增的業務對老業務有改動時,開發人員慣性的去尋找有沒有現成的邏輯處理,如果有就會想辦法複用,而現存的設計將大部分業務邏輯集中在ViewModel中處理,就必定會導致ViewModel引用的不可控。
1.3 問題的分析
直播間各業務引用關係錯亂只是表象,最直接的原因就是業務資料的訪問的不規範,錯綜複雜的引用關係會加重業務間的耦合情況,耦合的業務邏輯又會增加業務載入流程和資料分發的複雜度,週而復始,形成了惡性迴圈。
針對以上問題打破惡性迴圈,我們透過指令碼分析了直播間內60+業務模組,列出了1400+個引用ViewModel的具體使用場景,並且整理了的理想中的資料提供方作為後續改造的參考:
02 基於檢視劃分的業務資料
直播間中的業務使用MVVM的結構構建,我們提供了一套統一的構建模板來構建和管理各層邏輯,單個業務中每層有各自維護的資料和狀態資訊,這些資料禁止躍層訪問,並跟隨各層的生命週期建立和銷燬。
2.1 資料使用場景
資料使用分為三個場景:初始化資料,互動資料,對外提供資料
初始化資料
一個業務的初始化一般處於房間載入的某個任務中
業務初始化,由當前任務提供該階段可以訪問的資料,作為初始化資料
初始化資料會轉化為業務專有的資料結構(圖中Data),供內部邏輯、檢視使用和管理
互動資料
一個房間所有業務初始化完成後,如果沒後續的互動,理論上是完全靜止的,任何改變當前直播間的動作都可以看做是一個互動,而每個互動都會帶上一些資料
使用者每次對業務View的點選、滑動等都會產生一些事件並帶上相應的資料,這些事件可能直接在View層就已經消費掉,也可能會觸發一系列的邏輯互動
Socket和Http的響應作為另一類互動資料的來源,由Service層向上通知到各個業務邏輯層,業務邏輯需要監聽這些資料變化做相應處理
此外每個業務都可能會關心其他業務的改變,這些往往改變也會帶來一些資料,依據這些資料業務可能需要對自己的邏輯和View進行相應的操作
對外提供資料
在互動資料裡有提到關心其他業務的變化,這部分的變化應該由每個業務在API中決定暴露那些事件和資料供外部使用
如果關心某個業務的變化,可以透過ServiceManager獲取對應業務的API,透過關心業務API暴露的方法來獲取、訂閱資料
a. provideData型別方法:對外暴露提供資料的方法,由型別方法提供的資料表示,該業務可以對外提供的資料
b. notifyChange型別方法:有資料變化需要對外通知時對外暴露的方法,其他業務透過該型別方法可以訂閱相應的變化通知
每個業務在提供資料時應該考慮清楚需要暴露的資料,不可直接暴露Data給外部使用
每個業務在接收到其他業務的變化通知時,應該在對應的處理裡消費掉傳過來的資料,不要持有該部分資料
2.2 資料的流向
進入直播間時會請求一組初始化介面,響應資料將會由資料分發器管理,分發到各個業務的Services,不同各個業務拿到各自關心的資料後放到各自的businessData中
各業務的Service中將會管理業務所持有的資料,ViewModel想要獲取或改變某個資料時,需要持有對應業務的Service
ViewModel中將各業務的原始資料組合處理後透過LiveData通知對應的View,View可以透過ViewModel對原始資料進行修改
View間的事件(純粹的UI變化)將由ViewEventManager作為通道進行傳遞,傳遞過程中的資料為一次性資料,不可作為該次事件處理外的邏輯資料使用
03 直播間的檢視結構和業務區域
3.1 直播間的結構和區域劃分
在加入上下滑邏輯之前,房間的概念與整個Activity等價,一個房間在Activity被銷燬時釋放所有資源
在加入上下滑邏輯後,房間的概念變為滑動元件中的一個Item,一個房間在Item被划走時釋放所有資源,為了更好的理解業務執行邏輯,我們根據直播間的檢視結構對業務區域進行了劃分
a. 容器區域(Global)包含滑動元件和DIALOG業務層(目前僅話題和各種引導用到),在進房時建立該區域
b. 房間區域(Room)包含房間業務層和播放器業務層,這兩部分檢視均掛載在滑動元件的RoomItem上,在滑動停止時建立該區域
在使用者執行的滑動操作停止後會釋放上一個RoomItem的資源,並重新建立房間區域掛載到停止後的RoomItem根佈局上,而容器區域中的資源仍然隨Activity的銷燬而銷燬,當前的房間區域也會隨容器區域的銷燬而銷燬
業務僅需要宣告自己屬於Global還是Room區域,並在建立、銷燬的回撥中編寫邏輯,而不需要關心自己何時被建立和銷燬
3.2 按區域劃分載入流程
以構建item中房間容器的時機為分割點,之前的載入流程屬於容器區域,之後的載入流程屬於房間區域
房間初始化介面請求(P0、P1介面)比較特殊,請求時機以及請求的上游處理邏輯屬於容器區域,但是介面響應資料的處理邏輯屬於房間區域
在直播間銷燬的流程中,在容器銷燬流程和房間銷燬流程中都需要銷燬的邏輯,歸為房間區域管理,僅在容器銷燬流程中銷燬的邏輯歸容器區域管理
04 架構演進中的一些思考
1. 架構最後是為業務需求場景服務的,那它也要順應業務的變化而適時調整。來B站直播的期間經歷了直播間從不能滑動到可以上下滑,從老直播間為主到以新版直播間為主,整個產品互動形態發生了巨大變化,新的架構演進方向往往取決於對新業務形態的認知。
2. 隨著業務的不斷髮展和改變以及組織架構的調整,因為趕工期、圖方便而設計不合理但剛好能用的程式碼會越來越多,原先用起來很順暢的架構必定會慢慢腐爛變質,一直修修補補只是在掩飾問題和推遲問題的爆發,作為一線開發我完全可以理解開發時的內心想法:
別人都這麼寫,就算是不合理,跟著也總不會錯
時間不夠了,這坨程式碼真爛,但我只是來改點小功能,等誰改不動了誰去改
現有的架構根本沒考慮到我這種場景,先隨便找個地方放著,能實現需求再說
3. 基於以上思考,我認為架構演進的目的主要有兩個:
打破團隊的不滿:打破保守的做法,要積極面對不合理的地方。團隊不定期需要著手開啟重構,將大家平日對程式碼的不滿釋放出來。整理直播間老大難的歷史債,將架構的腐化(效率降低、抱怨上升)轉化為架構最佳化的動力。
團隊意識的培養:培養全員架構的意識,架構演進的過程中會牽扯眾多模組的重構,在各個模組重構的過程中傳達架構的思想、形成團隊共識,形成“人人都是架構師”的氛圍。
來自 “ 嗶哩嗶哩技術 ”, 原文作者:杜峰;原文連結:http://server.it168.com/a2022/1031/6771/000006771347.shtml,如有侵權,請聯絡管理員刪除。
相關文章
- B站公網架構實踐及演進架構
- 大型網站技術架構的演進網站架構
- 架構的演進架構
- B站基於Iceberg的湖倉一體架構實踐架構
- LinkedIn Feed流視訊自動播放架構演進架構
- 閒魚基於Flutter技術的架構演進和創新Flutter架構
- Airbnb的架構演進AI架構
- Serverless 架構的演進Server架構
- 大型網站的技術架構演進過程網站架構
- 今日頭條架構演進之路 — 高壓下的架構演進架構
- 基於python對B站收藏夾按照視訊釋出時間進行排序Python排序
- 網站架構及架構演變網站架構
- 微服務事件驅動架構演進微服務事件架構
- 餓了麼移動APP的架構演進APP架構
- 有贊基於ES的搜尋系統架構是如何演進的?架構
- 大型網站圖片伺服器架構的演進網站伺服器架構
- 架構演進之「微服務架構」架構微服務
- 今日頭條架構演進之路——高壓下的架構演進專題架構
- 聊聊演進式架構架構
- 京東咚咚架構演進架構
- 技術架構演進的思考架構
- 架構視覺化支撐系統演進探索架構視覺化
- 基於聲網 Flutter SDK 實現互動直播Flutter
- 大型網站架構演進的五大階段盤點網站架構
- 探索餓了麼移動APP的架構演進之路APP架構
- GMTC2019演講實錄|閒魚基於Flutter的架構演進與創新Flutter架構
- Serverless 架構模式及演進Server架構模式
- 統一接入層架構的演進架構
- 從MVC到DDD的架構演進MVC架構
- 圖解分散式架構的演進圖解分散式架構
- B站Android程式碼庫的演進歷程Android
- 大型網站架構體系的演變網站架構
- 基於php的教學互動網站系統PHP網站
- 滴滴機器學習平臺架構演進機器學習架構
- vivo推送平臺架構演進架構
- Flutter Fish Redux架構演進2.0FlutterRedux架構
- Python後端架構演進Python後端架構
- Serverless 架構演進與實踐Server架構