关于android:学会使用LiveData和ViewModel我相信会让你在写业务时变得轻松🌞

前言:

本文不定时更新,有问题欢送在评论区提出~

最近更新工夫:2022-06-21

介绍

在2017年,那时,观察者模式无效的简化了开发,然而诸如RxJava一类的库有一些太过简单,学习老本太高,为此,LiveData呈现了,一个专用于Android的,具备自主生命周期感知能力的,可观测的数据存储类。同时也呈现了ViewModel这个组件,配合LiveData,更不便的实现MVVM模式中Model与View的拆散。那么就让本文来带大家来学习LiveData与ViewModel的应用吧。

LiveData和ViewModel的关系:

本文的案例代码:https://github.com/taxze6/Jet…

LiveData

参考资料:

🌟官网文档:https://developer.android.goo…

🌟LiveData postValue详解:https://www.cnblogs.com/butto…

LiveData是一种可察看的数据存储器类(响应式编程,相似Vue)。与惯例的可察看类不同,LiveData 具备生命周期感知能力。LiveData最重要的是它理解观察者的生命周期,如ActivityFragment。

因而,当LiveData发送变动时,UI会收到告诉,而后UI能够应用新数据从新绘制本人。换句话说,LiveData能够很容易地使屏幕上产生的事件与数据放弃同步(响应式编程的外围)

应用 LiveData 具备以下劣势:

  • UI与数据状态匹配

    • LiveData 遵循观察者模式。当底层数据发生变化时,LiveData 会告诉Observer对象。您能够整合代码以在这些 Observer对象中更新界面。这样一来,您无需在每次利用数据发生变化时更新界面,因为观察者会替您实现更新。
  • 进步代码的稳定性

    代码稳定性在整个应用程序生命周期中减少:

    • 流动进行时不会产生解体。如果应用程序组件处于非活动状态,则这些更改不受影响。因而,您在更新数据时无需放心应用程序组件的生命周期。对于后盾堆栈中的流动,它不会承受任何LiveData事件
    • 内存透露会缩小,观察者会绑定到Lifecycle对象,并在其关联的生命周期受到销毁后进行自我清理
    • 勾销订阅任何观察者时无需放心
    • 如果因为配置更改(如设施旋转)而从新创立了 Activity 或 Fragment,它会立刻接管最新的可用数据。
  • 不再须要手动解决生命周期

    界面组件只是察看相干数据,不会进行或复原察看。LiveData 将主动治理所有这些操作,因为它在察看时能够感知相干的生命周期状态变动。

  • 数据始终保持最新状态

    如果生命周期变为非沉闷状态,它会在再次变为沉闷状态时接管最新的数据。例如,已经在后盾的 Activity 会在返回前台后立刻接管最新的数据。

  • 共享资源

    像单例模式一样,咱们也能够扩大咱们的LiveData对象来包装零碎服务,以便它们能够在咱们的应用程序中共享。一旦LiveData对象连接到零碎服务,任何须要资源的观察者能够轻松地观看LiveData对象。

在以下状况中,不要应用LiveData:

  • 您须要在信息上应用大量运算符,只管LiveData提供了诸如转换之类的工具,但只有Map和switchMap能够帮忙您
  • 您没有与信息的UI交互
  • 您有一次性的异步操作
  • 您不用将缓存的信息保留到UI中

如何应用LiveData

一般来说咱们会在 ViewModel 中创立 Livedata 对象,保障app配置变更时,数据不会失落,而后再 Activity/Fragment 的 onCreate 中注册 Livedata 监听(因为在 onStart 和 onResume 中进行监听可能会有冗余调用)

根底应用流程:

1.创立一个实例LiveData来保留某种类型的数据。个别在你创立的ViewModel类中实现

class MainViewModel : ViewModel() {
    var mycount: MutableLiveData<Int> = MutableLiveData()
}

2.在Activity或者Fragment中获取到ViewModel,通过ViewModel获取到对应的LiveData

class MainActivity : AppCompatActivity() {
    lateinit var viewModel: MainViewModel
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        /**记住相对不能够间接去创立ViewModel实例
        肯定要通过ViewModelProvider(ViewModelStoreOwner)构造函数来获取。
        因为每次旋转屏幕都会从新调用onCreate()办法,如果每次都创立新的实例的话就无奈保留数据了。
        用上述办法后,onCreate办法被再次调用,
        它会返回一个与MainActivity相关联的事后存在的ViewModel,这就是保留数据的起因。*/
        viewModel = ViewModelProvider(this@MainActivity,ViewModelProvider.
                        NewInstanceFactory()).get(MainViewModel::class.java)
    }
}

