亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android端內(nèi)數(shù)據(jù)狀態(tài)同步方案VM-Mapping詳解

 更新時(shí)間:2021年09月07日 14:50:30   作者:冬天的毛毛雨  
這篇文章主要介紹了Android端內(nèi)數(shù)據(jù)狀態(tài)同步方案VM-Mapping詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下

背景

西瓜在feed、詳情頁(yè)、個(gè)人主頁(yè)有一塊功能區(qū),包括了點(diǎn)贊、收藏、關(guān)注等功能。這些功能長(zhǎng)久以來(lái)都是孤立的:多個(gè)場(chǎng)景下點(diǎn)贊、收藏、關(guān)注等狀態(tài)或數(shù)量不一致。在以往的業(yè)務(wù)迭代中,都是業(yè)務(wù)A有了需求,就加個(gè)點(diǎn)贊的請(qǐng)求,把自己業(yè)務(wù)模塊的UI更新下就完事了,業(yè)務(wù)B也自己搞一下。當(dāng)西瓜開(kāi)始從切面發(fā)力互動(dòng)業(yè)務(wù)的時(shí)候,這些問(wèn)題就凸顯出來(lái)了。線上出現(xiàn)了很多在頁(yè)面A點(diǎn)贊/收藏完一個(gè)視頻到頁(yè)面B點(diǎn)贊/收藏狀態(tài)或者點(diǎn)贊/收藏?cái)?shù)不對(duì)的case。

例如:

問(wèn)題拆解

在分析這塊業(yè)務(wù)時(shí),梳理出幾種問(wèn)題:

  1. 業(yè)務(wù)上場(chǎng)景太分散,體現(xiàn)到代碼上就是在activity、scene、viewholder、自定義view等各種個(gè)樣的容器,多個(gè)業(yè)務(wù)模塊、多個(gè)端(web、flutter)上都有很相似的操作,代碼跨度很大。
  2. 存量的代碼中有些場(chǎng)景是處理過(guò)同步問(wèn)題的,但是處理的又不徹底,方案也不一樣,比如有的情況用了全局注冊(cè)callback,來(lái)通知所有對(duì)結(jié)果敏感的場(chǎng)景;有的情況用了Eventbus;有的情況是更新內(nèi)存,但是卻只是個(gè)別幾個(gè)模塊通用。
  3. 一部分問(wèn)題是原來(lái)的業(yè)務(wù)邏輯,比如,使用更新后的內(nèi)存變量在多個(gè)頁(yè)面或者模塊傳遞引用,由于層次比較深引用值被中間的流程篡改。
  4. 一部分問(wèn)題是服務(wù)端數(shù)據(jù)邏輯問(wèn)題。

其中3、4點(diǎn)問(wèn)題更像是邏輯bug。

多個(gè)端的數(shù)據(jù)同步可以通過(guò)跨端事件,每個(gè)端收到事件后更新自己就行。所以最復(fù)雜最難搞的問(wèn)題就是端內(nèi)多場(chǎng)景下的數(shù)據(jù)狀態(tài)同步問(wèn)題。

端內(nèi)問(wèn)題聚焦在幾個(gè)case:

  • case1:普通頁(yè)面,如Activity or Fragment上的狀態(tài)同步;
  • case2:feed卡片的狀態(tài)同步;
  • case3:feed卡片內(nèi)多個(gè)復(fù)雜層級(jí)之間的狀態(tài)同步;
  • case4:以上的組合。

目標(biāo)

  • 數(shù)據(jù)狀態(tài)同步,是要保證兩個(gè)一致性:數(shù)據(jù)一致性、UI一致性;
  • 方案要使用簡(jiǎn)單,理解簡(jiǎn)單;
  • 盡可能減少性能開(kāi)銷(xiāo)。

方案調(diào)研

EventBus

