欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android ViewModel與Lifecycles和LiveData組件用法詳細(xì)講解

 更新時間:2023年01月19日 09:24:29   作者:Mr YiRan  
JetPack是一個開發(fā)組件工具集,他的主要目的是幫助我們編寫出更加簡潔的代碼,并簡化我們的開發(fā)過程。JetPack中的組件有一個特點,它們大部分不依賴于任何Android系統(tǒng)版本,這意味者這些組件通常是定義在AndroidX庫當(dāng)中的,并且擁有非常好的向下兼容性

一、ViewModel

ViewModel 類旨在以注重生命周期的方式存儲和管理界面相關(guān)的數(shù)據(jù)。

ViewModel 類讓數(shù)據(jù)可在發(fā)生屏幕旋轉(zhuǎn)等配置更改后繼續(xù)留存。

簡單的說就是,在android中,當(dāng)Activity重建或銷毀時,頁面上的數(shù)據(jù)會丟失。為了保存頁面的數(shù)據(jù),我們以前通常的做法是在 onSaveInstanceState 中,將數(shù)據(jù)保存到 bundle 中,再在 onCreate 中將 bundle 中的數(shù)據(jù)取出來。

而使用 ViewModel,我們就無需再用這種方法保存,因為 ViewModel 會自動感知生命周期,處理數(shù)據(jù)的保存與恢復(fù)。即數(shù)據(jù)可在發(fā)生屏幕旋轉(zhuǎn)等配置(其它例如分辨率調(diào)整、權(quán)限變更、系統(tǒng)字體樣式、語言變更等)更改后繼續(xù)留存。

對于橫豎屏生命周期的總結(jié)是:先銷毀掉原來的生命周期,然后再重新跑一次。

但是,這樣子是不是會有問題呢?有些場景下: 比如說,做游戲開發(fā) 。橫豎屏的切換,生命周期重新加載,那么當(dāng)前頁面的數(shù)據(jù)也會重新開始了。但是ViewModel會保存里面的數(shù)據(jù)。

在切換語言的時候ViewModel也會保存數(shù)據(jù)

Activity等視圖文件中不保存數(shù)據(jù),在ViewModel里面保存數(shù)據(jù)

當(dāng)Activity或fragment被Destory或onCreate時ViewModel數(shù)據(jù)不會丟失

ViewModel基本用法

想要使用ViewModel組件,還需要添加如下依賴:

dependencies {
    //ViewModel是LifeCycle的一個組件庫,所以只需要添加LifeCycle庫的依賴即可
    implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
}

通常來講,我們需要給每個Activity和Fragment都創(chuàng)建一個對應(yīng)的ViewModel,因此為MainActivity創(chuàng)建一個對應(yīng)的MainViewModel類,并讓他繼承自ViewModel,代碼如下所示:

class MainViewModel :ViewModel(){
    var counter=0
}

現(xiàn)在我們在界面上添加一個按鈕,每點擊一次按鈕就讓計數(shù)器加1,并且把最新的計數(shù)顯示到界面上。修改布局代碼:

package com.example.JetPackTest
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import com.example.kotlintext.R
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
    lateinit var viewModel: MainViewModel
   private val TAG:String="MainActivity"
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.d(TAG, "onCreate: ")
        //viewModel= ViewModelProviders.of(this).get(MainViewModel::class.java) 
        viewModel=ViewModelProvider(this,ViewModelProvider.NewInstanceFactory()).get(MainViewModel::class.java)
        plusOneBtn.setOnClickListener {
             viewModel.counter++;
            refreshCounter()
        }
        refreshCounter()
    }
    private fun refreshCounter() {
        infoText.text=viewModel.counter.toString()
    }
    override fun onDestroy() {
        super.onDestroy()
        Log.d(TAG, "onDestroy: ")
    }
    override fun onStart() {
        super.onStart()
        Log.d(TAG, "onStart: ")
    }
    override fun onStop() {
        super.onStop()
        Log.d(TAG, "onStop: ")
    }
    override fun onRestart() {
        super.onRestart()
        Log.d(TAG, "onRestart: ")
    }
    override fun onPause() {
        super.onPause()
        Log.d(TAG, "onPause: ")
    }
}

