美文网首页Android-Jetpack
ViewModel、LiveData 使用

ViewModel、LiveData 使用

作者: ITRenj | 来源:发表于2019-07-23 02:27 被阅读14次

    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的引用。

    1. 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使用

    相关文章

      网友评论

        本文标题:ViewModel、LiveData 使用

        本文链接:https://www.haomeiwen.com/subject/tilnlctx.html