ViewModel、LiveData 使用

ViewModel、LiveData 使用

官方中文教学地址

在看以下内容之前,如果对DataBinding不够熟悉,建议先对DataBinding进行了解。

CSDN:Android 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使用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容