首先我們要通過ViewModelProvider來創(chuàng)建ViewModel的實例,之所以這么寫是ViewModel有獨立的生命周期,并且其生命周期要長于Activity。如果我們在onCreate()方法中創(chuàng)建ViewModel的實例,那么每次onCreate()方法執(zhí)行時候,ViewModel都會創(chuàng)建一個新的實例,這樣當(dāng)手機屏幕發(fā)生變化時候,就無法保留其中的數(shù)據(jù)了。

當(dāng)我們旋轉(zhuǎn)一下屏幕,你會發(fā)現(xiàn)Activity雖然重新被創(chuàng)建了,但計數(shù)器的數(shù)據(jù)沒有丟失

向ViewModel傳遞參數(shù)

如果我們需要通過構(gòu)造函數(shù)來傳遞一些參數(shù),需要借助ViewModelProvider.Factory就可以實現(xiàn)。雖然計數(shù)器在屏幕旋轉(zhuǎn)的時候不會丟失數(shù)據(jù),但是如果退出程序之后又重新打開,那么之前的計數(shù)就會被清零。這個時候我們就需要在退出程序的時候?qū)Ξ?dāng)前的計數(shù)進行保存,然后在重新打開程序的時候讀取之前保存的計數(shù),并傳遞給MainViewModel。修改MainViewModel中的代碼,如下所示

class MainViewModel(countReserved:Int) :ViewModel(){
    var counter=countReserved
}

我們通過給MainViewModel的構(gòu)造函數(shù)添加了一個countReserved參數(shù),這個參數(shù)用于記錄之前保存的計數(shù)值,并在初始化的時候賦值給counter變量。

新建一個MainViewModelFactory類,并讓它實現(xiàn)ViewModelProvider.Factory接口,代碼如下:

class MainViewModelFactory(private val countReserved:Int):ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return MainViewModel(countReserved)as T
    }
}

可以看到MainViewModel.Factory的構(gòu)造函數(shù)中也接收了一個countReserved參數(shù),另外ViewModelProvider.Factory接口要求我們必須實現(xiàn)create()方法,因此這里在create方法中我們創(chuàng)建了MainViewModel的實例,并將countReserved參數(shù)傳了進去。為什么這里就可以創(chuàng)建MainViewModel的實例了呢?因為create()方法的執(zhí)行時機和Activity的生命周期無關(guān),所以不會產(chǎn)生之前提到的問題。

另外,我們在界面上添加一個清零按鈕,方便用戶手動將計數(shù)器清零。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
>
 <TextView
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:id="@+id/infoText"
     android:layout_gravity="center_horizontal"
     android:textSize="32sp"
     />
    <Button
        android:id="@+id/plusOneBtn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="Plus One"
        />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/clearBtn"
        android:layout_gravity="center_horizontal"
        android:text="clear"
        />
</LinearLayout>

最后修改MainActivity中的代碼

package com.example.JetPackTest
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.edit
import androidx.lifecycle.ViewModelProvider
import com.example.kotlintext.R
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
    lateinit var viewModel: MainViewModel
    lateinit var sp:SharedPreferences
   private val TAG:String="MainActivity"
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.d(TAG, "onCreate: ")
        sp=getPreferences(Context.MODE_PRIVATE)
        val countReserved = sp.getInt("count_reserved", 0)