3.给LiveData增加观察者监听,用来监听LiveData中的数据变动,在Observer的onChanged中应用监听回调数据

/**
 *  订阅 ViewModel,mycount是一个LiveData类型 能够察看
 * */        
viewModel.mycount.observe(this@MainActivity) {
    countTv.text = viewModel.mycount.value.toString()
}
// LiveData onchange会主动感应生命周期 不须要手动
//        viewModel.mycount.observe(this, object : Observer<Int> {
//            override fun onChanged(t: Int?) {
//
//            }
//        })

进阶用法:

Transformations.map

当初有一个场景:咱们通过网络申请,取得了一个User类数据(LiveData),然而,咱们只想把User.name裸露给内部观察者,这样咱们就能够通过Transformations.map来转化LiveData的数据类型,从而来实现上述场景。这个函数罕用于对数据的封装。

//实体类
data class User(var name: String)
...
//Transformations.map接管两个参数,第一个参数是用于转换的LiveData原始对象,第二个参数是转换函数。
private val userLiveData: MutableLiveData<User> = MutableLiveData()
    val userNames: LiveData<String> = Transformations
        .map(userLiveData) { user ->
            user.name
}

Transformations.switchMap

switchMap是依据传入的LiveData的值,而后判断这个值,而后再去切换或者构建新的LiveData。比方咱们有些数据须要依赖其余数据进行查问,就能够应用switchMap。

例如,有一个学生,他有两门课程的问题,然而在UI组件中,咱们一次只能显示一门课的问题,在这个须要判断展现哪门课程问题的需要下,咱们就能够应用switchMap。

data class Student
    (var englishScore: Double, var mathScore: Double, val scoreTAG: Boolean)
​
.....
class SwitchMapViewModel:ViewModel {
    var studentLiveData = MutableLiveData<Student>()
    val transformationsLiveData = Transformations.switchMap(studentLiveData) {
        if (it.scoreTAG) {
            MutableLiveData(it.englishScore)
        } else {
            MutableLiveData(it.mathScore)
        }
    }
}
​
//应用时:
var student = Student()
person.englishScore = 88.2
person.mathScore = 91.3
//判断显示哪个问题
person.condition = true
switchMapViewModel.conditionLiveData.postValue(person)

MediatorLiveData

MediatorLiveData继承于MutableLiveData,在MutableLiveData的根底上,减少了合并多个LiveData数据源的性能。其实就是通过addSource()这个办法去监听多个LiveData。

例如:当初有一个存在本地的dbLiveData,还有一个网络申请来的LiveData,咱们须要讲下面两个后果联合之后展现给用户,第一种做法是咱们在Activity中别离注册这两个LiveData的观察者,当数据发生变化时去更新UI,然而咱们其实应用MediatorLiveData能够简化这个操作。

class MediatorLiveDataViewModel : ViewModel() {
    var liveDataA = MutableLiveData<String>()
    var liveDataB = MutableLiveData<String>()
​
    var mediatorLiveData = MediatorLiveData<String>()
    
    init {
        mediatorLiveData.addSource(liveDataA) {
            Log.d("This is livedataA", it)
            mediatorLiveData.postValue(it)
        }
​
        mediatorLiveData.addSource(liveDataB) {
            Log.d("This is livedataB", it)
            mediatorLiveData.postValue(it)
        }
    }
}

解释:

如果是第一次接触到LiveData的敌人可能会发现,咱们尽管始终在提LiveData,然而用的时候却是MutableLiveData,这两个有什么关系呢,既然都没怎么用LiveData,那么把题目间接改成MutableLiveData吧
其实,LiveData与MutableLiveData在概念上是截然不同的。惟一的几个区别别离是:

💡“此处援用:LiveData与MutableLiveData的区别文章中的段落”

  • MutableLiveData的父类是LiveData
  • LiveData在实体类里能够告诉指定某个字段的数据更新
  • MutableLiveData则是齐全是整个实体类或者数据类型变动后才告诉.不会细节到某个字段。

原理探索:

对于LiveData的根底应用咱们就讲到这里,想要摸索LiveData原理的敌人能够从上面几个角度:

  • LiveData的工作原理
  • LiveData的observe办法源码剖析
  • LifecycleBoundObserver源码剖析
  • activeStateChanged源码剖析(用于粘性事件)
  • postValue和setValue
  • considerNotify判断是否发送数据分析
  • 粘性事件的剖析

置信大家从以上几个角度去剖析LiveData会有不小的播种💪

