Android ViewModel與Lifecycles和LiveData組件用法詳細講解
一、ViewModel
ViewModel 類旨在以注重生命周期的方式存儲和管理界面相關的數(shù)據(jù)。
ViewModel 類讓數(shù)據(jù)可在發(fā)生屏幕旋轉等配置更改后繼續(xù)留存。
簡單的說就是,在android中,當Activity重建或銷毀時,頁面上的數(shù)據(jù)會丟失。為了保存頁面的數(shù)據(jù),我們以前通常的做法是在 onSaveInstanceState 中,將數(shù)據(jù)保存到 bundle 中,再在 onCreate 中將 bundle 中的數(shù)據(jù)取出來。
而使用 ViewModel,我們就無需再用這種方法保存,因為 ViewModel 會自動感知生命周期,處理數(shù)據(jù)的保存與恢復。即數(shù)據(jù)可在發(fā)生屏幕旋轉等配置(其它例如分辨率調整、權限變更、系統(tǒng)字體樣式、語言變更等)更改后繼續(xù)留存。
對于橫豎屏生命周期的總結是:先銷毀掉原來的生命周期,然后再重新跑一次。
但是,這樣子是不是會有問題呢?有些場景下: 比如說,做游戲開發(fā) 。橫豎屏的切換,生命周期重新加載,那么當前頁面的數(shù)據(jù)也會重新開始了。但是ViewModel會保存里面的數(shù)據(jù)。
在切換語言的時候ViewModel也會保存數(shù)據(jù)
Activity等視圖文件中不保存數(shù)據(jù),在ViewModel里面保存數(shù)據(jù)
當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)建一個對應的ViewModel,因此為MainActivity創(chuà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)建一個新的實例,這樣當手機屏幕發(fā)生變化時候,就無法保留其中的數(shù)據(jù)了。



