ViewModel、LiveData 使用
在看以下內(nèi)容之前,如果對DataBinding不夠熟悉,建議先對DataBinding進行了解。
簡書:ViewModel+LiveData+DataBinding使用
CSDN:ViewModel+LiveData+DataBinding使用
ViewModel 簡介
ViewModel類是用來保存UI數(shù)據(jù)的類,它會在配置變更(即 Configuration Change,例如手機屏幕的旋轉(zhuǎn))之后繼續(xù)存在;我們都知道,當手機屏幕發(fā)生旋轉(zhuǎn)的時候,Activity會被重新創(chuàng)建,也就是說生命周期又將從onCreate開始,如果你此時不及時保存,那么一些UI數(shù)據(jù)將會丟失,這樣肯定是會出問題的。但是,ViewModel并不會受此影響,即便手機屏幕發(fā)生旋轉(zhuǎn),ViewModel依然存在,這樣的話Activity的UI數(shù)據(jù)便可以保存下來。
ViewModel 使用說明、注意
- 所有Activity的UI相關(guān)數(shù)據(jù)應(yīng)該保存在ViewModel中,而不是保存在Activity中。這樣做的好處是,在配置變更的時候,你應(yīng)用的UI數(shù)據(jù)仍然存在。即使ViewModel這么強大,但它也不應(yīng)該不承擔過多責任,當有UI數(shù)據(jù)處理等相關(guān)事件建議創(chuàng)建Presenter類,或者創(chuàng)建一個更成熟的架構(gòu)。
- Activity負責展示UI數(shù)據(jù),并接收互動(一般來說是與用戶的互動)。但是Activity不應(yīng)當處理這些互動。
- 在應(yīng)用需要加載數(shù)據(jù)或者保存數(shù)據(jù)的時候,建議創(chuàng)建一個Repository的存儲區(qū)類,里面放置存儲與加載應(yīng)用數(shù)據(jù)的API。
- ViewModel不應(yīng)持有Context,就像之前說的:
它會在配置變更(即 Configuration Change,例如手機屏幕的旋轉(zhuǎn))之后繼續(xù)存在。
所以,ViewModel生命周期遠比Activity,F(xiàn)ragment等生命周期更長,具體如下圖所示。如果你這樣做了,加入在屏幕旋轉(zhuǎn)情況下,原Activity將會銷毀,新的Activity將會被創(chuàng)建。而ViewModel會一直持有原Activity,這樣便會造成內(nèi)存泄漏。如果你的ViewModel確實需要Context,那么你的ViewModel可以繼承AndroidViewModel,這樣你的ViewModel中會有Application的引用。
- ViewModel不應(yīng)當取代onSaveInstanceState方法。盡管ViewModel很出色了,但是它和onSaveInstanceState依然是相輔相成的作用。因為,當進程被關(guān)閉時,ViewModel將會被銷毀,但是onSaveInstanceState不會受到影響。
ViewModel 使用
ViewModel 引入
-
android.support 形式
implementation "android.arch.lifecycle:extensions:1.1.1" implementation "android.arch.lifecycle:viewmodel:1.1.1" -
androidx 支持(如下方式引入,包含了ViewModel和LiveData)
implementation "androidx.lifecycle:lifecycle-extensions:2.0.0" annotationProcessor "androidx.lifecycle:lifecycle-compiler:2.0.0"
ViewModel 用法
-
新建數(shù)據(jù)實體類,作為UI需要使用的數(shù)據(jù)
data class User(var name: String = "", var age: Int = 0, var address: String = "") -
新建我的ViewModel類 UserViewModel 繼承至 ViewModel
class UserViewModel:ViewModel() { var user = User("張三",25,"浙江省杭州市") } -
在Activity/Fragment中實例化
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var userViewModel = ViewModelProviders.of(this).get(UserViewModel::class.java) } }
經(jīng)過上面的步驟,我們的ViewModel就算建立好了,現(xiàn)在看上去并沒有什么實際的意義。但是不要急,慢慢的往下看,當他和LiveData以及DataBinding一起使用的時候就會發(fā)現(xiàn)新世界了。
注意點
-
ViewModel只提供一個默認的無參構(gòu)造函數(shù),如果你需要一個有參構(gòu)造函數(shù),那么就需要使用ViewModelFactory這個類,具體使用方法如下所示:
// 數(shù)據(jù)實體類 data class Student(var name: String, var address: String = "") // ViewModel 類 class StudentViewModel(var student: Student) : ViewModel() // 建立 StudentViewModelFactory 類 class StudentViewModelFactory(private var student: Student) : ViewModelProvider.NewInstanceFactory() { override fun <T : ViewModel?> create(modelClass: Class<T>): T { return StudentViewModel(student) as T } } // 在Activity/Fragment中使用 class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) var factory = StudentViewModelFactory(Student("李四","浙江省杭州市")) var viewModel = ViewModelProviders.of(this, factory).get(StudentViewModel::class.java) }}
-
當我們的 ViewModel 中需要使用Context時,也不應(yīng)該從 Activity/Fragment 中傳遞,而是應(yīng)該讓我們的ViewModel 繼承 AndroidViewModel,這樣就能在 ViewModel 中使用 Application了
class UserViewModel(application: Application) :AndroidViewModel(application) { var user = User("張三",25,"浙江省杭州市") }
LiveData簡介
LiveData是一個可觀察的數(shù)據(jù)持有者類。與常規(guī)可觀察性不同,LiveData具有生命周期感知能力,這意味著它尊重其他應(yīng)用程序組件(例如Activity、Fragment或Service)的生命周期。這種感知確保LiveData只更新處于活動生命周期狀態(tài)(Observer 的 Lifecycle 對象處于 STARTED 或者 RESUMED 狀態(tài))的應(yīng)用程序組件觀察者。
LiveData 的優(yōu)點
- 具有生命周期感知能力,可以做到在組件處于激活狀態(tài)的時候才會回調(diào)相應(yīng)的方法,從而刷新相應(yīng)的 UI。
- 不用擔心發(fā)生內(nèi)存泄漏
- 當 config 導致 Activity 重新創(chuàng)建的時候,不需要手動取處理數(shù)據(jù)的儲存和恢復(fù)。它已經(jīng)幫我們封裝好了。(需要使用ViewModel)
- 當 Actiivty 不是處于激活狀態(tài)的時候,如果你想 Livedata setValue 之后立即回調(diào) obsever 的 onChange 方法,而不是等到 Activity 處于激活狀態(tài)的時候才回調(diào) obsever 的 onChange 方法,你可以使用 ObserveForever 方法,但是你必須在 onDestroy 的時候 removeObserver。
LiveData 使用
LiveData 引入
-
android.support 形式
implementation "android.arch.lifecycle:livedata:1.1.1" -
androidx 支持(如下方式引入,包含了ViewModel和LiveData)
implementation "androidx.lifecycle:lifecycle-extensions:2.0.0" annotationProcessor "androidx.lifecycle:lifecycle-compiler:2.0.0"
LiveData 基本使用
-
直接使用默認值設(shè)置
// data class data class User(var name: String = "", var age: Int = 0, var address: String = "") // ViewModel class UserViewModel : ViewModel() { var userLiveData:MutableLiveData<User> = MutableLiveData() init { // 初始化是提供默認值 userLiveData.value = User("張三",25,"浙江省杭州市") } } // Activity class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var userViewModel = ViewModelProviders.of(this).get(UserViewModel::class.java) text_view.text = userViewModel.userLiveData.value.toString() } } // activity_main.xml <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <Button android:id="@+id/bt_change" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="修改學生" app:layout_constraintTop_toTopOf="parent"/> <TextView android:id="@+id/text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent"/> </androidx.constraintlayout.widget.ConstraintLayout> -
點擊事件修改值之后實時更新
// 只需要在 Activity 的 onCreate() 方法中增加以下代碼即可 // 增加改變監(jiān)聽 userViewModel.userLiveData.observe(this, Observer {user -> text_view.text = user.toString() }) // 點擊按鈕,改變User bt_change.setOnClickListener { userViewModel.userLiveData.postValue(User("李四",28,"湖南省長沙市")) }
LiveData 進階使用
-
map() :把一個數(shù)據(jù)類型變換為另外一個數(shù)據(jù)類型
// 將 User 對象變?yōu)?String private fun map(userLiveData: MutableLiveData<User>): LiveData<String> { return Transformations.map(userLiveData) { user -> user.name } } // 使用 map(userViewModel.userLiveData).observe(this, Observer { text_view.text = it }) -
switchMap() : 把一個數(shù)據(jù)變化為另外一個 LiveData
// 在回調(diào)中創(chuàng)建 LiveData 類型數(shù)據(jù)返回 private fun switchMap(userLiveData: MutableLiveData<User>): LiveData<String> { return Transformations.switchMap(userLiveData) { var result = MutableLiveData<String>() result.postValue(it.name) return@switchMap result } } // 使用 switchMap(userViewModel.userLiveData).observe(this, Observer { text_view.text = it }) -
MediatorLiveData : LiveData的一個子類,允許合并多個LiveData源。MediatorLiveData對象的觀察者隨后會在任何原始LiveData源對象更改時觸發(fā)
/ data class data class User(var name: String = "", var age: Int = 0, var address: String = "") // Activity class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var userViewModel = ViewModelProviders.of(this).get(UserViewModel::class.java) var mediatorLiveData: MediatorLiveData<User> = MediatorLiveData() var user1 = MutableLiveData<User>() var user2 = MutableLiveData<User>() mediatorLiveData.addSource(user1) { Log.i("bbb", it.toString()) text_view.text = it.address } mediatorLiveData.addSource(user2) { Log.i("ccc", it.toString()) text_view.text = it.address } // 這一步不能省略 mediatorLiveData.observe(this, Observer { Log.i("aaa", it.toString()) // 此處的返回值好像沒有作用,日志也沒有打印, // 但是該 mediatorLiveData.observe 不能省略,省略的話,界面不能更新 text_view.text = it.name }) // 點擊按鈕,改變User var flag = false bt_change.setOnClickListener { if (flag) user1.postValue(User("王五", 26, "深圳")) else user2.postValue(User("趙六", 27, "上海")) flag = !flag } } }
MutableLiveData 的 setValue() 和 potValue() 方法
- setValue():只能在主線程設(shè)置Value
- postValue():可以在子線程設(shè)置Value
ViewModel+LiveData+DataBinding使用
簡書:ViewModel+LiveData+DataBinding使用
CSDN:ViewModel+LiveData+DataBinding使用