本文接上篇 初识Kotlin 之6__探究Jetpack ,为 Jetpack 知识第二篇
一 ViewModel
ViewModel可以算是Jetpack中最重要的组件之一。ViewModel的一个重要作用是帮助Activity分担一部分工作。
它是专门用于存放与界面相关的数据的。 也就是说, 只要是界面上能看得到的数据,它的相关变量都应该存放在ViewModel中, 而不是Activity中, 这样可以在一定程度上减少Activity中的逻辑。
1. ViewModel是有生命周期的, 并且与Activity 不同, 它可以保证在手机屏幕发生旋转的时候不会被重新创建。 只有当Activity退出的时候才会跟着Activity 一起销毁。
ViewModel的生命周期如图所示
2. ViewModel的基本用法
由于Jetpack中的组件通常是以AndroidX库 的形式发布的,但是如果要想使用ViewModel组件, 需要在app/build.gradle文件中添加如下依赖:
dependencies{
// 添加使用 viewmodel 组件
implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
}
通常来讲, 比较好的编程规范是给每一个Activity 和 Fragment都创建一个对应的ViewModel, 在本示例中,我们为MainActivity 创建一个对应的MainViewModel类, 让它继承自ViewModel.
package com.rd.viewmodel
import androidx.lifecycle.ViewModel
class MainViewModel : ViewModel() {
var counter = 0
}
MainActivity 代码如下:
package com.rd.jetpack
import android.content.Context
import android.content.SharedPreferences
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.core.content.edit
import androidx.lifecycle.ViewModelProviders
import com.rd.viewmodel.MainViewModel
import com.rd.viewmodel.MainViewModelFactory
import com.rd.lifecycle.MyObserver
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
/**
* lateinit 表示 这个变量会被初始化,并且不会为null,但是在声明这里,我暂时还不知道什么时候会被初始化
*/
lateinit var viewModel : MainViewModel
lateinit var sp: SharedPreferences
val count_reserved = "count_reserved"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
lifecycle.addObserver(MyObserver(lifecycle))
sp = getPreferences(Context.MODE_PRIVATE)
val countReserved = sp.getInt(count_reserved, 0)
/**
* 保存当前计数的写法
*/
viewModel = ViewModelProviders.of(this, MainViewModelFactory(countReserved)).get(MainViewModel::class.java)
/** 不保存计数的写法
* viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
*/
// +1 按钮
plusOneBtn.setOnClickListener{
viewModel.counter++
refreshCounter()
}
//清零 按钮
clearBtn.setOnClickListener {
viewModel.counter=0
refreshCounter()
}
refreshCounter()
}
private fun refreshCounter(){
infoText.text = viewModel.counter.toString()
}
override fun onPause() {
super.onPause()
sp.edit{putInt(count_reserved,viewModel.counter)}
}
}
3. 要注意的是, 我们绝对不可以直接去创建ViewModel的实例, 而是一定要通过ViewModelProviders 来获取ViewModel的实例,
具体语法规则如下: ViewModelProviders.of(Activity 或 Fragment 的实例).get(<ViewModel的实例>::class.java)
之所以要这么写, 是因为ViewModel有其独立的生命周期,并且其生命周期要长于Activity。 如果在onCreate()中创建ViewModel的实例, 那么每次onCreate() 执行的时候,ViewModel都会创建一个新的实例, 这样当手机屏幕发生旋转的时候, 就无法保留其中的数据了。
4. 向ViewModel传递参数
由于所有ViewModel的实例都是通过ViewModelProviders 来获取的,因此我们没有任何地方可以向ViewModel的构造函数中传递参数。
要解决这个问题, 需要借助ViewModelProvider.Factory来实现,
特别注意: ViewModelProvider 和 ViewModelProviders 是两个不同的类,切勿混淆!
见下图所示
现在,修改MainViewModel的代码,如下所示:
package com.rd.viewmodel
import androidx.lifecycle.ViewModel
//以下为保存计数 的写法
class MainViewModel(countReserved: Int) : ViewModel(){
var counter = countReserved
}
如何向MainViewModel的构造函数传递数据? 新建一个MainViewModelFactory类,让它实现ViewModelProvider.Factory接口,
package com.rd.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
class MainViewModelFactory(private val countReserved: Int): ViewModelProvider.Factory{
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return MainViewModel(countReserved) as T
}
}
可以看到,MainViewModelFactory的构造函数中接收了一个countReserved参数。 在create()方法中创建了MainViewModel的实例,并带上了参数。