當我們旋轉一下屏幕,你會發(fā)現(xiàn)Activity雖然重新被創(chuàng)建了,但計數(shù)器的數(shù)據(jù)沒有丟失
向ViewModel傳遞參數(shù)
如果我們需要通過構造函數(shù)來傳遞一些參數(shù),需要借助ViewModelProvider.Factory就可以實現(xiàn)。雖然計數(shù)器在屏幕旋轉的時候不會丟失數(shù)據(jù),但是如果退出程序之后又重新打開,那么之前的計數(shù)就會被清零。這個時候我們就需要在退出程序的時候對當前的計數(shù)進行保存,然后在重新打開程序的時候讀取之前保存的計數(shù),并傳遞給MainViewModel。修改MainViewModel中的代碼,如下所示
class MainViewModel(countReserved:Int) :ViewModel(){
var counter=countReserved
}
我們通過給MainViewModel的構造函數(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的構造函數(shù)中也接收了一個countReserved參數(shù),另外ViewModelProvider.Factory接口要求我們必須實現(xiàn)create()方法,因此這里在create方法中我們創(chuàng)建了MainViewModel的實例,并將countReserved參數(shù)傳了進去。為什么這里就可以創(chuàng)建MainViewModel的實例了呢?因為create()方法的執(zhí)行時機和Activity的生命周期無關,所以不會產生之前提到的問題。
另外,我們在界面上添加一個清零按鈕,方便用戶手動將計數(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作為默認值。接下來在ViewModelProvider方法傳入MainViewModelFactory(countReserved)作為參數(shù),將讀取到的計數(shù)值傳給了MainViewModelFactory的構造函數(shù)。
并在onPause()方法中對當前的計數(shù)進行保存,這樣可以保證不管程序是退出還是進入后臺,計數(shù)都不會丟失。

二、Lifecycles
在編寫Android應用程序的時候,可能經常遇到需要感知Activity生命周期的情況。比如,某個頁面中發(fā)起了一條網絡請求,但是當請求得到響應的時候,界面或許已經關閉了,這個時候就不應該繼續(xù)對響應的結果進行處理。因此我們需要能夠時刻感知到Activity的生命周期,以便在適當?shù)臅r候進行相應的邏輯控制。
比如有個問題,如果要在一個非Activity的類中去感知Activity的聲明周期,應該怎么辦?
可以通過在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中重寫相應的生命周期方法,然后再通知給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中相應的聲明周期回調。另外還有一種ON_ANY類型,表示可以匹配Activity的任何生命周期回調。
因此,上述代碼中的activityStart()和activityStop()方法就應該分別在Activity的onStart()和onStop()觸發(fā)的時候執(zhí)行。
接下來借助LifecycleOwner,可以使用如下的語法結構讓MyObserver得到通知:
lifecycleOwner.lifecycle.addObserver(MyObserver)
首先調用lifecycleOwner的getLifecycle()方法,得到一個Lifecycle對象,然后調用它的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也適用。
運行程序,然后切到后臺,再回來的打印輸出

當然MyObserver除了感知Activity的生命周期發(fā)生變化,也能夠獲知當前的生命周期狀態(tài)。只需要在MyObserver的構造函數(shù)中將Lifecycle對象傳進來,如下所示:
class MyObserver(val lifecycle:Lifecycle):LifecycleObserver{
}
有了lifecycle對象之后,我們就可以在任何地方調用lifecycle.currentState來主動獲知當前的生命周期狀態(tài)。lifecycle.currentState返回的生命周期狀態(tài)是一個枚舉類型,一共有DESTROYED,INITIALIZED,CREATED,STARTED,RESUMED這五種狀態(tài)。
也就是說,當獲取的生命周期狀態(tài)是CREATED的時候,說明onCreate()方法已經執(zhí)行了,但是onStart()方法還沒有執(zhí)行。當獲取的生命周期狀態(tài)是STARTED的時候,說明onStart()方法已經執(zhí)行了,但是onResume()方法還沒有執(zhí)行。
三、LiveData
LiveData是Jetpack提供的一種響應式編程組件,它可以包含任何類型的數(shù)據(jù),并在數(shù)據(jù)發(fā)生變化的時候通知給觀察者。
LiveData的基本用法
之前編寫的計數(shù)器雖然功能簡單,但還是有問題。當點擊Plus One按鈕時,都會先給ViewModel中的計數(shù)加1,然后立即獲取最新的計數(shù)。這種方式雖然可以在單線程中正常工作,但如果ViewModel的內部開啟了線程去執(zhí)行一些耗時邏輯,那么在點擊按鈕后就立即去獲取最新的數(shù)據(jù),得到的肯定還是之前的數(shù)據(jù)。
之前我們使用都是在Activity中手動獲取ViewModel中的數(shù)據(jù)這種交互方式,但是ViewModel卻無法將數(shù)據(jù)的變化主動通知給Activity。
或許你會把Activity的實例傳給ViewModel,這樣ViewModel不就能主動對Activity進行通知了嗎?但是要知道ViewModel的生命周期是長于Activity的,如果把Activity的實例傳給ViewModel,就很有可能就因為Activity無法釋放而造成內存泄露。
如果我們將計數(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ù)據(jù),但是只能在主線程中調用;postValue()方法用于在非主線程中給LiveData設置數(shù)據(jù)。
這里在init結構體中給counter設置數(shù)據(jù),這樣之前保存的計數(shù)值劇可以在初始化的時候得到恢復。接下來新增了plusOne()和clear()這兩個方法,分別用于給計數(shù)加1以及將計數(shù)清零。plusOne()方法中的邏輯是先獲取counter中包含的數(shù)據(jù),然后給它加1,再重新設置到counter中。調用LiveData的getValue()方法獲得的數(shù)據(jù)是可能為空的,因此這里使用了一個?:操作符,當獲取到的數(shù)據(jù)為空時,就用0來作為默認計數(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)
}
}
}
這里調用了ViewModel.counter的observe()方法來觀察數(shù)據(jù)的變化。經過對MainViewModel的改造,現(xiàn)在counter變量已經變成了一個LiveData對象,任何LiveData對象都可以調用它的observe()方法來觀察數(shù)據(jù)的變化。observe()方法接口接收兩個參數(shù),第一個是一個LifecycleOwner對象,Activity本身就是一個LifecycleOwner對象,因此直接傳this就好;第二個參數(shù)是一個Observer接口,當counter中包含的數(shù)據(jù)發(fā)生變化時,就會回調這里,因此這里將最新的數(shù)據(jù)更新到界面。
為了在非ViewModel中就只能觀察LiveData的數(shù)據(jù)變化,而不能給LiveData設置數(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變量。
這樣,當外部調用counter變量時,實際上獲得的就是_counter的實例,但是無法給counter設置數(shù)據(jù),從而保證了ViewModel的數(shù)據(jù)封裝性。
map和switchMap
LiveData為了能夠應對各種不同的需求場景,提供了兩種轉換方法:map()和switchMap()方法。
map()方法,這個方法的作用是將實際包含數(shù)據(jù)的LiveData和僅用于觀察數(shù)據(jù)的LiveData進行轉換。
那么什么情況下,會用到這個方法呢?
比如說有個User類,User類包含用戶的姓名和年齡,定義如下:
data class User(var firstName:String,var lastName:String,var age:Int)
我們可以在ViewModel中創(chuàng)建一個相應的LiveData來包含User類型的數(shù)據(jù),如下所示:
class MainViewModel(countReserved:Int):ViewModel(){
val userLiveData=MutableLiveData<User>()
}
如果MainActivity中明確只會顯示用戶的姓名,而完全不關心用戶的年齡,這個時候還將User類型的LiveData暴露給外部就不合適了。
而map()方法就是專門解決這個問題,它可以將User類型的LiveData自由地轉型成任意其他類型的LiveData
class MainViewModel(countReserved:Int):ViewModel(){
private val userLiveData=MutableLiveData<User>()
val userName:LiveData<String> =Transformations.map(userLiveData){user->
"${user.firstName}${user.lastName}"
}
...
}
這里我們調用了Transformations的map()方法來對LiveData的數(shù)據(jù)類型進行轉換。map()方法接收兩個參數(shù):第一個參數(shù)是原始的LiveData對象;第二個參數(shù)是一個轉換函數(shù),我們在轉換函數(shù)中編寫具體的轉換邏輯即可。這里的邏輯就是將user對象轉換為一個只包含用戶姓名的字符串。
另外還將userLiveData聲明成了private,以保證數(shù)據(jù)的封裝性。外部使用的時候只要觀察userName這個LiveData就可以了。當userLiveData的數(shù)據(jù)發(fā)生變化時,map()方法會監(jiān)聽到變化并執(zhí)行轉換函數(shù)的邏輯,然后再將轉換之后的數(shù)據(jù)通知給userName的觀察者。
switchMap()方法的使用場景比較固定:如果ViewModel中的某個LiveData對象是調用另外的方法獲取的,那么我們就可以借助switchMap()方法,將這個LiveData對象轉換成另一個可觀察的LiveData對象。
比如:LiveData對象的實例都是在ViewModel中創(chuàng)建的,然而在實際的項目中,不可能一直都是這種理想情況,很有可能ViewModel中的某個LiveData對象是調用另外的方法獲取的。
新建一個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當做用戶姓名來創(chuàng)建一個新的User對象。
getUser()方法返回的是一個包含User數(shù)據(jù)的LiveData對象,而且每次調用getUser()方法都會返回一個新的LiveData實例。
然后再MainViewModel中也定義一個getUser()方法,并且讓它調用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->
}
因為每次調用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ù)變化,然后調用了Transformations的switchMap()方法,用來對另一個可觀察的LiveData對象進行轉換。
switchMap()方法同樣接收兩個參數(shù):第一個參數(shù)傳入我們新增的userIdLiveData,switchMap()方法會對它進行觀察;第二個參數(shù)是一個轉換函數(shù),注意:我們必須在這個轉換函數(shù)中返回一個LiveData對象,因為switchMap()方法的工作原理就是將轉換函數(shù)中返回LiveData對象轉換為另一個可觀察的LiveData對象。我們只需要在轉換函數(shù)中調用Respository的getUser()方法來得到LiveData對象,將其返回。
首先,當外部調用MainViewModel的getUser()方法來獲取用戶數(shù)據(jù)時,并不會發(fā)起任何請求或者函數(shù)調用,只會傳入userId的值設置到userIdLiveData中。一旦userIdLiveData的數(shù)據(jù)發(fā)生變化,那么觀察userIdLiveData的switchMap()方法就會執(zhí)行,并且調用我們編寫的轉換函數(shù)。然后在轉換函數(shù)中調用Repository.getUser()方法獲取真正的用戶數(shù)據(jù)。同時,switchMap()方法會將Repository.getUser()方法返回的LiveData對象轉換成一個可觀察的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,然后調用MainViewModel的getUser()方法來獲取用戶數(shù)據(jù),但是這個方法不會有返回值。等數(shù)據(jù)獲取完后,可觀察LiveData對象的observe()方法將會得到通知,我們在這里將獲取的用戶名顯示到界面上。