viewModel=ViewModelProvider(this,MainViewModelFactory(countReserved)).get(MainViewModel::class.java)
        plusOneBtn.setOnClickListener {
             viewModel.counter++;
            refreshCounter()
        }
        clearBtn.setOnClickListener {
            viewModel.counter=0
            refreshCounter()
        }
        refreshCounter()
    }
    private fun refreshCounter() {
        infoText.text=viewModel.counter.toString()
    }
    override fun onDestroy() {
        super.onDestroy()
        Log.d(TAG, "onDestroy: ")
    }
    override fun onStart() {
        super.onStart()
        Log.d(TAG, "onStart: ")
    }
    override fun onStop() {
        super.onStop()
        Log.d(TAG, "onStop: ")
    }
    override fun onRestart() {
        super.onRestart()
        Log.d(TAG, "onRestart: ")
    }
    override fun onPause() {
        super.onPause()
        Log.d(TAG, "onPause: ")
        sp.edit {
            putInt("count_reserved",viewModel.counter)
        }
    }
}

在onCreate()方法中,我們首先獲取了SharedPreferences的實例,然后讀取之前保存的計數(shù)值,如果沒有讀到的話,就使用0作為默認(rèn)值。接下來在ViewModelProvider方法傳入MainViewModelFactory(countReserved)作為參數(shù),將讀取到的計數(shù)值傳給了MainViewModelFactory的構(gòu)造函數(shù)。

并在onPause()方法中對當(dāng)前的計數(shù)進行保存,這樣可以保證不管程序是退出還是進入后臺,計數(shù)都不會丟失。

二、Lifecycles

在編寫Android應(yīng)用程序的時候,可能經(jīng)常遇到需要感知Activity生命周期的情況。比如,某個頁面中發(fā)起了一條網(wǎng)絡(luò)請求,但是當(dāng)請求得到響應(yīng)的時候,界面或許已經(jīng)關(guān)閉了,這個時候就不應(yīng)該繼續(xù)對響應(yīng)的結(jié)果進行處理。因此我們需要能夠時刻感知到Activity的生命周期,以便在適當(dāng)?shù)臅r候進行相應(yīng)的邏輯控制。

比如有個問題,如果要在一個非Activity的類中去感知Activity的聲明周期,應(yīng)該怎么辦?

可以通過在Activity中嵌入一個隱藏的Fragment來進行感知,或者通過手寫監(jiān)聽器的方式來進行感知。

下面通過監(jiān)聽器的方式來對Activity的生命周期進行感知

class MyObserver{
fun activityStart(){
}
fun activityStop(){
}
}
class MainActivity:AppCompatActivity(){
lateinit var observer:MyObserver
override fun onCreate(savedInstanceState:Bundle?){
observer=MyObserver()
}
override fun onStart(){
super.onStart()
observer.activityStart()
}
override fun onStop(){
super.onStop()
observer.activityStop()
}
}

這里我們?yōu)榱俗孧yObserver能夠感知到Activity的生命周期,需要專門在MainActivity中重寫相應(yīng)的生命周期方法,然后再通知給MyObserver。這種實現(xiàn)方式需要在Activity中編寫太多額外的邏輯。

而Lifecycles組件就可以在任何一個類中都能輕松感知到Activity的生命周期,同時又不需要在Activity中編寫大量的邏輯處理。

新建一個MyObserver類,并讓它實現(xiàn)LifecycleObserver接口,代碼如下所示:

class MyObserver:LifecycleObserver {
    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun  activityStart(){
        Log.d("MyObserver", "activityStart")
    }
    
    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun activityStop(){
        Log.d("MyObserver", "activityStop")
    }
}

可以看到,我們在方法上使用了@OnLifecycleEvent注解,并傳入了一種生命周期事件。生命周期事件的類型一共有7種:ON_CREATE、ON_START、ON_STOP、ON_RESUEM、ON_DESTORY分別匹配Activity中相應(yīng)的聲明周期回調(diào)。另外還有一種ON_ANY類型,表示可以匹配Activity的任何生命周期回調(diào)。

因此,上述代碼中的activityStart()和activityStop()方法就應(yīng)該分別在Activity的onStart()和onStop()觸發(fā)的時候執(zhí)行。

接下來借助LifecycleOwner,可以使用如下的語法結(jié)構(gòu)讓MyObserver得到通知:

lifecycleOwner.lifecycle.addObserver(MyObserver)

