RxJava和DiffUtil結(jié)合

原文地址:https://android.jlelse.eu/a-nice-combination-of-rxjava-and-diffutil-fe3807186012

如果你使用RecyclerView族檬,并且保持了與API相同的更新涧团,你也許會(huì)注意到DIffUtil類添加了好幾個(gè)版本行疏,這個(gè)優(yōu)秀的工具類通過(guò)將已經(jīng)存在的數(shù)據(jù)和新生成的數(shù)據(jù)進(jìn)行對(duì)比,然后調(diào)用notifyItemInsertednotifyItemRemoved來(lái)輕松實(shí)現(xiàn)數(shù)據(jù)的改變懈万。你需要做的就是實(shí)現(xiàn)一個(gè)回調(diào),在回調(diào)中將現(xiàn)有數(shù)據(jù)和新獲取的數(shù)據(jù)進(jìn)行比較。

private static class MyDiffCallback extends DiffUtil.Callback{
  private List<Thing> current;
  private List<Thing> next;
  public MyDiffCallback(List<Thing> current,List<Thing> next){
    this.current  = current;
    this.next = next;
  }

  @Override
  public int getOldListSize(){
    return current.size();
  }

 @Override
  public int getNewListSize(){
    return next.size();
  }

  @Override
  public boolean areItemsTheSame(int oldItemPosition,int newItemPosition){
    Thing currentItem = current.get(oldItemPosition);
    Thing nextItem = next.get(newItemPosition);
    return currentItem.getId() == nextItem.getId();
  }

  @Override
  public boolean areContentsTheSame(int oldItemPosition,int newItemPosition){
    Thing currentItem = current.get(oldItemPosition);
    Thing nextItem = current.get(newItemPosition);
    return currentItem.equals(nextItem);
  }
}
DiffResult diffResult = DiffUtil.calculateDiff(new MyDiffCallback(current,next),true);
diffResult.dispatchUpdatesTo(adapter);

上述代碼顯示了如何實(shí)現(xiàn)回調(diào)帝璧,如何執(zhí)行計(jì)算以及如何將notify-call分配到RecyclerView的適配器。

然而這里還存在一個(gè)挑戰(zhàn)那就是如果數(shù)據(jù)量特別大的話湿刽,或者對(duì)比比較復(fù)雜的情況下的烁,你不能在主線程中調(diào)用計(jì)算功能,需要把這些移到一個(gè)后臺(tái)進(jìn)程诈闺,然后把結(jié)果發(fā)送給主線程用來(lái)設(shè)置新的數(shù)據(jù)渴庆,

現(xiàn)在事情變得很棘手,因?yàn)镈iggUtil計(jì)算需要用到已經(jīng)存在的數(shù)據(jù)和新的數(shù)據(jù)雅镊,如果你的適配器有一個(gè)setData()方法襟雷,那么他將需要一個(gè)getData()方法,這意味著將從多個(gè)線程訪問(wèn)數(shù)據(jù)仁烹,因此你需要一些用于同步或者線程安全的數(shù)據(jù)結(jié)構(gòu)耸弄,如何避免這種情況呢。

使用Rxjava將會(huì)解決一切問(wèn)題卓缰,假設(shè)你有一個(gè)Flowable<List<Thing>> listOfThings,他將發(fā)出RecycleView將顯示的獲取的新版本的數(shù)據(jù)计呈,我們可以在IO或者在計(jì)算線程調(diào)度以免阻塞主線程砰诵,然后再主線程進(jìn)行觀察事件將數(shù)據(jù)傳遞給adapter。

ThingRepository
  .latestThings(2,TimeUnit.SECONDS)
  .subscribeOn(computation())
  .observeOn(mainThread())
  .subscribe(things -> {
    adapter.setThings(things);
    adapter.notifyDataSetChanged();
  })

以上的代碼確保了在計(jì)算線程中獲取新的數(shù)據(jù)列表捌显,并且發(fā)送給主線程然后調(diào)用adapter的notifyDataSetChanged茁彭,這樣做有效,但是看起來(lái)不是很好扶歪,因?yàn)槊總€(gè)新列表都需要重新繪制理肺。

為了使用DiffUtil,我們需要調(diào)用calculateDiff()方法善镰,并將DiffResult與最新的List<Thing>一起傳遞給我們的訂閱者哲嘲,一個(gè)簡(jiǎn)單的方法是使用Pair類,這是支持庫(kù)中一個(gè)簡(jiǎn)單并且強(qiáng)大的實(shí)現(xiàn)類媳禁,我們將更改訂閱以接受Pair<List<Thing>,<DiffResult>>事件眠副。

.subscribe(listDiffResultPair -> {
  List<Thing> nextThings = listDiffResultPair.first;
  DiffUtil.DiffResult diffResult = listDiffResultPair.second;
  adapter.setThings(nextThings);
  diffResult.dispatchUpdatesTo(adapter);
});

.scan()