這個(gè)方案的本質(zhì)是:監(jiān)聽(tīng)者收到事件->更新UI/更新數(shù)據(jù)Model

  • 對(duì)于case1:如果是A頁(yè)面發(fā)起,B頁(yè)面被動(dòng)接收,只需要在B頁(yè)面接收事件,更新B頁(yè)面的Model對(duì)象+UI即可。但是在收到事件之后,一定要把當(dāng)前頁(yè)面的model對(duì)象更新,不然會(huì)有不一致的問(wèn)題。
  • 對(duì)于case2
  1. eventbus注冊(cè)在ViewHolder 上:由于ViewHolder的復(fù)用,ViewHolder的數(shù)量是少于“ListData”的,那么意味著,只在ViewHolder上監(jiān)聽(tīng),會(huì)出現(xiàn)那些沒(méi)有和ViewHolder 建立聯(lián)系的數(shù)據(jù)無(wú)法被更新到。如果使用黏性事件,該事件會(huì)一直在內(nèi)存中,粘性事件的膨脹不可控,很可能會(huì)造成嚴(yán)重的內(nèi)存問(wèn)題。
  2. eventbus注冊(cè)在Activity or 其它頁(yè)面上,收到事件后,遍歷數(shù)據(jù)列表,更新,然后通過(guò)RecyclerView的onDataItemChanged方法局部更新。但是在很多場(chǎng)景,比如西瓜feed,feed框架之下的view層次非常深。很多時(shí)候Rd只關(guān)注某類(lèi)卡片下的某個(gè)UI組件,F(xiàn)eed框架和頂層頁(yè)面容器離的很遠(yuǎn),修改成本高,容易出錯(cuò),對(duì)feed框架或者頂層容器的侵入比較大。另外,onDataItemChanged的局部更新是ViewHolder 對(duì)應(yīng)的itemView的,這個(gè)維度比較大,并不能刷新單獨(dú)的一個(gè)點(diǎn)贊按鈕。

基于k-v的監(jiān)聽(tīng)、通知

以對(duì)象id為key,某個(gè)屬性值如點(diǎn)贊數(shù)為value。事件發(fā)生時(shí),將修改值寫(xiě)入k-v列表,監(jiān)聽(tīng)者全部監(jiān)聽(tīng)這個(gè)變化。當(dāng)新進(jìn)入一個(gè)場(chǎng)景時(shí),查詢(xún)k-v列表作為最新值。這個(gè)方案和Eventbus粘性事件很像。

  • k-v 粒度太細(xì),一直在內(nèi)存中,非常容易膨脹,沒(méi)有合適的釋放時(shí)機(jī),導(dǎo)致內(nèi)存浪費(fèi);一旦移除,就可能概率的數(shù)據(jù)同步失效。
  • k-v列表內(nèi)的狀態(tài)要使用者在合適的時(shí)機(jī)同步到業(yè)務(wù)層數(shù)據(jù)Model。

全局共享數(shù)據(jù)Model實(shí)例

同一個(gè)數(shù)據(jù)Model對(duì)象,比如一個(gè)卡片Model,每次更新都是全局可見(jiàn)的。但是很明顯,

  • 對(duì)數(shù)據(jù)Model的要求很高。一個(gè)業(yè)務(wù)層數(shù)據(jù)Model類(lèi)型,要全局統(tǒng)一,比如,一個(gè)視頻卡片業(yè)務(wù)層的類(lèi)型是“ModelA”,那么全局場(chǎng)景不能有“ModelB”表示卡片。在很多場(chǎng)景下,業(yè)務(wù)層會(huì)對(duì)原始數(shù)據(jù)Model進(jìn)行包裝適配;
  • 內(nèi)存占用很大;可能要緩存很多個(gè)列表。

基于注解的對(duì)象映射方案VM-Mapping

特點(diǎn)

  1. 以命名空間+指定字段值 為key,匹配相同注解名的字段的映射,打平了Model類(lèi)型的不同、層級(jí)嵌套的約束;
  2. 直接更新結(jié)果到數(shù)據(jù)model(如article),與數(shù)據(jù)model視角的同步;
  3. 打平了多個(gè)頁(yè)面、復(fù)雜view層級(jí)嵌套的差異;
  4. 自動(dòng)處理更新,使用者僅需要關(guān)心怎么更新UI,不需要考慮數(shù)據(jù)Model的一致性;
  5. 任意場(chǎng)景的支持。