ViewModel

官网文档:https://developer.android.goo…

官网简介

ViewModel类旨在以重视生命周期的形式存储和治理界面相干的数据。ViewModel类让数据可在产生屏幕旋转等配置更改后持续留存。

生命周期

ViewModel的生命周期会比创立它的Activity、Fragment的生命周期都要长。所以ViewModel中的数据会始终存活在Activity/Fragment中。

根底应用流程:

1.结构数据对象

自定义ViewModel类,继承ViewModel,而后在自定义的ViewModel类中增加须要的数据对象

class MainViewModel : ViewModel() {
    ...
}

2.获取数据

有两种常见的ViewModel创立形式,第一种是在activity或fragment种间接基于 ViewModelProvider 获取。第二种是通过ViewModelFactory 创立

//第一种 ViewModelProvider间接获取
ViewModelProvider(this@MainActivity).get(MainViewModel::class.java)
​
//第二种 通过 ViewModelFactory 创立
class TestViewModelFactory(private val param: Int) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return TestViewModel(param) as T
    }
}
​
ViewModelProvider(this@MainActivity,TestViewModelFactory(0)).get(TestViewModel::class.java)
​

应用ViewModel就是这么简略🚢

ViewModel常见的应用场景

  • 应用ViewModel,在横竖屏切换后,Activity重建,数据仍能够保留
  • 同一个Activity下,Fragment之间的数据共享
  • 与LiveData配合实现代码的解耦

ViewModel和onSaveInstanceState的区别

我置信大家肯定晓得onSaveInstanceState,它也是用来保留UI状态的,你能够应用它保留你所想保留的货色,在Activity被杀死之前,它个别在onStop或者onPause之前触发。尽管ViewModel被设计为利用除了onSaveInstanceState的另一个选项,然而还是有一些显著的区别。因为资源限度,ViewModel无奈在过程敞开后持续存在,但onSaveInstance蕴含执行此工作。ViewModel是存储数据的绝佳抉择,而onSaveInstanceState bundles不是用于该目标的适合选项。

ViewModel用于存储尽可能多的UI数据。因而,在配置更改期间不须要从新加载或从新生成该数据。

另一方面,如果该过程被框架敞开,onSaveInstanceState应该存储回复UI状态所需的起码数据量。例如,能够将所有用户的数据寄存在ViewModel中,而仅将用户的数据库ID存储在onSaveInstanceState中。

android onSaveInstanceState调用机会具体总结

onSaveInstanceState用法及源码剖析

ViewModel和Context

ViewModel不应该蕴含对Activity,Fragment或context的援用,此外,ViewModel不应蕴含对UI控制器(如View)的援用,因为这将创立对Context的间接援用。当您旋转Activity被销毁的屏幕时,您有一个ViewModel蕴含对已销毁Activity的援用,这就是内存透露。因而,如果须要应用上下文,则必须应用应用程序上下文 (AndroidViewModel)

LiveData和ViewModel的根本用法咱们曾经介绍完了,当初用几个例子带大家来更好的应用它们

案例一:计数器 — 两个Activity共享一个ViewModel

话不多说,先上效果图:


尽管这个案例是比较简单的,然而我置信能够帮忙你更快的相熟LiveData和ViewModel

想要实现效果图的话须要从上面几步来写(只解说外围代码,具体代码请本人查看仓库):

第一步:创立ViewModel

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
​
class MainViewModel : ViewModel() {
    private var _mycount: MutableLiveData<Int> = MutableLiveData()
    //只裸露不可变的LiveData给内部
    val mycount: LiveData<Int> get() = _mycount
    init {
        //初始化
        _mycount.value = 0
    }
    /**
     * mycount.value若为空就赋值为0,不为空则加一
     * */
    fun add() {
        _mycount.value = _mycount.value?.plus(1)
    }
    /**
     * mycount.value若为空就赋值为0,不为空则减一,能够为正数
     * */
    fun reduce() {
        _mycount.value = _mycount.value?.minus(1)
    }
    /**
     * 随机参数
     * */
    fun random() {
        val random = (0..100).random()
        _mycount.value = random
    }
    /**
     * 革除数据
     * */
    fun clear() {
        _mycount.value = 0
    }
}

第二步:标记ViewModel的作用域

因为,咱们是两个Activity共享一个ViewModel,所以咱们须要标记ViewModel的作用域

