6-LiveData

LiveData

LiveData 是一種可觀察的數據存儲器類。與常規的可觀察類不同,LiveData 具有生命週期感知能力,意指它遵循其他應用組件(如 Activity、Fragment 或 Service)的生命週期。這種感知能力可確保 LiveData 僅更新處於活躍生命週期狀態的應用組件觀察者。

優勢

  1. 確保界面符合數據狀態

    ​ LiveData 遵循觀察者模式。當生命週期狀態發生變化時,LiveData 會通知 Observer 對象。您可以整合代碼以在這些 Observer 對象中更新界面。觀察者可以在每次發生更改時更新界面,而不是在每次應用數據發生更改時更新界面。

  2. 不會發生內存泄露

    ​ 觀察者會綁定到 Lifecycle 對象,並在其關聯的生命週期遭到銷燬後進行自我清理。

  3. 不會因 Activity 停止而導致崩潰

    ​ 如果觀察者的生命週期處於非活躍狀態(如返回棧中的 Activity),則它不會接收任何 LiveData 事件。

  4. 不再需要手動處理生命週期

    ​ 界面組件只是觀察相關數據,不會停止或恢復觀察。LiveData 將自動管理所有這些操作,因爲它在觀察時可以感知相關的生命週期狀態變化。

  5. 數據始終保持最新狀態

    ​ 如果生命週期變爲非活躍狀態,它會在再次變爲活躍狀態時接收最新的數據。例如,曾經在後臺的 Activity 會在返回前臺後立即接收最新的數據。

  6. 適當的配置更改

    ​ 如果由於配置更改(如設備旋轉)而重新創建了 Activity 或 Fragment,它會立即接收最新的可用數據。

  7. 共享資源

    ​ 您可以使用單一實例模式擴展 LiveData 對象以封裝系統服務,以便在應用中共享它們。LiveData 對象連接到系統服務一次,然後需要相應資源的任何觀察者只需觀察 LiveData 對象。有關詳情,請參閱擴展 LiveData

使用

請按照以下步驟使用 LiveData 對象:

  1. 創建 LiveData 實例以存儲某種類型的數據。這通常在 ViewModel 類中完成。
  2. 創建可定義 onChanged() 方法的 Observer 對象,該方法可以控制當 LiveData 對象存儲的數據更改時會發生什麼。通常情況下,您可以在界面控制器(如 Activity 或 Fragment)中創建 Observer 對象。
  3. 使用 [observe()](https://developer.android.com/reference/androidx/lifecycle/LiveData#observe(android.arch.lifecycle.LifecycleOwner, android.arch.lifecycle.Observer)) 方法將 Observer 對象附加到 LiveData 對象。observe() 方法會採用 LifecycleOwner 對象。這樣會使 Observer 對象訂閱 LiveData 對象,以使其收到有關更改的通知。通常情況下,您可以在界面控制器(如 Activity 或 Fragment)中附加 Observer 對象。

注意:您可以使用 observeForever(Observer) 方法來註冊未關聯 LifecycleOwner 對象的觀察者。在這種情況下,觀察者會被視爲始終處於活躍狀態,因此它始終會收到關於修改的通知。您可以通過調用 removeObserver(Observer) 方法來移除這些觀察者。

創建

LiveData 是一種可用於任何數據的封裝容器,其中包括可實現 Collections 的對象,如 ListLiveData 對象通常存儲在 ViewModel 對象中,並可通過 getter 方法進行訪問,如以下示例中所示:

    class NameViewModel : ViewModel() {

        // Create a LiveData with a String
        val currentName: MutableLiveData<String> by lazy {
            MutableLiveData<String>()
        }

        // Rest of the ViewModel...
    }
    

注意:請確保將用於更新界面的 LiveData 對象存儲在 ViewModel 對象中,而不是將其存儲在 Activity 或 Fragment 中,原因如下:

​ 避免 Activity 和 Fragment 過於龐大。現在,這些界面控制器負責顯示數據,但不負責存儲數據狀態。將 LiveData 實例與特定的 Activity 或 Fragment 實例分離開,並使 對象在配置更改後繼續存在。

觀察

在大多數情況下,應用組件的 onCreate() 方法是開始觀察 LiveData 對象的正確着手點,原因如下:

  • 確保系統不會從 Activity 或 Fragment 的 onResume() 方法進行冗餘調用。
  • 確保 Activity 或 Fragment 變爲活躍狀態後具有可以立即顯示的數據。一旦應用組件處於 STARTED 狀態,就會從它正在觀察的 LiveData 對象接收最新值。只有在設置了要觀察的 LiveData 對象時,纔會發生這種情況。

通常,LiveData 僅在數據發生更改時才發送更新,並且僅發送給活躍觀察者。

此行爲的一種例外情況是,觀察者從非活躍狀態更改爲活躍狀態時也會收到更新。

此外,如果觀察者第二次從非活躍狀態更改爲活躍狀態,則只有在自上次變爲活躍狀態以來值發生了更改時,它纔會收到更新。

    class NameActivity : AppCompatActivity() {

        private lateinit var model: NameViewModel

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)

            // Other code to setup the activity...

            // Get the ViewModel.
            model = ViewModelProviders.of(this).get(NameViewModel::class.java)

            // Create the observer which updates the UI.
            val nameObserver = Observer<String> { newName ->
                // Update the UI, in this case, a TextView.
                nameTextView.text = newName
            }

            // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
            model.currentName.observe(this, nameObserver)
        }
    }
    