思考

  1. 數(shù)據(jù)狀態(tài)同步,到底同步的是什么?
  2. 上述的方案中大致有幾個(gè)角色:事件、監(jiān)聽(tīng)者、數(shù)據(jù)Model、UI。到底誰(shuí)應(yīng)該是主導(dǎo)者?
  3. 基于事件的方案都需要把狀態(tài)同步給數(shù)據(jù)Model,能簡(jiǎn)化嗎?

圖片

這個(gè)過(guò)程中有四個(gè)角色,三個(gè)操作。

突破View層級(jí)的限制

從MVVM說(shuō)起。

MVVM是一種軟件設(shè)計(jì)典范,用一種業(yè)務(wù)邏輯、數(shù)據(jù)、界面顯示分離的方法組織代碼。

MVVM本質(zhì)上是一種數(shù)據(jù)驅(qū)動(dòng)UI的理念。從這個(gè)理念看,數(shù)據(jù)狀態(tài)同步,同步的是數(shù)據(jù)Model,UI的變更是由數(shù)據(jù)的變更引起的,真正關(guān)注的點(diǎn)應(yīng)該在數(shù)據(jù)本身上。

這樣,就不再需要額外一個(gè)接受事件的“容器”,來(lái)控制數(shù)據(jù)和UI了。到現(xiàn)在,只有三個(gè)角色,兩個(gè)操作了。

再回過(guò)頭看,為什么跨頁(yè)面、跨多View層級(jí)很難找到一個(gè)通用方案,是因?yàn)榭傇谡乙粋€(gè)“容器”來(lái)承載事件的接受,然后再做雙份(數(shù)據(jù)和View)的同步。而且這個(gè)“容器”通常本身就是一個(gè)頁(yè)面,或者其它不同層級(jí)上的view,本身就存在很多樣化,為這種多樣化適配,就會(huì)讓事情變得復(fù)雜。

假如不再找額外的“容器”,直接把監(jiān)聽(tīng)綁定在數(shù)據(jù)上,那么View層級(jí)的限制也就不存在了。因?yàn)椴还茉谑裁磮?chǎng)景,什么層級(jí),真正的邏輯中心都是數(shù)據(jù),View也是通過(guò)數(shù)據(jù)渲染出來(lái)的,View不關(guān)心自己在什么層級(jí),只關(guān)心數(shù)據(jù)的變化。

突破類(lèi)型的限制

這里有幾個(gè)類(lèi)型的限制:

  1. 數(shù)據(jù)Model的類(lèi)型是否只能一成不變,假如網(wǎng)絡(luò)請(qǐng)求的原始數(shù)據(jù)是A類(lèi)型,在場(chǎng)景1直接用了A類(lèi)型,在場(chǎng)景2為了適配UI對(duì)A做了包裝:

雖然類(lèi)型不同,但是對(duì)A、B來(lái)說(shuō),都是要更新diggStatus的;

  1. 在Android,數(shù)據(jù)Model的類(lèi)型是強(qiáng)類(lèi)型,是從網(wǎng)絡(luò)由二進(jìn)制流反序列化出來(lái)的,那么同一個(gè)二進(jìn)流,既可以反序列化成A類(lèi)型,又可以反序列化成B類(lèi)型,只要滿(mǎn)足反序列化規(guī)則就行。但是事實(shí)上,他們的業(yè)務(wù)本質(zhì)還是一個(gè)東西。
  2. 事件本身也是一個(gè)數(shù)據(jù),只是它是用戶(hù)操作發(fā)起的,表象看和數(shù)據(jù)Model無(wú)關(guān),但是一個(gè)事件既然能更新某個(gè)數(shù)據(jù)Model,那他們一定存在著對(duì)應(yīng)關(guān)系。