首先調(diào)用lifecycleOwner的getLifecycle()方法,得到一個Lifecycle對象,然后調(diào)用它的addObserver()方法來觀察LifecyclerOwner的生命周期,再把MyObserver的實例傳進去就可以了。

又因為我們的Activity是繼承自AppCompatActivity的,或者Fragment繼承自androidx.fragment.app.Fragment,他們本身就是一個LifecycleOwner的實例,這部分工作AndroidX庫自動幫我們完成。所以可以這么寫

class MainActivity : AppCompatActivity() {
  private val TAG:String="MainActivity"
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        lifecycle.addObserver(MyObserver())
    }
    }

加上這一行,MyObserver就能自動感知到Activity的生命周期了。不僅在Activity適用,F(xiàn)ragment也適用。

運行程序,然后切到后臺,再回來的打印輸出

當(dāng)然MyObserver除了感知Activity的生命周期發(fā)生變化,也能夠獲知當(dāng)前的生命周期狀態(tài)。只需要在MyObserver的構(gòu)造函數(shù)中將Lifecycle對象傳進來,如下所示:

class MyObserver(val lifecycle:Lifecycle):LifecycleObserver{
}

有了lifecycle對象之后,我們就可以在任何地方調(diào)用lifecycle.currentState來主動獲知當(dāng)前的生命周期狀態(tài)。lifecycle.currentState返回的生命周期狀態(tài)是一個枚舉類型,一共有DESTROYED,INITIALIZED,CREATED,STARTED,RESUMED這五種狀態(tài)。

也就是說,當(dāng)獲取的生命周期狀態(tài)是CREATED的時候,說明onCreate()方法已經(jīng)執(zhí)行了,但是onStart()方法還沒有執(zhí)行。當(dāng)獲取的生命周期狀態(tài)是STARTED的時候,說明onStart()方法已經(jīng)執(zhí)行了,但是onResume()方法還沒有執(zhí)行。

三、LiveData

LiveData是Jetpack提供的一種響應(yīng)式編程組件,它可以包含任何類型的數(shù)據(jù),并在數(shù)據(jù)發(fā)生變化的時候通知給觀察者。

LiveData的基本用法

之前編寫的計數(shù)器雖然功能簡單,但還是有問題。當(dāng)點擊Plus One按鈕時,都會先給ViewModel中的計數(shù)加1,然后立即獲取最新的計數(shù)。這種方式雖然可以在單線程中正常工作,但如果ViewModel的內(nèi)部開啟了線程去執(zhí)行一些耗時邏輯,那么在點擊按鈕后就立即去獲取最新的數(shù)據(jù),得到的肯定還是之前的數(shù)據(jù)。

之前我們使用都是在Activity中手動獲取ViewModel中的數(shù)據(jù)這種交互方式,但是ViewModel卻無法將數(shù)據(jù)的變化主動通知給Activity。

或許你會把Activity的實例傳給ViewModel,這樣ViewModel不就能主動對Activity進行通知了嗎?但是要知道ViewModel的生命周期是長于Activity的,如果把Activity的實例傳給ViewModel,就很有可能就因為Activity無法釋放而造成內(nèi)存泄露。

如果我們將計數(shù)器的計數(shù)使用LiveData來包裝,然后在Activity中去觀察它,就可以主動將數(shù)據(jù)變化通知給Activity了。

修改MainViewModel中的代碼,如下所示:

class MainViewModel(countReserved:Int) :ViewModel(){
    var counter=MutableLiveData<Int>()
    init {
        counter.value=countReserved
    }
    fun plusOne(){
        val count=counter.value?:0
        counter.value=count+1
    }
    fun clear(){
        counter.value=0
    }
}

這里我們將counter變量修改成了一個MutableLiveData對象,并指定它的泛型為Int,表示它包含的是整型數(shù)據(jù)。MutableLiveData是一種可變的LiveData,用法很簡單,主要有3種讀寫數(shù)據(jù)的方法,分別是getValue()、setValue()和postValue()方法。getValue()方法用于獲取LiveData中包含的數(shù)據(jù);setValue()方法用于給LiveData設(shè)置數(shù)據(jù),但是只能在主線程中調(diào)用;postValue()方法用于在非主線程中給LiveData設(shè)置數(shù)據(jù)。

