Viewmodel
官方介绍
ViewModel is a class that is responsible for preparing and managing the data for an Activity or a Fragment. It also handles the communication of the Activity / Fragment with the rest of the application (e.g. calling the business logic classes).
特点:
1.单一职责,将数据从业务中抽离出来。即只要是界面上看的到的数据,相关变量都应该存放在ViewModel,而不是Activity中
2.生命周期长,存在于所属对象(Activity,Fragment)的全部生命周期。
3.扩展性好,方便与其他功能组合,例如Livedata,room
使用场景:
1.横竖屏切换,Activity重建,数据可依然保存
2.同一个Activity下,Fragment之间的数据共享
注意事项
1.不能将context或带有Context引用的对象传入ViewModel中,这可能会导致页面无法被销毁,从而导致内存泄漏
2.同一个Activity下的fragment通过ViewModel进行数据共享时,ViewModel的拥有者是Activity,传参要注意。
3.一个Activity对应一个ViewModel
基本用法
添加依赖
implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
相关代码
新建一个MainActivity对应的MainViewModel类,并让它继承自ViewModel,加一个变量counter用于计数
class MainactyViewModel : ViewModel() {
var counter = 0;
/**
* 与之相关的Activity被销毁时,该方法会被系统调用。我们可以在该方法中执行一些资源释放的相关操作
*/
override fun onCleared() {
super.onCleared()
}
}
在布局界面添加一个按钮,每点击一次按钮,就让计数器加1,并把最新计数显示出来
<RelativeLayout
android:layout_below="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_counter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="0"
android:textStyle="bold"
android:textSize="20sp"/>
<TextView
android:id="@+id/tv_counter2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_counter"
android:text="0"
android:textStyle="bold"
android:textSize="20sp"
android:layout_centerHorizontal="true"/>
<Button
android:id="@+id/btn_plus"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="+1"
android:layout_below="@+id/tv_counter2"/>
</RelativeLayout>
在MainActivity中实现计数器逻辑
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = ViewModelProviders.of(this).get(MainactyViewModel::class.java)
btn_plus.setOnClickListener{
viewModel.counter++
counter2++
refreshCounter()
}
refreshCounter()
}
private fun refreshCounter() {
tv_counter.setText(viewModel.counter.toString())
tv_counter2.setText(counter2.toString())
}
我们不能直接去创建ViewModel的实例,而是一定要通过ViewModelProviders来获取ViewModel的实例,具体语法规则如下:
ViewModelProviders.of(<Activity或者Fragment实例>).get(<ViewModel>::class.java)
之所以这么写,是因为viewModel的生命周期长于Activity.如果我们在onCreate()方法中创建ViewModel的实例,那么每次onCreate方法执行的时候,ViewModel都会创建一个新的实例,当手机屏幕发生旋转的时候,就无法保留其中的数据。
测试结果
点击+1按钮三次,tv_counter和tv_counter2的值均为3,将屏幕旋转,tv_counter的值为3,tv_counter2的值为0,再次点击+1按钮,tv_counter值为4,tv_counter2值为1
向ViewModel传递参数
在大多数情况下,我们需要通过构造函数来传递一些参数,我们可以借助ViwModelProvider.Factory来实现
相关代码
给MainactyViewModel的构造函数添加一个counter参数
class MainactyViewModel(counter : Int) : ViewModel() {
var counter = counter;
}
新建一个MainViewModelFactory类并实现ViwModelProvider.Factory接口,create方法执行的时机和Activity的周期无关,因此可以创建MainViewModel的实例
class MainViewModelFactory(private var counter :Int) : ViewModelProvider.Factory{
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return MainactyViewModel(counter) as T
}
}
在MainActivity中使用
viewModel = ViewModelProviders.of(this,MainViewModelFactory(counter2)).get(MainactyViewModel::class.java)
AndroidViewModel
前面提到了,在使用ViewModel的时候,不能将Context传入ViewModel。但如果实际运用中,的确需要使用context,此时可以使用AndroidViewModel类。该类继承自ViewModel,并接收Application作为Context,这意味着它的生命周期和Application是一样的。