傳遞給DiffUtil.calculateDiff()的回調(diào)需要當(dāng)前列表和新列表才能生效,我們?nèi)绾未_保我們從數(shù)據(jù)源所獲取的每一個(gè)新列表竣稽,我們也想獲取上一個(gè)事件囱怕,這也是RxJava更神秘的一個(gè)運(yùn)算符scan(),scan的初始值將是一個(gè)由空列表和空值作為DiffResult值組成的對(duì)。

現(xiàn)在我們將會(huì)調(diào)用calculateDiff()并將其列在我們的Pair中毫别,我們使用結(jié)果構(gòu)建一個(gè)新的DiffResult娃弓。
我們還有一件事要考慮,如果我們這樣離開(kāi)岛宦,第一件事將包含一個(gè)DiffResult為null的Pair台丛,所以我們必須在我們的訂閱中對(duì)null進(jìn)行檢查,但是砾肺,由于也有可能我們開(kāi)始的時(shí)候是一個(gè)空列表挽霉,所以我們可以使用skip運(yùn)算符來(lái)簡(jiǎn)單的跳過(guò)他,這將會(huì)忽略第一個(gè)事件变汪。

List<Thing> emptyList = new ArrayList<>();
adapter.setThings(emptyList);
Pair<List<Things>,DiffUtil.DiffResult> initialPair = Pair.create(emptyList,null); 
ThingRepository
  .latestThings(2,TimeUnit.SECONDS)
  .scan(initialPair,(pair,next) -> {
    MyDiffCallback callback = new MyDiffCallback(pair.first,next);
    DiffUtil.DiffResult result = DiffUtil.calculateDiff(callback);
    return Pair.create(next,result);
  })
  .skip(1);

這可以使用RxJava和一些職能操作符在后臺(tái)線程上使用DiffUtil侠坎,還沒(méi)有必要考慮適配器保存的當(dāng)前數(shù)據(jù)的任何同步或并發(fā),我們的實(shí)力應(yīng)用程序的結(jié)果可以在下面看到裙盾。

Demo地址:

https://github.com/ErikHellman/RxJavaAndDiffUtil

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末实胸,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子番官,更是在濱河造成了極大的恐慌庐完,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件徘熔,死亡現(xiàn)場(chǎng)離奇詭異门躯,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)近顷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門生音,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人窒升,你說(shuō)我怎么就攤上這事缀遍。” “怎么了饱须?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵域醇,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我蓉媳,道長(zhǎng)譬挚,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任酪呻,我火速辦了婚禮减宣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘玩荠。我一直安慰自己漆腌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布阶冈。 她就那樣靜靜地躺著闷尿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪女坑。 梳的紋絲不亂的頭發(fā)上填具,一...
    開(kāi)封第一講書(shū)人閱讀 51,688評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音匆骗,去河邊找鬼劳景。 笑死,一個(gè)胖子當(dāng)著我的面吹牛碉就,可吹牛的內(nèi)容都是我干的枢泰。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼铝噩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼衡蚂!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起骏庸,我...
    開(kāi)封第一講書(shū)人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤毛甲,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后具被,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體玻募,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年一姿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了七咧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片跃惫。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖艾栋,靈堂內(nèi)的尸體忽然破棺而出爆存,到底是詐尸還是另有隱情,我是刑警寧澤蝗砾,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布先较,位于F島的核電站,受9級(jí)特大地震影響悼粮,放射性物質(zhì)發(fā)生泄漏闲勺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一扣猫、第九天 我趴在偏房一處隱蔽的房頂上張望菜循。 院中可真熱鬧,春花似錦申尤、人聲如沸债朵。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)序芦。三九已至,卻和暖如春粤咪,著一層夾襖步出監(jiān)牢的瞬間谚中,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工寥枝, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留宪塔,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓囊拜,卻偏偏與公主長(zhǎng)得像某筐,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子冠跷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,167評(píng)論 25 707
  • 我從去年開(kāi)始使用 RxJava 南誊,到現(xiàn)在一年多了。今年加入了 Flipboard 后蜜托,看到 Flipboard 的...
    Jason_andy閱讀 5,473評(píng)論 7 62
  • 前言我從去年開(kāi)始使用 RxJava 抄囚,到現(xiàn)在一年多了。今年加入了 Flipboard 后橄务,看到 Flipboard...
    占導(dǎo)zqq閱讀 9,164評(píng)論 6 151
  • MySQL數(shù)據(jù)庫(kù)是較為常見(jiàn)的數(shù)據(jù)幔托,在阿里云上有提供DMS能方便快捷的管理數(shù)據(jù)庫(kù),可以阿里云幫你創(chuàng)建,也可以自己...
    Lazy_Caaat閱讀 1,367評(píng)論 2 3
  • 羅貫中先生寫(xiě)《三國(guó)演義》是注入了感情色彩的重挑。親蜀疏魏中立于吳嗓化。他的情感自然會(huì)傳遞給讀者。不過(guò)成人讀書(shū)應(yīng)客觀公正谬哀,...
    沉之閱讀 883評(píng)論 1 6