這個(gè)問(wèn)題的本質(zhì)是,類(lèi)型約束是語(yǔ)言特性,但是和業(yè)務(wù)屬性無(wú)關(guān),只要他們能確認(rèn)是一個(gè)業(yè)務(wù)含義,不管他們?cè)趺磽Q“馬甲”,他們總是能匹配上的。

這樣就演變成了:

  1. 怎么確定兩個(gè)類(lèi)型是一個(gè)業(yè)務(wù)含義;
  2. 怎么確定屬性的對(duì)應(yīng)關(guān)系(字段匹配)。

第一個(gè)好說(shuō),主要能有唯一的業(yè)務(wù)標(biāo)識(shí),就能確定是一個(gè)業(yè)務(wù)含義;怎么確定屬性的對(duì)應(yīng)關(guān)系呢?

現(xiàn)有的技術(shù)體系里就有可以借鑒的思想:數(shù)據(jù)庫(kù)的使用。像jetpack 的Room組件:

可以看到,我們只要要在應(yīng)用層這么定義一個(gè)數(shù)據(jù)Model叫User,為它加上注解,就可以把數(shù)據(jù)庫(kù)中的字段和我們的數(shù)據(jù)對(duì)應(yīng)上。那么方案呼之欲出,注解是可以完成屬性匹配的。

于是乎整個(gè)流程就簡(jiǎn)化成了:

這個(gè)流程可以看到,只剩下了兩個(gè)角色,和兩個(gè)操作了。

所謂數(shù)據(jù)更新UI,就是View-Model;數(shù)據(jù)映射數(shù)據(jù),就是Data-Mapping,于是這個(gè)方案的名稱(chēng)就是VM-Mapping。

詳細(xì)設(shè)計(jì)

需要對(duì)上述抽象流程做實(shí)現(xiàn)。

映射

前面說(shuō)到,映射關(guān)系由注解維護(hù),一個(gè)有三個(gè)注解:

  • Mappable注解 :

標(biāo)注在class上,用來(lái)識(shí)別這個(gè)類(lèi)是不是可以被處理。

其中mappingSpace是命名空間,表示是“一類(lèi)”數(shù)據(jù),可以和數(shù)據(jù)庫(kù)表名對(duì)比理解,mappingSpace就是tableName。

  • PrimaryKey注解:

標(biāo)記在字段上,被標(biāo)記的字段作為Model對(duì)象的唯一標(biāo)識(shí)。

mappingSpace+PrimaryKey的值,就是在映射關(guān)系中的唯一業(yè)務(wù)標(biāo)識(shí)。

  • MappableKey注解:

標(biāo)注在字段上,需要被映射對(duì)應(yīng)的字段

映射關(guān)系說(shuō)明:

數(shù)據(jù)驅(qū)動(dòng)UI

Android里有很多類(lèi)似理念的東西,比如LiveData,就是數(shù)據(jù)更新通知到UI上。本質(zhì)上數(shù)據(jù)驅(qū)動(dòng)UI,就是在數(shù)據(jù)Data<->UI 之間建一個(gè)“橋梁”。

這個(gè)不過(guò)LiveData并不適合用在這里,理由是:

  • LiveData綁定的生命周期是LifecycleOwner,也就是Activity、Fragment維度,明顯我們的場(chǎng)景維度更細(xì);
  • 直接observeForever也可以,但是由于View層級(jí)的多樣,調(diào)用方通常需要合適的時(shí)機(jī)移除;
  • LiveData 強(qiáng)引用了數(shù)據(jù)Data,這個(gè)“橋梁”本身對(duì)數(shù)據(jù)Data的生命周期造成了影響。

VM-Mapping做了個(gè)簡(jiǎn)單方案。用了兩級(jí)HashMap,一級(jí)HashMap使用業(yè)務(wù)唯一標(biāo)識(shí)(mappingSpace+PrimaryKey的值)為KEY,二級(jí)使用WeakHashMap,以數(shù)據(jù)Model實(shí)例為KEY,XGViewModel為VALUE。維護(hù)數(shù)據(jù)Data 和 UI回調(diào)之間的關(guān)系:

