ViewModel、LiveData 使用
在看以下內容之前,如果對DataBinding不夠熟悉,建議先對DataBinding進行了解。
簡書:ViewModel+LiveData+DataBinding使用
CSDN:ViewModel+LiveData+DataBinding使用
ViewModel 簡介
ViewModel類是用來保存UI數據的類,它會在配置變更(即 Configuration Change,例如手機屏幕的旋轉)之後繼續存在;我們都知道,當手機屏幕發生旋轉的時候,Activity會被重新創建,也就是說生命週期又將從onCreate開始,如果你此時不及時保存,那麼一些UI數據將會丟失,這樣肯定是會出問題的。但是,ViewModel並不會受此影響,即便手機屏幕發生旋轉,ViewModel依然存在,這樣的話Activity的UI數據便可以保存下來。
ViewModel 使用說明、注意
- 所有Activity的UI相關數據應該保存在ViewModel中,而不是保存在Activity中。這樣做的好處是,在配置變更的時候,你應用的UI數據仍然存在。即使ViewModel這麼強大,但它也不應該不承擔過多責任,當有UI數據處理等相關事件建議創建Presenter類,或者創建一個更成熟的架構。
- Activity負責展示UI數據,並接收互動(一般來說是與用戶的互動)。但是Activity不應當處理這些互動。
- 在應用需要加載數據或者保存數據的時候,建議創建一個Repository的存儲區類,裏面放置存儲與加載應用數據的API。
- ViewModel不應持有Context,就像之前說的:
它會在配置變更(即 Configuration Change,例如手機屏幕的旋轉)之後繼續存在。
所以,ViewModel生命週期遠比Activity,Fragment等生命週期更長,具體如下圖所示。如果你這樣做了,加入在屏幕旋轉情況下,原Activity將會銷燬,新的Activity將會被創建。而ViewModel會一直持有原Activity,這樣便會造成內存泄漏。如果你的ViewModel確實需要Context,那麼你的ViewModel可以繼承AndroidViewModel,這樣你的ViewModel中會有Application的引用。
5. ViewModel不應當取代onSaveInstanceState方法。儘管ViewModel很出色了,但是它和onSaveInstanceState依然是相輔相成的作用。因爲,當進程被關閉時,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 用法
-
新建數據實體類,作爲UI需要使用的數據
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) } }
經過上面的步驟,我們的ViewModel就算建立好了,現在看上去並沒有什麼實際的意義。但是不要急,慢慢的往下看,當他和LiveData以及DataBinding一起使用的時候就會發現新世界了。
注意點
-
ViewModel只提供一個默認的無參構造函數,如果你需要一個有參構造函數,那麼就需要使用ViewModelFactory這個類,具體使用方法如下所示:
// 數據實體類 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時,也不應該從 Activity/Fragment 中傳遞,而是應該讓我們的ViewModel 繼承 AndroidViewModel,這樣就能在 ViewModel 中使用 Application了
class UserViewModel(application: Application) :AndroidViewModel(application) { var user = User("張三",25,"浙江省杭州市") }
LiveData簡介
LiveData是一個可觀察的數據持有者類。與常規可觀察性不同,LiveData具有生命週期感知能力,這意味着它尊重其他應用程序組件(例如Activity、Fragment或Service)的生命週期。這種感知確保LiveData只更新處於活動生命週期狀態(Observer 的 Lifecycle 對象處於 STARTED 或者 RESUMED 狀態)的應用程序組件觀察者。
LiveData 的優點
- 具有生命週期感知能力,可以做到在組件處於激活狀態的時候纔會回調相應的方法,從而刷新相應的 UI。
- 不用擔心發生內存泄漏
- 當 config 導致 Activity 重新創建的時候,不需要手動取處理數據的儲存和恢復。它已經幫我們封裝好了。(需要使用ViewModel)
- 當 Actiivty 不是處於激活狀態的時候,如果你想 Livedata setValue 之後立即回調 obsever 的 onChange 方法,而不是等到 Activity 處於激活狀態的時候纔回調 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 基本使用
-
直接使用默認值設置
// 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() 方法中增加以下代碼即可 // 增加改變監聽 userViewModel.userLiveData.observe(this, Observer {user -> text_view.text = user.toString() }) // 點擊按鈕,改變User bt_change.setOnClickListener { userViewModel.userLiveData.postValue(User("李四",28,"湖南省長沙市")) }
LiveData 進階使用
-
map() :把一個數據類型變換爲另外一個數據類型
// 將 User 對象變爲 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() : 把一個數據變化爲另外一個 LiveData
// 在回調中創建 LiveData 類型數據返回 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源對象更改時觸發
/ 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():只能在主線程設置Value
- postValue():可以在子線程設置Value