Jetpack从入门到几乎入门(二)

1,675 阅读4分钟

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 设置数据,其他两个方法用于主线程。

    我们在initt结构体中用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的基本用法就介绍到这里,此系列将会继续更新,欢迎大佬们指正。