XGViewModel維護(hù)了通知給UI的弱引用回調(diào)合集。一個(gè)數(shù)據(jù)Model實(shí)例對(duì)應(yīng)了一個(gè)XGViewModel。

當(dāng)映射發(fā)生時(shí),會(huì)通過(guò)業(yè)務(wù)標(biāo)識(shí)Key,查找所有還沒(méi)有被回收的數(shù)據(jù)Model實(shí)例,然后通過(guò)對(duì)應(yīng)的XGViewModel通知UI自己的變更。

總體流程

在這個(gè)流程中,業(yè)務(wù)使用只需要關(guān)心發(fā)起映射數(shù)據(jù)和更新視圖。

因?yàn)榇嬖诹斜?,那么?huì)有一個(gè)列表的維護(hù)者,就是所謂的映射中心。映射中心有兩個(gè)核心能力:

  1. 收集需要被更新的數(shù)據(jù)Model列表;
  2. 查找匹配。

其它細(xì)節(jié)

  • 因?yàn)槭褂昧朔瓷?,為了減少性能損耗,會(huì)對(duì)收集的數(shù)據(jù)Model類(lèi)型做class和相關(guān)字段的緩存。
  • 列表存在膨脹現(xiàn)象,二級(jí)弱引用列表的key是數(shù)據(jù)Model實(shí)例本身,當(dāng)它被虛擬機(jī)回收的時(shí)候,會(huì)把一級(jí)列表中的該項(xiàng)移除,當(dāng)一級(jí)列表某個(gè)key下沒(méi)有內(nèi)容時(shí),也會(huì)把該key移除。
  • 移除的時(shí)機(jī)在每次添加數(shù)據(jù)Model到列表;
  • 移除的條件是一級(jí)列表長(zhǎng)度達(dá)到閾值。

但是注意,這個(gè)移除并不會(huì)影響VM-Mapping的能力,因?yàn)閂M-Mapping關(guān)注的是數(shù)據(jù)本身,當(dāng)數(shù)據(jù)被回收的時(shí)候,不會(huì)有任何場(chǎng)景會(huì)用到這個(gè)數(shù)據(jù),自然也不用關(guān)心是不是需要通知到它。

  • 為了避免影響主線程,和多線程競(jìng)爭(zhēng)列表的問(wèn)題,映射中心操作都在單子線程中處理。

方案對(duì)比

方案 優(yōu)勢(shì) 劣勢(shì)
Eventbus 理解成本低 事件、UI、數(shù)據(jù)Model三個(gè)角色都要保持一致,適配各種場(chǎng)景的成本高,不通用。
全局共享數(shù)據(jù)Model實(shí)例 使用簡(jiǎn)單 條件苛刻;占用內(nèi)存,膨脹不可控制。
基于k-v的監(jiān)聽(tīng)、通知 各場(chǎng)景通用 粒度太細(xì)導(dǎo)致內(nèi)存不可控制,移除策略會(huì)導(dǎo)致同步失效。事件需要手動(dòng)同步數(shù)據(jù)Model。
VM-Mapping 使用簡(jiǎn)單,不需要手動(dòng)同步回?cái)?shù)據(jù)Model,在所有場(chǎng)景下通用。 用到了反射,有一部分性能損耗。

方案收益

西瓜在之前遺留了大量的類(lèi)似問(wèn)題,一直沒(méi)有好的方案解決,要么存在根本性缺陷,要么實(shí)施成本高。VM-Mapping支持了在西瓜中視頻相關(guān)的核心場(chǎng)景快速接入,實(shí)現(xiàn)了線上點(diǎn)贊數(shù)異常問(wèn)題清零。