這里在init結(jié)構(gòu)體中給counter設(shè)置數(shù)據(jù),這樣之前保存的計數(shù)值劇可以在初始化的時候得到恢復(fù)。接下來新增了plusOne()和clear()這兩個方法,分別用于給計數(shù)加1以及將計數(shù)清零。plusOne()方法中的邏輯是先獲取counter中包含的數(shù)據(jù),然后給它加1,再重新設(shè)置到counter中。調(diào)用LiveData的getValue()方法獲得的數(shù)據(jù)是可能為空的,因此這里使用了一個?:操作符,當(dāng)獲取到的數(shù)據(jù)為空時,就用0來作為默認(rèn)計數(shù)。

修改MainActivity

class MainActivity : AppCompatActivity() {
    //對變量進行延遲初始化,這樣在就不用先給全局變量賦值,而且在賦值的時候賦值為null,后面還要進行判空
    // 如果變量多會比較麻煩
  lateinit var viewModel: MainViewModel
    lateinit var sp: SharedPreferences
  private val TAG:String="MainActivity"
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
       sp=getPreferences(Context.MODE_PRIVATE)
      val countReserved = sp.getInt("count_reserved", 0)
      viewModel= ViewModelProvider(this,MainViewModelFactory(countReserved)).get(MainViewModel::class.java)
        plusOneBtn.setOnClickListener {
        viewModel.plusOne()
        }
        clearBtn.setOnClickListener {
        viewModel.clear()
        }
        viewModel.counter.observe(this, Observer { count->
            infoText.text=count.toString()
        })
}
    override fun onPause() {
        super.onPause()
        Log.d(TAG, "onPause: ")
        sp.edit {
            putInt("count_reserved",viewModel.counter.value?:0)
        }
    }
}

這里調(diào)用了ViewModel.counter的observe()方法來觀察數(shù)據(jù)的變化。經(jīng)過對MainViewModel的改造,現(xiàn)在counter變量已經(jīng)變成了一個LiveData對象,任何LiveData對象都可以調(diào)用它的observe()方法來觀察數(shù)據(jù)的變化。observe()方法接口接收兩個參數(shù),第一個是一個LifecycleOwner對象,Activity本身就是一個LifecycleOwner對象,因此直接傳this就好;第二個參數(shù)是一個Observer接口,當(dāng)counter中包含的數(shù)據(jù)發(fā)生變化時,就會回調(diào)這里,因此這里將最新的數(shù)據(jù)更新到界面。

為了在非ViewModel中就只能觀察LiveData的數(shù)據(jù)變化,而不能給LiveData設(shè)置數(shù)據(jù),下面改造MainViewModel:

class MainViewModel(countReserved:Int) :ViewModel(){
    val counter:LiveData<Int> get() = _counter
    private val _counter=MutableLiveData<Int>()
    init {
        _counter.value=countReserved
    }
    fun plusOne(){
        val count=_counter.value?:0
        _counter.value=count+1
    }
    fun clear(){
        _counter.value=0
    }
}

將原來的counter變量改名為_counter變量,并給它加上private修飾符,這樣_counter變量就對外部不可見了。然后又定義了一個counter變量,將它的類型聲明為不可變的LiveData,并在它的get()屬性方法中返回_counter變量。

這樣,當(dāng)外部調(diào)用counter變量時,實際上獲得的就是_counter的實例,但是無法給counter設(shè)置數(shù)據(jù),從而保證了ViewModel的數(shù)據(jù)封裝性。

map和switchMap

LiveData為了能夠應(yīng)對各種不同的需求場景,提供了兩種轉(zhuǎn)換方法:map()和switchMap()方法。

map()方法,這個方法的作用是將實際包含數(shù)據(jù)的LiveData和僅用于觀察數(shù)據(jù)的LiveData進行轉(zhuǎn)換。