LiveData內部不會判斷即將設置的數(shù)據(jù)和原有數(shù)據(jù)是否相同,只是調用了setValue()或postValue()方法,就一定會觸發(fā)數(shù)據(jù)變化事件。
如果Activity處于不可見狀態(tài)的時候(手機息屏,或者被其他的Activity遮擋),LiveData發(fā)生了多次數(shù)據(jù)變化,當Activity恢復可見狀態(tài)時,只有最新的那份數(shù)據(jù)才會通知給觀察者,前面的數(shù)據(jù)在這種情況下相當于已經過期了,會被直接丟棄。
到此這篇關于Android ViewModel與Lifecycles和LiveData組件用法詳細講解的文章就介紹到這了,更多相關Android ViewModel Lifecycles LiveData內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Android 判斷是開發(fā)debug模式,還是發(fā)布release模式的方法
下面小編就為大家?guī)硪黄狝ndroid 判斷是開發(fā)debug模式,還是發(fā)布release模式的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-12-12
Flutter使用JsBridge方式處理Webview與H5通信的方法
這篇文章主要介紹了Flutter使用JsBridge方式處理Webview與H5通信的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-04-04
Android開發(fā)之自定義星星評分控件RatingBar用法示例
這篇文章主要介紹了Android開發(fā)之自定義星星評分控件RatingBar用法,結合具體實例形式分析了Android自定義評分控件的具體實現(xiàn)步驟以及功能、布局相關操作技巧,需要的朋友可以參考下2019-03-03
Android Fragment+FragmentTabHost組件實現(xiàn)常見主頁面(仿微信新浪)
本文主要介紹Fragment+FragmentTabHost組件實現(xiàn)常見主頁面,這里整理了詳細資料及簡單示例代碼,有興趣的小伙伴可以參考下2016-09-09
Android ListView滾動到底后自動加載數(shù)據(jù)
這篇文章主要為大家詳細介紹了Android之ListView滾動到底后自動加載數(shù)據(jù),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-09-09
Android 自定義 View 中使用 Spannable的實例詳解
這篇文章主要介紹了Android 自定義 View 中使用 Spannable的相關知識,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-05-05
Android DrawerLayout實現(xiàn)側拉菜單功能
這篇文章主要介紹了Android DrawerLayout實現(xiàn)側拉菜單功能,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-06-06