后續(xù)計(jì)劃

  • 根據(jù)統(tǒng)計(jì),由于使用運(yùn)行時(shí)注解+反射,一個(gè)操作的耗時(shí)均值在10ms左右。仍然有可以?xún)?yōu)化的空間??梢钥紤]使用編譯時(shí)注解維護(hù)數(shù)據(jù)映射關(guān)系。
  • 目前訂閱數(shù)據(jù)的變化,維度是數(shù)據(jù)本身,而不是變化的字段,可以考慮通過(guò)kotlin delegate 細(xì)化監(jiān)聽(tīng)維度。

到此這篇關(guān)于Android端內(nèi)數(shù)據(jù)狀態(tài)同步方案VM-Mapping詳解的文章就介紹到這了,更多相關(guān)Android端內(nèi)數(shù)據(jù)狀態(tài)同步方案VM-Mapping內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • OpenGL Shader實(shí)現(xiàn)簡(jiǎn)單轉(zhuǎn)場(chǎng)效果詳解

    OpenGL Shader實(shí)現(xiàn)簡(jiǎn)單轉(zhuǎn)場(chǎng)效果詳解

    轉(zhuǎn)場(chǎng)效果常出現(xiàn)再視頻剪輯當(dāng)中,用于銜接兩段視頻片段切換的過(guò)渡效果。本文將介紹如何利用OpenGL Shader實(shí)現(xiàn)簡(jiǎn)單的轉(zhuǎn)場(chǎng)效果,需要的小伙伴可以參考一下
    2022-02-02
  • Android Bitmap像素級(jí)操作詳解

    Android Bitmap像素級(jí)操作詳解

    這篇文章主要介紹了Android Bitmap像素級(jí)操作詳解,想了解Bitmap的同學(xué)可以參考下
    2021-04-04
  • Android ViewFlipper簡(jiǎn)單用法解析

    Android ViewFlipper簡(jiǎn)單用法解析

    這篇文章主要為大家詳細(xì)介紹了Android ViewFlipper簡(jiǎn)單用法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • Android開(kāi)發(fā)中如何去掉app標(biāo)題欄的實(shí)現(xiàn)

    Android開(kāi)發(fā)中如何去掉app標(biāo)題欄的實(shí)現(xiàn)

    這篇文章主要介紹了Android開(kāi)發(fā)中如何去掉app標(biāo)題欄的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • Android四大組件之Activity深入解讀生命周期

    Android四大組件之Activity深入解讀生命周期

    雖然說(shuō)我們天天都在使用Activity,但是你真的對(duì)Activity的生命機(jī)制完全了解了嗎?Activity的生命周期方法只有七個(gè),但是其實(shí)那只是默認(rèn)的情況。也就是說(shuō)在其他情況下,Activity的生命周期可能不會(huì)是按照我們以前所知道的流程,這就要說(shuō)到Activity的啟動(dòng)模式
    2022-07-07
  • AndroidStudio項(xiàng)目制作倒計(jì)時(shí)模塊的方法

    AndroidStudio項(xiàng)目制作倒計(jì)時(shí)模塊的方法

    本篇文章主要介紹了AndroidStudio項(xiàng)目制作倒計(jì)時(shí)模塊的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-04-04
  • internal修飾符探索kotlin可見(jiàn)性控制詳解

    internal修飾符探索kotlin可見(jiàn)性控制詳解

    這篇文章主要為大家介紹了internal修飾符探索kotlin可見(jiàn)性控制詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • Android10 App啟動(dòng)Activity源碼分析

    Android10 App啟動(dòng)Activity源碼分析

    這篇文章主要為大家介紹了Android10 App啟動(dòng)Activity源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • android自定義Camera實(shí)現(xiàn)錄像和拍照

    android自定義Camera實(shí)現(xiàn)錄像和拍照

    這篇文章主要為大家詳細(xì)介紹了android自定義Camera實(shí)現(xiàn)錄像和拍照功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • Android?RecyclerLineChart實(shí)現(xiàn)圖表繪制教程

    Android?RecyclerLineChart實(shí)現(xiàn)圖表繪制教程

    這篇文章主要為大家介紹了Android?RecyclerLineChart實(shí)現(xiàn)圖表繪制教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12

最新評(píng)論