Jetpack从入门到几乎入门(二)
我正在参加「掘金·启航计划」
前言
Jetpack系列:
Jetpack从入门到几乎入门(一) - 掘金 (juejin.cn)
Jetpack从入门到几乎入门(二) - 掘金 (juejin.cn)
Jetpack从入门到几乎入门(三) - 掘金 (juejin.cn)
本文是我在学习guolin大神的《第一行代码》第三版Jetpack部分的知识总结,文中部分代码参考自《第一行代码》第三版
在阅读本文前,您需要掌握kotlin语言的基本语法,了解ViewModel的应用
LiveData
简介
在学习LiveData之前,我接触到响应式编程是在学习RxJava的时候。RxJava的观察者模式的确给开发带来了很大的便利,但是RxJava的一些库学习起来过于复杂,对初学者有不小的挑战。就在此时,Jetpack 提供的,专用于Android的响应式编程组件LiveData横空出世。它和它的绝佳拍档ViewModel一起,再次提高了程序员的开发速度。接下来就让我们一起学习LiveData并探究LiveData与ViewModel的配合使用。
LiveData 的基本用法
我们先写一个带ViewModel的简易的计数器程序,此计数器能做到数据持久化。具体请参考Jetpack从入门到几乎入门(一) - 掘金 (juejin.cn)
-
MainViewModel
class MainViewModel(countReserved: Int) : ViewModel() { var counter = countReserved }
-
MainActivity
class MainActivity : AppCompatActivity() { lateinit var viewModel: MainViewModel lateinit var sp: SharedPreferences val binding: ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(binding.root) sp = getPreferences(Context.MODE_PRIVATE) val countReserved = sp.getInt("count_reserved", 0) viewModel = ViewModelProvider(this, MainViewModelFactory(countReserved)).get(MainViewModel::class.java) binding.plusOneBtn.setOnClickListener { viewModel.counter++ refreshCounter() } binding.clearBtn.setOnClickListener{ viewModel.counter = 0; refreshCounter() } refreshCounter() } private fun refreshCounter() { binding.infoText.text = viewModel.counter.toString() } override fun onPause() { super.onPause() sp.edit { putInt("count_reserved", viewModel.counter) } } }
虽然我们可以实现每按一次按钮,counter就+1,但我们都是在Activity中手动获取ViewModel 中的数据,若ViewModel的内部开启了线程去执行耗时操作,那么Activity马上去获取ViewModel 中的数据的话,只能得到改变发生前的数据,也就是原数据。我们期望的是,ViewModel只要发生变化就能主动告知Activity,所以我们应该使用LiveData对此程序进行优化。
LiveData的优势
LiveData是响应式编程组件,当底层数据发生变化时,LiveData 会通知Observer
对象,我们可以在Observer
对象中编写更新UI的代码,意思就是,当LiveData发生变化时,UI会收到新数据并重绘。且与一般的遵循观察者模式的组件不同,LiveData还具有生命周期感知能力。
事不宜迟,我们开始编写具体的代码。
-
MainViewModel
class MainViewModel(countReserved: Int) : ViewModel() { var counter = MutableLiveData<Int>() init { counter.value = countReserved } fun plusOne(){ val count = counter.value ?: 0 //当获取到的数据为空时,就用0来作为默认计数 counter.value = count + 1 } fun clear(){ counter.value = 0 } }
我们把原来的
counter
改成了一个包含Int型的MutableLiveData
对象。MutableLiveData
是一种可变的LiveData,它有3钟读写数据的方法,分别为getValue()
、setValue()
和postValue()
,值得注意的是,postValue()
方法用于在非主线程中给LiveData 设置数据,其他两个方法用于主线程。我们在
init
t结构体中用setValue()
给counter
设置数据,在plusOne()
和clear()
中对counter
进行修改 -
MainActivity
class MainActivity : AppCompatActivity() { ... override fun onCreate(savedInstanceState: Bundle?) { ... binding.plusOneBtn.setOnClickListener { viewModel.plusOne() } binding.clearBtn.setOnClickListener{ viewModel.clear() } //LiveData对象可以调用它的observe()方法观察数据的变化 viewModel.counter.observe(this, Observer { count -> binding.infoText.text = count.toString() }) } override fun onPause() { super.onPause() sp.edit { putInt("count_reserved", viewModel.counter.value ?: 0) } } }
上述代码中最关键的地方为
viewModel.counter
中的observe()
方法observe()
方法接收两个参数:- 第一个参数是一个
LifecycleOwner
对象,Activity 本身就是一个LifecycleOwner
对象,这里我们直接传this - 第二个参数是一个
Observer
接口,当counter
中的数据发生变化时,就会回调到这里,我们在这里更新UI
- 第一个参数是一个
以上就是LiveData的基本用法,但这个程序还有缺点,我们把counter这个可变的LiveData 暴露给了外部,破坏了ViewModel 数据的封装性。下面我们继续改造MainViewModel
class MainViewModel(countReserved: Int) : ViewModel() {
private val _counter = MutableLiveData<Int>()
val counter: LiveData<Int>
get() = _counter //在counter的get方法中返回_counter变量
init {
_counter.value = countReserved
}
fun plusOne(){
val count = _counter.value ?: 0 //当获取到的数据为空时,就用0来作为默认计数
_counter.value = count + 1
}
fun clear(){
_counter.value = 0
}
}
我们将原来的counter
变量改名为counter
变量,并给它加上private修饰符,使得counter
对外部不可见。我们又定义了不可变的LiveData类型的counter
,并在它的get()属性方法中返回counter
变量,当外部调用counter
变量时,实际上获得的就是counter
的实例,但无法给counter
设置数据。
好了,关于LiveData的基本用法就介绍到这里,此系列将会继续更新,欢迎大佬们指正。