那么什么情況下,會用到這個方法呢?

比如說有個User類,User類包含用戶的姓名和年齡,定義如下:

data class User(var firstName:String,var lastName:String,var age:Int)

我們可以在ViewModel中創(chuàng)建一個相應(yīng)的LiveData來包含User類型的數(shù)據(jù),如下所示:

class MainViewModel(countReserved:Int):ViewModel(){
val userLiveData=MutableLiveData<User>()
}

如果MainActivity中明確只會顯示用戶的姓名,而完全不關(guān)心用戶的年齡,這個時候還將User類型的LiveData暴露給外部就不合適了。

而map()方法就是專門解決這個問題,它可以將User類型的LiveData自由地轉(zhuǎn)型成任意其他類型的LiveData

class MainViewModel(countReserved:Int):ViewModel(){
private val userLiveData=MutableLiveData<User>()
val userName:LiveData<String> =Transformations.map(userLiveData){user->
"${user.firstName}${user.lastName}"
}
...
}

這里我們調(diào)用了Transformations的map()方法來對LiveData的數(shù)據(jù)類型進行轉(zhuǎn)換。map()方法接收兩個參數(shù):第一個參數(shù)是原始的LiveData對象;第二個參數(shù)是一個轉(zhuǎn)換函數(shù),我們在轉(zhuǎn)換函數(shù)中編寫具體的轉(zhuǎn)換邏輯即可。這里的邏輯就是將user對象轉(zhuǎn)換為一個只包含用戶姓名的字符串。

另外還將userLiveData聲明成了private,以保證數(shù)據(jù)的封裝性。外部使用的時候只要觀察userName這個LiveData就可以了。當(dāng)userLiveData的數(shù)據(jù)發(fā)生變化時,map()方法會監(jiān)聽到變化并執(zhí)行轉(zhuǎn)換函數(shù)的邏輯,然后再將轉(zhuǎn)換之后的數(shù)據(jù)通知給userName的觀察者。

switchMap()方法的使用場景比較固定:如果ViewModel中的某個LiveData對象是調(diào)用另外的方法獲取的,那么我們就可以借助switchMap()方法,將這個LiveData對象轉(zhuǎn)換成另一個可觀察的LiveData對象。

比如:LiveData對象的實例都是在ViewModel中創(chuàng)建的,然而在實際的項目中,不可能一直都是這種理想情況,很有可能ViewModel中的某個LiveData對象是調(diào)用另外的方法獲取的。

新建一個Repository單例類,代碼如下所示:

object  Repository {
    fun getUser(userId:String):LiveData<User>{
        val liveData=MutableLiveData<User>()
        liveData.value=User(userId,userId,0)
        return liveData
    }
}

這里在Repository類中添加了一個getUser()方法,這個方法接收一個userId參數(shù)。每次將傳入的userId當(dāng)做用戶姓名來創(chuàng)建一個新的User對象。

getUser()方法返回的是一個包含User數(shù)據(jù)的LiveData對象,而且每次調(diào)用getUser()方法都會返回一個新的LiveData實例。

然后再MainViewModel中也定義一個getUser()方法,并且讓它調(diào)用Repository的getUser()方法來獲取LiveData對象:

class MainViewModel(countReserved:Int) :ViewModel(){
   fun getUser(userId:String):LiveData<User>{
        return Repository.getUser(userId)
    }    
}

接下來的問題是,在Activity中如何觀察LiveData的數(shù)據(jù)變化呢?既然getUser()方法返回的是一個LiveData對象,那么我們可不可以直接在Activity中使用如下寫法呢?

viewModel.getUser(userId).observe(this)
{user->
}

因為每次調(diào)用getUser()返回的都是一個新的LiveData實例,而上述寫法會一直觀察老的LiveData實例,從而根本無法觀察到數(shù)據(jù)的變化,會發(fā)現(xiàn)這種情況下LiveData是不可觀察的。

這個時候switchMap()方法就可以派上用場了。