在傳遞 nameObserver 參數的情況下調用 [observe()](https://developer.android.com/reference/androidx/lifecycle/LiveData#observe(android.arch.lifecycle.LifecycleOwner, android.arch.lifecycle.Observer)) 後,系統會立即調用 onChanged(),從而提供 mCurrentName 中存儲的最新值。 如果 LiveData 對象尚未在 mCurrentName 中設置值,則不會調用 onChanged()

更新

​ LiveData 沒有公開可用的方法來更新存儲的數據。MutableLiveData 類將公開 setValue(T)postValue(T) 方法,如果您需要修改存儲在 LiveData 對象中的值,則必須使用這些方法。通常情況下會在 ViewModel 中使用 MutableLiveData,然後 ViewModel 只會向觀察者公開不可變的 LiveData 對象。

​ 設置觀察者關係後,您可以更新 LiveData 對象的值(如以下示例中所示),這樣當用戶點按某個按鈕時會觸發所有觀察者:

    button.setOnClickListener {
        val anotherName = "John Doe"
        model.currentName.setValue(anotherName)
    }
    //在本例中調用 setValue(T) 導致觀察者使用值 John Doe 調用其 onChanged() 方法。

注意:您必須調用 setValue(T) 方法以從主線程更新 LiveData 對象。如果在 worker 線程中執行代碼,則您可以改用 postValue(T) 方法來更新 LiveData 對象。

將LiveData與Room一起使用

Room 持久性庫支持返回 LiveData 對象的可觀察查詢。可觀察查詢屬於數據庫訪問對象 (DAO) 的一部分。

當數據庫更新時,Room 會生成更新 LiveData 對象所需的所有代碼。在需要時,生成的代碼會在後臺線程上異步運行查詢。此模式有助於使界面中顯示的數據與存儲在數據庫中的數據保持同步。

將協程與LiveData一起使用

LiveData 支持 Kotlin 協程。

擴展

如果觀察者的生命週期處於 STARTEDRESUMED 狀態,則 LiveData 會認爲該觀察者處於活躍狀態。

以下示例代碼說明了如何擴展 LiveData 類:

    class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
        private val stockManager = StockManager(symbol)

        private val listener = { price: BigDecimal ->
            value = price
        }

        override fun onActive() {
            stockManager.requestPriceUpdates(listener)
        }

        override fun onInactive() {
            stockManager.removeUpdates(listener)
        }
    }
    

本例中的價格監聽器實現包括以下重要方法:

  • LiveData 對象具有活躍觀察者時,會調用 onActive() 方法。這意味着,您需要從此方法開始觀察股價更新。
  • LiveData 對象沒有任何活躍觀察者時,會調用 onInactive() 方法。由於沒有觀察者在監聽,因此沒有理由與 StockManager 服務保持連接。
  • setValue(T) 方法將更新 LiveData 實例的值,並將更改通知給任何活躍觀察者。

可以使用 StockLiveData 類,如下所示:

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        val myPriceListener: LiveData<BigDecimal> = ...
        myPriceListener.observe(this, Observer<BigDecimal> { price: BigDecimal? ->
            // Update the UI.
        })
    }
    

[observe()](https://developer.android.com/reference/androidx/lifecycle/LiveData#observe(android.arch.lifecycle.LifecycleOwner, android.arch.lifecycle.Observer)) 方法將傳遞 Fragment(它是 LifecycleOwner 的實例)作爲第一個參數。這樣做表示此觀察者已綁定到與所有者關聯的 Lifecycle 對象,這意味着:

  • 如果 Lifecycle 對象未處於活躍狀態,那麼即使值發生更改,也不會調用觀察者。
  • 銷燬 Lifecycle 對象後,會自動移除觀察者。

LiveData 對象具有生命週期感知能力,這一事實意味着您可以在多個 Activity、Fragment 和 Service 之間共享它們。爲使示例保持簡單,您可以將 LiveData 類實現爲單一實例,如下所示:

    class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
        private val stockManager: StockManager = StockManager(symbol)

        private val listener = { price: BigDecimal ->
            value = price
        }

        override fun onActive() {
            stockManager.requestPriceUpdates(listener)
        }

        override fun onInactive() {
            stockManager.removeUpdates(listener)
        }

        companion object {
            private lateinit var sInstance: StockLiveData

            @MainThread
            fun get(symbol: String): StockLiveData {
                sInstance = if (::sInstance.isInitialized) sInstance else StockLiveData(symbol)
                return sInstance
            }
        }
    }

