<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>

          ViewModels and LiveData- Patterns + AntiPatterns

          共 8501字,需瀏覽 18分鐘

           ·

          2021-12-10 09:34

          點(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。

          Views and ViewModels

          Distributing responsibilities

          img

          理想情況下,ViewModels不應(yīng)該知道關(guān)于Android的任何事情。這可以提高可測(cè)試性、泄漏安全性和模塊化。一般的經(jīng)驗(yàn)法則是,確保在你的ViewModels中沒(méi)有android.*的導(dǎo)入(android.arch.*等例外)。這同樣適用于presenters。

          • ? 不要讓ViewModels(和Presenters)知道Android框架類(lèi)的情況

          條件語(yǔ)句、循環(huán)和一般決策應(yīng)該在ViewModels或應(yīng)用程序的其他層中完成,而不是在Activities或Fragments中。視圖通常沒(méi)有單元測(cè)試(除非你使用Robolectric),所以代碼行數(shù)越少越好。視圖應(yīng)該只知道如何顯示數(shù)據(jù)并將用戶(hù)事件發(fā)送到ViewModel(或Presenter)。這就是所謂的被動(dòng)視圖模式。

          • ?將Activity和Fragment中的邏輯保持在最低限度

          View references in ViewModels

          視圖模型與Activity或Fragment有不同的作用域。當(dāng)一個(gè)ViewModel活著并運(yùn)行時(shí),一個(gè)Activity可以處于其生命周期的任何狀態(tài)。在ViewModel不知道的情況下,Activity和Fragment可以被銷(xiāo)毀并再次創(chuàng)建。

          img

          將視圖(Activity或Fragment)的引用傳遞給ViewModel是一個(gè)嚴(yán)重的風(fēng)險(xiǎn)。讓我們假設(shè)ViewModel從網(wǎng)絡(luò)上請(qǐng)求數(shù)據(jù),并且數(shù)據(jù)在一段時(shí)間后回來(lái)。這時(shí),View的引用可能會(huì)被破壞,也可能是一個(gè)不再可見(jiàn)的舊Activity,產(chǎn)生內(nèi)存泄漏,并可能導(dǎo)致崩潰。

          • ? 避免在ViewModels中對(duì)View進(jìn)行引用。

          在ViewModels和View之間進(jìn)行通信的推薦方式是觀察者模式,使用LiveData或來(lái)自其他庫(kù)的觀察變量方式。

          Observer Pattern

          img

          在Android中設(shè)計(jì)表現(xiàn)層的一個(gè)非常方便的方法是讓View(Activity或Fragment)觀察(訂閱)ViewModel的變化。由于ViewModel并不了解Android,所以它不知道Android是如何喜歡頻繁地殺死View的。這有一些好處。

          • ViewModel在配置變化時(shí)被持久化,所以當(dāng)重新請(qǐng)求發(fā)生時(shí),不需要重新查詢(xún)外部數(shù)據(jù)源(如數(shù)據(jù)庫(kù)或網(wǎng)絡(luò))。
          • 當(dāng)長(zhǎng)期運(yùn)行的操作結(jié)束時(shí),ViewModel中的觀察變量會(huì)被更新。數(shù)據(jù)是否被觀察并不重要。當(dāng)試圖更新不存在的視圖時(shí),不會(huì)發(fā)生空指針異常。
          • ViewModels不引用視圖,所以?xún)?nèi)存泄漏的風(fēng)險(xiǎn)較小。
          private void subscribeToModel() {
            // Observe product data
            viewModel.getObservableProduct().observe(this, new Observer<Product>() {
                @Override
                public void onChanged(@Nullable Product product) {
                  mTitle.setText(product.title);
                }
            });
          }
          • ?不要把數(shù)據(jù)推送給UI,而是讓UI觀察到它的變化。

          Fat ViewModels

          只要能讓你分離關(guān)注點(diǎn),就是一個(gè)好主意。如果你的ViewModel容納了太多的代碼或者有太多的責(zé)任,可以考慮。

          • 將一些邏輯轉(zhuǎn)移到與ViewModel相同范圍的presenter中。它將與你的應(yīng)用程序的其他部分通信,并更新ViewModel中的LiveData持有者。
          • 添加一個(gè)Domain layer并采用Clean Architecture。這將導(dǎo)致一個(gè)非常可測(cè)試和可維護(hù)的架構(gòu)。它也有利于快速離開(kāi)主線(xiàn)程。在Architecture Blueprints中有一個(gè)Clean Architecture的例子。

          例子在這里:https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html

          • ? 分散責(zé)任,如果需要的話(huà),添加領(lǐng)域?qū)印?

          Using a data repository

          正如在《應(yīng)用程序架構(gòu)指南》中看到的那樣,大多數(shù)應(yīng)用程序都有多個(gè)數(shù)據(jù)源,例如。

          • 遠(yuǎn)程:網(wǎng)絡(luò)或云
          • 本地:數(shù)據(jù)庫(kù)或文件
          • 內(nèi)存中的緩存

          在你的應(yīng)用程序中設(shè)置一個(gè)數(shù)據(jù)層是個(gè)好主意,完全不知道你的表現(xiàn)層。讓緩存和數(shù)據(jù)庫(kù)與網(wǎng)絡(luò)保持同步的算法并非易事。建議有一個(gè)單獨(dú)的存儲(chǔ)庫(kù)類(lèi)作為處理這種復(fù)雜性的單一入口。

          如果你有多個(gè)非常不同的數(shù)據(jù)模型,可以考慮添加多個(gè)存儲(chǔ)庫(kù)。

          • ? 添加一個(gè)數(shù)據(jù)存儲(chǔ)庫(kù)作為你的數(shù)據(jù)的單點(diǎn)入口

          Dealing with data state

          考慮這個(gè)場(chǎng)景:你正在觀察一個(gè)由ViewModel暴露的LiveData,它包含一個(gè)要顯示的項(xiàng)目列表。視圖如何區(qū)分正在加載的數(shù)據(jù)、網(wǎng)絡(luò)錯(cuò)誤和一個(gè)空列表?

          你可以從ViewModel中暴露出一個(gè)LiveData 。例如,MyDataState可以包含關(guān)于數(shù)據(jù)是否正在加載、是否已經(jīng)成功加載或失敗的信息。

          img

          你可以把數(shù)據(jù)包裝在一個(gè)有狀態(tài)和其他元數(shù)據(jù)(如錯(cuò)誤信息)的類(lèi)中。參見(jiàn)我們樣本中的資源類(lèi):https://developer.android.com/jetpack/guide#addendum。

          • ?使用包裝器或另一個(gè)LiveData暴露你的數(shù)據(jù)的狀態(tài)信息。

          Saving activity state

          Activity狀態(tài)是你在一個(gè)Activity消失時(shí)重新創(chuàng)建屏幕所需要的信息,這意味著該Activity被破壞或進(jìn)程被殺死。旋轉(zhuǎn)是最常見(jiàn)的情況,我們已經(jīng)用ViewModels覆蓋了這種情況。所以,狀態(tài)被保存在ViewModel中是安全的。

          然而,你可能需要在ViewModels也消失的其他情況下恢復(fù)狀態(tài):例如,當(dāng)操作系統(tǒng)資源不足并殺死了你的進(jìn)程時(shí)。

          為了有效地保存和恢復(fù)UI狀態(tài),可以使用持久性、onSaveInstanceState()和ViewModels的組合。

          對(duì)于一個(gè)例子,請(qǐng)看。ViewModels: 持久性、onSaveInstanceState()、恢復(fù)UI狀態(tài)和加載器

          https://medium.com/androiddevelopers/viewmodels-persistence-onsaveinstancestate-restoring-ui-state-and-loaders-fc7cc4a6c090

          Events

          事件是發(fā)生一次的事情。ViewModels暴露了數(shù)據(jù),但事件呢?例如,導(dǎo)航事件或顯示Snackbar信息是只應(yīng)執(zhí)行一次的動(dòng)作。

          事件的概念與LiveData存儲(chǔ)和恢復(fù)數(shù)據(jù)的方式并不完全相符??紤]一個(gè)有以下字段的ViewModel。

          LiveData<String> snackbarMessage = new MutableLiveData<>();

          一個(gè)Activity開(kāi)始觀察這個(gè),ViewModel完成了一個(gè)操作,所以它需要更新消息。

          snackbarMessage.setValue("Item saved!");

          該Activity接收該值并顯示Snackbar。這顯然是有效的。

          然而,如果用戶(hù)旋轉(zhuǎn)手機(jī),新的Activity被創(chuàng)建并開(kāi)始觀察。當(dāng)LiveData觀察開(kāi)始時(shí),該Activity立即收到舊的值,這導(dǎo)致消息再次顯示出來(lái)。

          與其試圖用庫(kù)或架構(gòu)組件的擴(kuò)展來(lái)解決這個(gè)問(wèn)題,不如將其作為一個(gè)設(shè)計(jì)問(wèn)題來(lái)面對(duì)。我們建議你把你的事件作為你的狀態(tài)的一部分。

          • ?將事件設(shè)計(jì)成你的狀態(tài)的一部分。更多細(xì)節(jié)請(qǐng)閱讀LiveData與SnackBar、Navigation和其他事件(SingleLiveEvent案例)。

          Leaking ViewModels

          反應(yīng)式范式在Android中運(yùn)行良好,因?yàn)樗试S在UI和你的應(yīng)用程序的其他層之間建立一個(gè)方便的連接。LiveData是這個(gè)結(jié)構(gòu)的關(guān)鍵組件,所以通常你的Activity和Fragment會(huì)觀察LiveData實(shí)例。

          ViewModels如何與其他組件通信由你決定,但要注意泄漏和邊緣情況??紤]一下這個(gè)圖,視圖層使用觀察者模式,數(shù)據(jù)層使用回調(diào)。

          img

          如果用戶(hù)退出了應(yīng)用程序,視圖就會(huì)消失,所以ViewModel就不會(huì)再被觀察。如果repository是一個(gè)單例或其他范圍的應(yīng)用程序,repository將不會(huì)被銷(xiāo)毀,直到進(jìn)程被殺死。這只會(huì)在系統(tǒng)需要資源或用戶(hù)手動(dòng)殺死應(yīng)用程序時(shí)發(fā)生。如果repository持有對(duì)ViewModel中回調(diào)的引用,ViewModel將被暫時(shí)泄露。

          img

          如果ViewModel是輕量級(jí)的,或者操作被保證快速完成,這種泄漏就不是什么大問(wèn)題。然而,情況并不總是這樣的。理想情況下,只要沒(méi)有任何視圖在觀察它們,ViewModel就應(yīng)該是自由的。

          img

          你有很多選擇來(lái)實(shí)現(xiàn)這一點(diǎn)。

          • 通過(guò)ViewModel.onCleared()你可以告訴repository放棄對(duì)ViewModel的回調(diào)。
          • 在repository中,你可以使用WeakReference,也可以使用事件總線(xiàn)(兩者都容易被濫用,甚至被認(rèn)為是有害的)。
          • 使用LiveData在存儲(chǔ)庫(kù)和ViewModel之間進(jìn)行通信,其方式類(lèi)似于在View和ViewModel之間使用LiveData。

          這點(diǎn)用Flow也可以解決。

          • ?考慮邊緣情況、泄漏以及長(zhǎng)期運(yùn)行的操作會(huì)如何影響你架構(gòu)中的實(shí)例。
          • ? 不要在ViewModel中放置對(duì)保存清潔狀態(tài)或與數(shù)據(jù)有關(guān)的邏輯。你從ViewModel進(jìn)行的任何調(diào)用都可能是最后一次。

          LiveData in repositories

          為了避免泄露ViewModels和回調(diào)地獄,可以像這樣觀察存儲(chǔ)庫(kù)。

          img

          當(dāng)ViewModel被清除或視圖的生命周期結(jié)束時(shí),訂閱被清除。

          img

          如果你嘗試這種方法,會(huì)有一個(gè)問(wèn)題:如果你不能訪問(wèn)LifecycleOwner,你如何從ViewModel訂閱Repository?使用Transformations是解決這個(gè)問(wèn)題的一個(gè)非常方便的方法。Transformations.switchMap讓你創(chuàng)建一個(gè)新的LiveData,對(duì)其他LiveData實(shí)例的變化做出反應(yīng)。它還允許在整個(gè)鏈條上攜帶觀察者的生命周期信息。

          LiveData<Repo> repo = Transformations.switchMap(repoIdLiveData, repoId -> {
                  if (repoId.isEmpty()) {
                      return AbsentLiveData.create();
                  }
                  return repository.loadRepo(repoId);
              }
          );

          在這個(gè)例子中,當(dāng)觸發(fā)器得到更新時(shí),該函數(shù)被應(yīng)用,結(jié)果被派發(fā)到下游。一個(gè)Activity將觀察repo,同樣的LifecycleOwner將被用于repository.loadRepo(id)調(diào)用。

          只要你認(rèn)為你在ViewModel中需要一個(gè)Lifecycle對(duì)象,一個(gè)Transformation可能就是解決方案。

          Extending LiveData

          LiveData最常見(jiàn)的用例是在ViewModels中使用MutableLiveData,并將它們作為L(zhǎng)iveData公開(kāi),使它們從觀察者那里不可改變。

          如果你需要更多的功能,擴(kuò)展LiveData會(huì)讓你知道什么時(shí)候有活躍的觀察者。例如,當(dāng)你想開(kāi)始監(jiān)聽(tīng)一個(gè)位置或傳感器服務(wù)時(shí),這很有用。

          public class MyLiveData extends LiveData<MyData> {

              public MyLiveData(Context context) {
                  // Initialize service
              }

              @Override
              protected void onActive() {
                  // Start listening
              }

              @Override
              protected void onInactive() {
                  // Stop listening
              }
          }

          When not to extend LiveData

          你也可以使用onActive()來(lái)啟動(dòng)一些加載數(shù)據(jù)的服務(wù),但除非你有很好的理由,否則你不需要等待LiveData的觀察。一些常見(jiàn)的模式。給ViewModel添加一個(gè)start()方法,并盡快調(diào)用它:https://github.com/android/architecture-samples/blob/dev-todo-mvvm-live/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskFragment.java#L64

          設(shè)置一個(gè)啟動(dòng)加載的屬性:https://github.com/android/architecture-components-samples/blob/master/GithubBrowserSample/app/src/main/java/com/android/example/github/ui/repo/RepoFragment.kt

          • ? 你通常不會(huì)擴(kuò)展LiveData。讓你的Activity或Fragment告訴ViewModel何時(shí)開(kāi)始加載數(shù)據(jù)。

          原文鏈接:https://medium.com/androiddevelopers/viewmodels-and-livedata-patterns-antipatterns-21efaef74a54

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

          專(zhuān)注 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è)“三連”支持一下??


          瀏覽 67
          點(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>
                  极品AV| 免费啊啊啊黄色视频 | 人人干在线视频 | 国产精品怕怕怕 | 91视频你懂的 |