修改MainViewModel中的代碼:

class MainViewModel(countReserved:Int) :ViewModel(){
    private val userIdLiveData=MutableLiveData<String>()
    val user:LiveData<User> =Transformations.switchMap(userIdLiveData){
        userId ->  Repository.getUser(userId)//此時的userId就是userIdLiveData的類型對象String
    }
    fun getUser(userId:String){
        userIdLiveData.value=userId
    }
}

定義了一個新的userIdLiveDat對象,用來觀察userId的數(shù)據(jù)變化,然后調(diào)用了Transformations的switchMap()方法,用來對另一個可觀察的LiveData對象進行轉(zhuǎn)換。

switchMap()方法同樣接收兩個參數(shù):第一個參數(shù)傳入我們新增的userIdLiveData,switchMap()方法會對它進行觀察;第二個參數(shù)是一個轉(zhuǎn)換函數(shù),注意:我們必須在這個轉(zhuǎn)換函數(shù)中返回一個LiveData對象,因為switchMap()方法的工作原理就是將轉(zhuǎn)換函數(shù)中返回LiveData對象轉(zhuǎn)換為另一個可觀察的LiveData對象。我們只需要在轉(zhuǎn)換函數(shù)中調(diào)用Respository的getUser()方法來得到LiveData對象,將其返回。

首先,當(dāng)外部調(diào)用MainViewModel的getUser()方法來獲取用戶數(shù)據(jù)時,并不會發(fā)起任何請求或者函數(shù)調(diào)用,只會傳入userId的值設(shè)置到userIdLiveData中。一旦userIdLiveData的數(shù)據(jù)發(fā)生變化,那么觀察userIdLiveData的switchMap()方法就會執(zhí)行,并且調(diào)用我們編寫的轉(zhuǎn)換函數(shù)。然后在轉(zhuǎn)換函數(shù)中調(diào)用Repository.getUser()方法獲取真正的用戶數(shù)據(jù)。同時,switchMap()方法會將Repository.getUser()方法返回的LiveData對象轉(zhuǎn)換成一個可觀察的LiveData對象。對于Activity只需要觀察這個LiveData對象就可以了。

修改activity_main.xml文件,新增一個Get User按鈕

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/getUserBtn"
        android:layout_gravity="center_horizontal"
        android:text="Get User"
        />
</LinearLayout>

修改MainActivity的代碼:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        viewModel= ViewModelProvider(this,MainViewModelFactory(countReserved)).get(MainViewModel::class.java)
        getUserBtn.setOnClickListener {
            val userId=(0..10000).random().toString()
            viewModel.getUser(userId)
        }
        viewModel.user.observe(this, Observer { user ->
            infoText.text=user.firstName
        })
    }
}

通過Get User按鈕的點擊事件中使用隨機函數(shù)生成一個userId,然后調(diào)用MainViewModel的getUser()方法來獲取用戶數(shù)據(jù),但是這個方法不會有返回值。等數(shù)據(jù)獲取完后,可觀察LiveData對象的observe()方法將會得到通知,我們在這里將獲取的用戶名顯示到界面上。

LiveData內(nèi)部不會判斷即將設(shè)置的數(shù)據(jù)和原有數(shù)據(jù)是否相同,只是調(diào)用了setValue()或postValue()方法,就一定會觸發(fā)數(shù)據(jù)變化事件。

如果Activity處于不可見狀態(tài)的時候(手機息屏,或者被其他的Activity遮擋),LiveData發(fā)生了多次數(shù)據(jù)變化,當(dāng)Activity恢復(fù)可見狀態(tài)時,只有最新的那份數(shù)據(jù)才會通知給觀察者,前面的數(shù)據(jù)在這種情況下相當(dāng)于已經(jīng)過期了,會被直接丟棄。

到此這篇關(guān)于Android ViewModel與Lifecycles和LiveData組件用法詳細(xì)講解的文章就介紹到這了,更多相關(guān)Android ViewModel Lifecycles LiveData內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論