import androidx.lifecycle.*
​
/**
 * 用于标记viewmodel的作用域
 */
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD)
annotation
class VMScope(val scopeName: String) {}
​
private val vMStores = HashMap<String, VMStore>()
​
fun LifecycleOwner.injectViewModel() {
    //依据作用域创立商店
    this::class.java.declaredFields.forEach { field ->
        field.getAnnotation(VMScope::class.java)?.also { scope ->
            val element = scope.scopeName
            var store: VMStore
            if (vMStores.keys.contains(element)) {
                store = vMStores[element]!!
            } else {
                store = VMStore()
                vMStores[element] = store
            }
            val clazz = field.type as Class<ViewModel>
            val vm = ViewModelProvider(store, ViewModelProvider.NewInstanceFactory()).get(clazz)
            field.set(this, vm)
        }
    }
}
​
class VMStore : ViewModelStoreOwner {
    private var vmStore: ViewModelStore? = null
    override fun getViewModelStore(): ViewModelStore {
        if (vmStore == null)
            vmStore = ViewModelStore()
        return vmStore!!
    }
​
}

第三步:在Activity中应用(都是局部代码)

class MainActivity : AppCompatActivity() {
    @VMScope("count") //设置作用域
    lateinit var viewModel: MainViewModel
​
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        injectViewModel()
        initEvent()
    }
​
    private fun initEvent() {
        val cardReduce: CardView = findViewById(R.id.card_reduce)
        .....
        cardReduce.setOnClickListener {
            //调用自定义ViewModel中的办法
            viewModel.reduce()
        }
        
        .....
        
        /**
         *  订阅 ViewModel,mycount是一个LiveData类型 能够察看
         * */
        viewModel.mycount.observe(this@MainActivity) {
            countTv.text = viewModel.mycount.value.toString()
        }
}
    
在第二个Activity中也是相似...

这样就能够实现效果图啦🏀

案例二:同一个Activity下的两个Fragment共享一个ViewModel

话不多说,先上效果图


这个成果就很简略了,在同一个Activity下,有两个Fragment,这两个Fragment共享一个ViewModel

这个案例次要是想带大家理解一下ViewModel在Fragment中的应用

第一步:仍旧是创立ViewModel

class BlankViewModel : ViewModel() {
    private val numberLiveData = MutableLiveData<Int>()
​
    private var i = 0
    fun getLiveData(): LiveData<Int> {
        return numberLiveData
    }
​
    fun addOne(){
        i++
        numberLiveData.value = i
    }
}

非常简单的一个ViewModel

第二步:在Fragment中应用

//左Fragment
class LeftFragment : Fragment() {
    
    private val viewModel:BlankViewModel by activityViewModels()
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_left, container, false)
    }
​
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        //对+1按钮监听
        left_button.setOnClickListener {
            viewModel.addOne()
        }
        activity?.let {it ->
            viewModel.getLiveData().observe(it){
                left_text.text = it.toString()
            }
        }
    }
}
​
//右Fragment
class RightFragment : Fragment() {
    private val viewModel: BlankViewModel by activityViewModels()
    
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_right, container, false)
    }
​
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        right_button.setOnClickListener {
            viewModel.addOne()
        }
        activity?.let { it ->
            viewModel.getLiveData().observe(it) {
                right_text.text = it.toString()
            }
        }
    }
}

这样,这个简略的案例就实现啦。

尾述

终于把LiveData和ViewModel的大抵应用解说了一遍,但仅仅这样还是不够的,你还须要在更多更多的实际中去相熟,去深刻学习….

对于我

Hello,我是Taxze,如果您感觉文章对您有价值,欢送 ❤️,也欢送关注我的博客。

如果您感觉文章还差了那么点货色,也请通过关注督促我写出更好的文章——万一哪天我提高了呢?😝

根底系列:

2022 · 让我带你Jetpack架构组件从入门到精通 — Lifecycle

学会应用LiveData和ViewModel,我置信会让你在写业务时变得轻松🌞 (本文🌟)

以下局部还在码字,连忙点个珍藏吧🔥

2022 · 让我带你Jetpack架构组件从入门到精通 — DataBinding

2022 · 让我带你Jetpack架构组件从入门到精通 — Navigation

2022 · 让我带你Jetpack架构组件从入门到精通 — Room

2022 · 让我带你Jetpack架构组件从入门到精通 — Paging3

2022 · 让我带你Jetpack架构组件从入门到精通 — WorkManager

2022 · 让我带你Jetpack架构组件从入门到精通 — ViewPager2

2022 · 让我带你Jetpack架构组件从入门到精通 — 登录注册页面实战(MVVM)

进阶系列:

协程 + Retrofit网络申请状态封装

Room 缓存封装

…..

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理