<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一篇掌握LiveData transformations

          共 7866字,需瀏覽 16分鐘

           ·

          2022-01-10 19:14

          點(diǎn)擊上方藍(lán)字關(guān)注我,知識(shí)會(huì)給你力量


          這個(gè)系列我做了協(xié)程和Flow開(kāi)發(fā)者的一系列文章的翻譯,旨在了解當(dāng)前協(xié)程、Flow、LiveData這樣設(shè)計(jì)的原因,從設(shè)計(jì)者的角度,發(fā)現(xiàn)他們的問(wèn)題,以及如何解決這些問(wèn)題,pls enjoy it。

          在使用Android架構(gòu)組件時(shí),LiveData是一個(gè)很好的工具。在我知道如何使用Transformations類之前,我一直在濫用LiveData,并產(chǎn)生了大量的爛代碼。在使用LiveData和架構(gòu)組件的幾年中,我想我已經(jīng)找到了一些好的做法和模式,我想與你分享。

          The basics…

          對(duì)LiveData進(jìn)行轉(zhuǎn)換是非常容易的,有一個(gè)名為T(mén)ransformations的輔助類正是為了這個(gè)目的。這個(gè)類提供了三個(gè)靜態(tài)方法:map、switchMap和distinctUntilChanged,這些方法將在下面解釋。下面的所有例子都將使用下面的數(shù)據(jù)類,它代表了我們從數(shù)據(jù)庫(kù)或后臺(tái)API接收的一個(gè)Player數(shù)據(jù)。這個(gè)Player模型只有一個(gè)名字和分?jǐn)?shù)字段,以方便舉例,但在現(xiàn)實(shí)中,它將有更多的字段。

          data class Player(val name: String, val score: Int = 0, val ...)

          map

          將LiveDatain的值轉(zhuǎn)換為另一個(gè)值。下面是一個(gè)簡(jiǎn)單的例子,說(shuō)明如何使用它。

          val player: LiveData<Player> = ...

          val playerName: LiveData<String> = 
              Transformations.map(player) { it.name }

          switchMap

          將一個(gè)LiveDatain的值轉(zhuǎn)換為另一個(gè)LiveData。switchMap的轉(zhuǎn)換可能有點(diǎn)棘手,所以讓我們從一個(gè)簡(jiǎn)單的例子開(kāi)始。我們想為Player實(shí)現(xiàn)一個(gè)基本的搜索功能。每次搜索文本發(fā)生變化時(shí),我們都想更新搜索結(jié)果。下面的代碼顯示了它是如何工作的。

          val searchQuery: LiveData<String> = ...

          fun getSearchResults(query: String): LiveData<List<Player>> = ...

          val searchResults: LiveData<List<Player>> = 
              Transformations.switchMap(searchQuery) { getSearchResults(it) }

          distinctUntilChanged

          對(duì)LiveData進(jìn)行過(guò)濾,除非數(shù)值發(fā)生了變化,否則不會(huì)被檢索出來(lái)。很多時(shí)候,我們可能會(huì)收到一個(gè)不包含任何相關(guān)變化的通知。如果我們監(jiān)聽(tīng)的是所有球員的名字,我們不想在分?jǐn)?shù)發(fā)生變化時(shí)更新用戶界面。這就是distinctUntilChanged方法的用處。

          val players: LiveData<List<Player>> = ...

          val playerNames: LiveData<List<String>> = 
              Transformations.distinctUntilChanged(
                  Transformations.map(players) { players -> players.map { it.name } }
              )

          這是一個(gè)非常好的功能,我在我的代碼中經(jīng)常使用它。對(duì)于我的使用情況,它主要與RecyclerView/適配器的更新有關(guān)。

          livedata-ktx extensions for Transformations

          上述所有的Transformations類函數(shù)也可以作為L(zhǎng)iveData的擴(kuò)展函數(shù),使用下面的依賴。

          androidx.lifecycle:lifecycle-livedata-ktx:<version>

          有了它,例如,你可以把上面的例子改寫(xiě)成下面這樣。

          val players: LiveData<List<Player>> = ...

          val playerNames: LiveData<List<String>> = players.map { it.map { player -> player.name } }
                  .distinctUntilChanged()

          Behind the scenes of the Transformations class

          我們剛剛涵蓋了3個(gè)簡(jiǎn)單的轉(zhuǎn)換,你實(shí)際上可以自己寫(xiě)。所有這些都是使用MediatorLiveData類編寫(xiě)的。MediatorLiveData類是我在處理LiveData時(shí)使用最多的類(盡管我在有意義的時(shí)候使用map / switchMap / distinctUntilChanged)。

          為了給你一個(gè)例子,說(shuō)明你什么時(shí)候應(yīng)該創(chuàng)建你自己的MediatorLiveData類,看看這段代碼。

          val players: LiveData<List<Player>> = ...

          val dbGame: LiveData<GameEntity> = ...

          val game: LiveData<Game> = 
              Transformations.map(dbGame) { game ->
                  val players = this.players.value // Getting current players here may be unsafe
                  Game(players = game.playerIds.mapNotNull { playerId ->
                      players?.find { it.id == playerId }
                  })
              }

          通過(guò)只映射dbGame的變化,我在Player更新時(shí)取了玩家的當(dāng)前值(this.player.value)。所以,當(dāng)Player被更新時(shí),我并沒(méi)有更新Game。為了解決這個(gè)問(wèn)題,我應(yīng)該使用MediatorLiveData來(lái)合并Player和Game,如果他們中的任何一個(gè)被更新。這將看起來(lái)像這樣。

          val players: LiveData<List<Player>> = ...
          val dbGame: LiveData<GameEntity> = ...

          val game: LiveData<Game> = MediatorLiveData<Game>()
              .apply {
                  fun update() {
                      val players = players.value ?: return
                      val game = dbGame.value ?: return
                    
                      value = Game(players = game.playerIds
                                  .mapNotNull { playerId ->
                                      players?.find { it.id == playerId }
                                  }
                              )
                  }

                  addSource(players) { update() }
                  addSource(dbGame) { update() }
                
                  update()
              }

          有了這個(gè)解決方案,每當(dāng)球員或dbGame更新時(shí),我都會(huì)得到Game更新。

          MediatorLiveData

          MediatorLiveData可以轉(zhuǎn)換、過(guò)濾和合并其他LiveData實(shí)例。每當(dāng)我創(chuàng)建MediatorLiveData時(shí),我傾向于遵循同樣的模式,它看起來(lái)像這樣。

          val a = MutableLiveData<Int>(40)
          val b = MutableLiveData<Int>(2)

          val sum: LiveData<Int> = MediatorLiveData<Int>().apply {
              fun update() {
                  // OPTION 3
                  val aVal = a.value ?: return
                  val bVal = b.value ?: return
                
                  // OPTION 4
                  value = aVal + bVal
              }

              // OPTION 1
              addSource(a) { update() }
              addSource(b) { update() }
            
              // OPTION 2
              update()
          }

          在這個(gè)例子中,我正在觀察兩個(gè)LiveData源(a和b)。我在調(diào)解器創(chuàng)建時(shí)調(diào)用了更新函數(shù),只有在兩個(gè)源都是非空的情況下才會(huì)發(fā)出一個(gè)值。這種模式非常通用,但讓我們一個(gè)一個(gè)地走完每一步。

          方案1

          在從這個(gè)LiveData發(fā)出任何東西之前,你想監(jiān)控哪些源的變化。這可以只是一個(gè)單一的源(或更多),但沒(méi)有固定的上限。(即讓你對(duì)單個(gè)LiveData進(jìn)行條件映射或合并多個(gè)LiveDatas)

          方案2

          如果你想在創(chuàng)建MediatorLiveData時(shí)設(shè)置一個(gè)初始值,在這里調(diào)用內(nèi)部更新函數(shù)。為了簡(jiǎn)單起見(jiàn),我通常調(diào)用我的更新函數(shù),但只是設(shè)置MediatorLiveData的值/postValue也可以。在某些情況下,我不想發(fā)出一個(gè)初始值,因?yàn)槲蚁M赼或b還沒(méi)有設(shè)置的情況下發(fā)出空值。那么我就跳過(guò)在這里調(diào)用更新或設(shè)置初始值。

          方案3

          因?yàn)橹灰猘或b發(fā)出更新,就會(huì)調(diào)用update,我們必須期望a和b為空。有時(shí)你實(shí)際上想更新你的MediatorLiveData,即使一個(gè)或多個(gè)來(lái)源目前是空的,但這是一個(gè)很好的方法,在從MediatorLiveData發(fā)出新值之前,確保局部變量aVal和bVal不是空的。你甚至可以在這里應(yīng)用更多的驗(yàn)證/過(guò)濾,以減少你所創(chuàng)建的最終MediatorLiveData的排放。

          方案4

          由于MediatorLiveData是一個(gè)LiveData實(shí)例,我們可以設(shè)置值(像上面的例子)或調(diào)用postValue(如果由于某種原因,你在發(fā)射值時(shí)不在主線程上)。這也是你決定如何轉(zhuǎn)換源數(shù)據(jù)值的地方。上面的例子只是將aVal和bVal相加,但你當(dāng)然可以在這里應(yīng)用你想要的任何轉(zhuǎn)換。

          結(jié)論

          在所有的LiveData轉(zhuǎn)換中使用map、switchMap和distinctUntilChanged。除非有必要,否則應(yīng)避免編寫(xiě)自己的轉(zhuǎn)換,并嘗試結(jié)合操作來(lái)創(chuàng)建更復(fù)雜的轉(zhuǎn)換。

          使用distinctUntilChanged來(lái)避免發(fā)出相同的數(shù)據(jù),這將導(dǎo)致不必要的UI更新。

          如果你發(fā)現(xiàn)自己在地圖/switchMap內(nèi)或觀察塊內(nèi)使用.value屬性獲得另一個(gè)LiveData的當(dāng)前值,你應(yīng)該考慮創(chuàng)建一個(gè)MediatorLiveData來(lái)正確合并來(lái)源。

          原文鏈接:https://proandroiddev.com/livedata-transformations-4f120ac046fc

          向大家推薦下我的網(wǎng)站 https://xuyisheng.top/  點(diǎn)擊原文一鍵直達(dá)

          專注 Android-Kotlin-Flutter 歡迎大家訪問(wèn)



          往期推薦


          本文原創(chuàng)公眾號(hào):群英傳,授權(quán)轉(zhuǎn)載請(qǐng)聯(lián)系微信(Tomcat_xu),授權(quán)后,請(qǐng)?jiān)谠瓌?chuàng)發(fā)表24小時(shí)后轉(zhuǎn)載。
          < END >
          作者:徐宜生

          更文不易,點(diǎn)個(gè)“三連”支持一下??


          瀏覽 51
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  成人毛片18女人毛片免费看dvd | 东京热自拍 | 大鸡吧AV在线 | 亚洲成人在线操 | 精品久久久久久久久久 |