並且您可以在 Fragment 中使用它,如下所示:

    class MyFragment : Fragment() {

        override fun onActivityCreated(savedInstanceState: Bundle?) {
            StockLiveData.get(symbol).observe(this, Observer<BigDecimal> { price: BigDecimal? ->
                // Update the UI.
            })

        }

多個 Fragment 和 Activity 可以觀察 MyPriceListener 實例。僅當一個或多個系統服務可見且處於活躍狀態時,LiveData 纔會連接到該服務。

轉換

​ 如果希望在將 LiveData 對象分派給觀察者之前對存儲在其中的值進行更改,或者您可能需要根據另一個實例的值返回不同的 LiveData 實例。Lifecycle 軟件包會提供 Transformations 類,該類包括可應對這些情況的輔助程序方法。

  • [Transformations.map()](https://developer.android.com/reference/androidx/lifecycle/Transformations#map(android.arch.lifecycle.LiveData, android.arch.core.util.Function))

    對存儲在 LiveData 對象中的值應用函數,並將結果傳播到下游。

        val userLiveData: LiveData<User> = UserLiveData()
        val userName: LiveData<String> = Transformations.map(userLiveData) {
            user -> "${user.name} ${user.lastName}"
        }
    
  • [Transformations.switchMap()](https://developer.android.com/reference/androidx/lifecycle/Transformations#switchMap(android.arch.lifecycle.LiveData, android.arch.core.util.Function>))

    map() 類似,對存儲在 LiveData 對象中的值應用函數,並將結果解封和分派到下游。傳遞給 switchMap() 的函數必須返回 LiveData 對象,如以下示例中所示:

      private fun getUser(id: String): LiveData {   ...  }  
    	val userId: LiveData = ...  
    	val user = Transformations.switchMap(userId) { id -> getUser(id) }  
    

​ 可以使用轉換方法在觀察者的生命週期內傳送信息。除非觀察者正在觀察返回的 LiveData 對象,否則不會計算轉換。因爲__轉換是以延遲的方式計算__,所以與生命週期相關的行爲會隱式傳遞下去,而不需要額外的顯式調用或依賴項。

如果您認爲 ViewModel 對象中需要有 Lifecycle 對象,那麼進行轉換或許是更好的解決方案。例如,假設您有一個界面組件,該組件接受地址並返回該地址的郵政編碼。您可以爲此組件實現簡單的 ViewModel,如以下示例代碼所示:

    class MyViewModel(private val repository: PostalCodeRepository) : ViewModel() {

        private fun getPostalCode(address: String): LiveData<String> {
            // 不要這麼做
            return repository.getPostCode(address)
        }
    }
    

​ UI組件需要在每次調用getPostalCode()的時候,從之前的LiveData對象中取消註冊,並註冊到新的實例。此外,如果UI組件被重新創建,它將觸發對repository.getPostCode()方法的另一次調用,而不是使用上次調用的結果。

​ 您也可以將郵政編碼查詢實現爲地址輸入的轉換,如以下示例中所示:

    class MyViewModel(private val repository: PostalCodeRepository) : ViewModel() {
        private val addressInput = MutableLiveData<String>()
        val postalCode: LiveData<String> = Transformations.switchMap(addressInput) {
                address -> repository.getPostCode(address) }

        private fun setInput(address: String) {
            addressInput.value = address
        }
    }

在這種情況下,postalCode 字段定義爲 addressInput 的轉換。只要您的應用具有與 postalCode 字段關聯的活躍觀察者,就會在 addressInput 發生更改時重新計算並檢索該字段的值。

此機制允許較低級別的應用創建以延遲的方式按需計算的 LiveData 對象。ViewModel 對象可以輕鬆獲取對 LiveData 對象的引用,然後在其基礎之上定義轉換規則。

創建新的轉換 MediatorLiveData

有十幾種不同的特定轉換在您的應用中可能很有用,但默認情況下不提供它們。要實現您自己的轉換,您可以使用 MediatorLiveData類,該類可以監聽其他 LiveData 對象並處理它們發出的事件。MediatorLiveData 正確地將其狀態傳播到源 LiveData 對象。要詳細瞭解此模式,請參閱 Transformations 類的參考文檔。

合併多個源

MediatorLiveData 是 LiveData 的子類,允許您合併多個 LiveData 源。只要任何原始的 LiveData 源對象發生更改,就會觸發 MediatorLiveData 對象的觀察者。

​ 例如,如果界面中有可以從本地數據庫或網絡更新的 LiveData 對象,則可以向 MediatorLiveData 對象添加以下源:

  • 與存儲在數據庫中的數據關聯的 LiveData 對象。

  • 與從網絡訪問的數據關聯的 LiveData 對象。

​ 您的 Activity 只需觀察 MediatorLiveData 對象即可從這兩個源接收更新。有關詳細示例,請參閱應用架構指南的附錄:公開網絡狀態部分。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章