您当前的位置:首页 > 计算机 > 编程开发 > 安卓(android)开发

安卓开发ViewModel和LiveData

时间:02-07来源:作者:点击数:

ViewModel

ViewModel主要用于保存数据,而且在Activity非法销毁时数据也不会丢失,示例如下:

在这里插入图片描述

如上图,一个TextView和一个Button,点击按钮后生成一个随机数,并显示到TextView上,代码如下:

class MainActivity : AppCompatActivity() {

    var number: Int = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val numberText = findViewById<TextView>(R.id.number_text)
        val button = findViewById<Button>(R.id.button)

        numberText.text = number.toString()

        button.setOnClickListener {
            number = Random().nextInt(10)
            numberText.text = number.toString()
        }
    }

}

运行程序,然后点击“生成随机数”按钮,效果如下:

在这里插入图片描述

然后我们旋转手机屏幕,发现之前生成的随机数变为了0,如下:

在这里插入图片描述

这是因为在旋转屏幕的时候,MainActivity被销毁了,保存在其中的number属性也跟着销毁了,然后系统重新创建了一个MainActivity,此时的number属性默认值为0,所以显示就是0了。使用ViewModel就可以解决这个问题,如下:

  1. 添加依赖:
    implementation "androidx.activity:activity-ktx:1.3.0"
    implementation "androidx.fragment:fragment-ktx:1.3.6"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0-alpha03"
    
    本来添加lifecycle-viewmodel-ktx依赖就可以了的,但是viewModels()函数是在高版本的fragment-ktxactivity-ktx中才有,默认的appcompat中也会依赖这两个,但是依赖的版本比较低,所以需要单独依赖这两个的最新版本。

还有很多的相关依赖,可查看官方文档:https://developer.android.google.cn/jetpack/androidx/releases/lifecycle#declaring_dependencies

def lifecycle_version = "2.5.0-beta01"
def arch_version = "2.1.0"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// ViewModel utilities for Compose (用于 Compose 的 ViewModel 实用程序)
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
// Lifecycles only (without ViewModel or LiveData) (仅生命周期(没有 ViewModel 或 LiveData))
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
// Saved state module for ViewModel (ViewModel 的已保存状态模块)
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"
// Annotation processor (注释处理器)
// kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
// alternately - if using Java8, use the following instead of lifecycle-compiler (或者 - 如果使用 Java8,请使用以下内容而不是生命周期编译器)
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
// optional - helpers for implementing LifecycleOwner in a Service (在服务中实现 LifecycleOwner 的助手)
implementation "androidx.lifecycle:lifecycle-service:$lifecycle_version"
// optional - ProcessLifecycleOwner provides a lifecycle for the whole application process (ProcessLifecycleOwner 为整个应用程序流程提供生命周期)
implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"
// optional - ReactiveStreams support for LiveData (对 LiveData 的 ReactiveStreams 支持)
implementation "androidx.lifecycle:lifecycle-reactivestreams-ktx:$lifecycle_version"
// optional - Test helpers for LiveData (LiveData 的测试助手)
testImplementation "androidx.arch.core:core-testing:$arch_version"
  1. 代码:
    class MyViewModel : ViewModel() {
        var number: Int = 0
    }
    
    class MainActivity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            val model: MyViewModel by viewModels()
    
            val numberText = findViewById<TextView>(R.id.number_text)
            val button = findViewById<Button>(R.id.button)
    
            numberText.text = model.number.toString()
    
            button.setOnClickListener {
                model.number = Random().nextInt(10)
                numberText.text = model.number.toString()
            }
        }
    
    }
    

再次运行就OK了,不论怎么旋转屏幕,number属性的值都得以保留,这是因为我们把number属性声明在ViewModel中,之前是声明在MainActivity中的,这说明MainActivity在被非法销毁时,ViewModel并没有跟着被销毁,所以当MainActivity重新创建的时候取出来的ViewModel就是之前的ViewModel

LiveData

LiveData用于包装数据,让数据可以被观察,这也是它和ViewModel的区别,把数据放在ViewModel中,是为了Activity在非法销毁时数据依然存在,而把数据放在LiveData中,是为了让数据可以被观察,那如果想实现数据可以被观察,数据又不会因Activity非法销毁而销毁,则可以把数据放在LiveData中,再把LiveData放在ViewModel中,我们修改前面ViewModel的例子,如下:

  1. 添加依赖
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.0-alpha03"
    
  2. 代码
class MyViewModel : ViewModel() {
    var numberLiveData: MutableLiveData<Int> = MutableLiveData<Int>().apply { value = 0 }
}

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val model: MyViewModel by viewModels()

        val numberText = findViewById<TextView>(R.id.number_text)
        val button = findViewById<Button>(R.id.button)

        model.numberLiveData.observe(this) { number ->
            numberText.text = number.toString()
        }

        button.setOnClickListener {
            model.numberLiveData.setValue(Random().nextInt(10))
        }
    }

}

如上代码,非常简单,numberLiveData的类型就是一个MutableLiveData,我们把int类型的的值包装在这里面,这样就可以被观察了,在MainActivity中,给这个MutableLiveData添加观察者,以监听int值的变化。

相关链接:

https://proandroiddev.com/optimizing-viewmodel-with-lifecycle-2-2-0-a2895b5c01fd

https://docs.google.com/document/d/1KkxQXDtA3zJ1ledARXr3IRdciYdrIJj40zNwRSJP6zU/edit?rm=minimal

其它

注意:ViewModel绝不能引用ViewLifecycle或可能存储对Activity上下文的引用的任何类。

如果ViewModel需要Application上下文(例如,为了查找系统服务),它可以扩展AndroidViewModel类并设置用于接收Application的构造函数,因为Application类会扩展Context。示例如下:

class MyViewModel(application: Application) : AndroidViewModel(application) {
    var numberLiveData: MutableLiveData<Int> = MutableLiveData<Int>().apply { value = 0 }
}

这样通过viewModel就能获取到Application的引用了,如下:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val model: MyViewModel by viewModels()
        val application = model.getApplication<MyApplication>()
    }

}

当然,也可以在ViewModel的内部调用getApplication<MyApplication>()来获取Application的引用。

ViewModel的生命周期:

在这里插入图片描述

可以看到,当调用Activityfinish()函数时,ViewModel才会消失,且此时ViewModel中的onCleared()函数被调用,以便它可以清理资源。对于Fragment,则ViewModel的消失是在Fragment分离时。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门