ViewModel、LiveData 使用

ViewModel、LiveData 使用

官方中文教學地址

在看以下內容之前,如果對DataBinding不夠熟悉,建議先對DataBinding進行了解。

Android DataBinding 使用博客說明

Android DataBinding 使用測試代碼

簡書:ViewModel+LiveData+DataBinding使用

CSDN:ViewModel+LiveData+DataBinding使用

ViewModel 簡介

ViewModel類是用來保存UI數據的類,它會在配置變更(即 Configuration Change,例如手機屏幕的旋轉)之後繼續存在;我們都知道,當手機屏幕發生旋轉的時候,Activity會被重新創建,也就是說生命週期又將從onCreate開始,如果你此時不及時保存,那麼一些UI數據將會丟失,這樣肯定是會出問題的。但是,ViewModel並不會受此影響,即便手機屏幕發生旋轉,ViewModel依然存在,這樣的話Activity的UI數據便可以保存下來。

ViewModel 使用說明、注意

  1. 所有Activity的UI相關數據應該保存在ViewModel中,而不是保存在Activity中。這樣做的好處是,在配置變更的時候,你應用的UI數據仍然存在。即使ViewModel這麼強大,但它也不應該不承擔過多責任,當有UI數據處理等相關事件建議創建Presenter類,或者創建一個更成熟的架構。
  2. Activity負責展示UI數據,並接收互動(一般來說是與用戶的互動)。但是Activity不應當處理這些互動。
  3. 在應用需要加載數據或者保存數據的時候,建議創建一個Repository的存儲區類,裏面放置存儲與加載應用數據的API。
  4. 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 引入

  1. android.support 形式

     implementation "android.arch.lifecycle:extensions:1.1.1"
     implementation "android.arch.lifecycle:viewmodel:1.1.1"
    
  2. androidx 支持(如下方式引入,包含了ViewModel和LiveData)

     implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"
     annotationProcessor "androidx.lifecycle:lifecycle-compiler:2.0.0"
    

ViewModel 用法

  1. 新建數據實體類,作爲UI需要使用的數據

     data class User(var name: String = "", var age: Int = 0, var address: String = "")
    
  2. 新建我的ViewModel類 UserViewModel 繼承至 ViewModel

     class UserViewModel:ViewModel() {
     	var user = User("張三",25,"浙江省杭州市")
     }
    
  3. 在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一起使用的時候就會發現新世界了。

注意點

  1. 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)
     }
    

    }

  2. 當我們的 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 的優點

  1. 具有生命週期感知能力,可以做到在組件處於激活狀態的時候纔會回調相應的方法,從而刷新相應的 UI。
  2. 不用擔心發生內存泄漏
  3. 當 config 導致 Activity 重新創建的時候,不需要手動取處理數據的儲存和恢復。它已經幫我們封裝好了。(需要使用ViewModel)
  4. 當 Actiivty 不是處於激活狀態的時候,如果你想 Livedata setValue 之後立即回調 obsever 的 onChange 方法,而不是等到 Activity 處於激活狀態的時候纔回調 obsever 的 onChange 方法,你可以使用 ObserveForever 方法,但是你必須在 onDestroy 的時候 removeObserver。

LiveData 使用

LiveData 引入

  1. android.support 形式

     implementation "android.arch.lifecycle:livedata:1.1.1"
    
  2. androidx 支持(如下方式引入,包含了ViewModel和LiveData)

     implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"
     annotationProcessor "androidx.lifecycle:lifecycle-compiler:2.0.0"
    

LiveData 基本使用

  1. 直接使用默認值設置

     // 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>
    
  2. 點擊事件修改值之後實時更新

     // 只需要在 Activity 的 onCreate() 方法中增加以下代碼即可
    
      // 增加改變監聽
     userViewModel.userLiveData.observe(this, Observer {user ->
         text_view.text = user.toString()
     })
     // 點擊按鈕,改變User
     bt_change.setOnClickListener {
         userViewModel.userLiveData.postValue(User("李四",28,"湖南省長沙市"))
     }
    

LiveData 進階使用

  1. 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
     })
    
  2. 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
     })
    
  3. 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

ViewModel+LiveData+DataBinding使用

簡書:ViewModel+LiveData+DataBinding使用

CSDN:ViewModel+LiveData+DataBinding使用

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