Android Jetpack架构组件(三)—ViewModel

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Jetpack架构组件(三)—ViewModel相关的知识,希望对你有一定的参考价值。

参考技术A ViewModel:是以感知生命周期的形式来存储和管理视图相关的数据。

ViewModel主要有以下的特点:

继承ViewMode,实现自定义ViewModel。

在Activity中使用LoginViewModel

首先查看ViewModel源码

可以看到,我们并没有手动调用 ViewModel 的构造函数来创建 ViewModel 实例,而是由 ViewModelProvider 来获取,其实ViewModel 初始化是在ViewModelProvider内部自己通过反射来构建出 ViewModel 实例。

ViewModelProvider 一共包含三个构造函数,可以看到,不管是哪种方式,最终都是要拿到两个构造参数:ViewModelStore 和 Factory,且都不能为 null。

Activity和Fragment实现了ViewModelStoreOwner和HasDefaultViewModelProviderFactory接口,这两个接口分别提供了ViewModelStore 和 Factory;

既然 Factory 实例也有了,下一步就是来调用 ViewModelProvider(this).get() 方法了。get() 方法需要我们传入 Class 对象,ViewModelProvider 需要拿到 Class 才能完成反射操作。在此方法里主要是通过 modelClass 来自动生成一个字符串 Key,并将参数转发给另外一个 get() 方法。

该方法会通过 key 从 ViewModelStore 里取 ViewModel 实例,如果取不到值或者是取出来的值类型不符,则会通过 mFactory.create(modelClass) 方法来反射初始化 ViewModel,并在返回初始化结果前将它存到 mViewModelStore 中,这样就完成了 ViewModel 的初始化流程了。

结论:ViewModel保持不变是因为ViewModelStore没有变化。

原因:Activity 每次获取 ViewModel 实例都会先尝试从 mViewModelStore 中取值,只有在取不到值的时候才会去重新构建一个新的 ViewModel 实例,且构建后的 ViewModel 实例也会被保存在mViewModelStore 中。那既然 Activity 可以在页面销毁重建的情况下获取到之前的 ViewModel 实例,那么不也就间接说明了在这种情况下 ViewModelStore 也是一直被保留着而没有被回收。

ComponentActivity 的 getViewModelStore() 方法获取 ViewModelStore 实例的来源有两种:

这里只要看第一种情况

NonConfigurationInstances 是 ComponentActivity 的一个静态内部类,其内部就包含了一个 ViewModelStore 成员变量,在 Activity 被重建时,其对应的 ViewModelStore 就被保存在了这。

通过查找引用,可以找到 ComponentActivity 就是在 onRetainNonConfigurationInstance() 方法里来完成 NonConfigurationInstances.viewModelStore 变量的赋值。从该方法名可以猜出,该方法就用于获取非配置项实例,以便在后续重建 Activity 时恢复数据。

通过查找方法引用,可以知道 onRetainNonConfigurationInstance() 又是被父类 android.app.Activity 的以下方法所调用,由父类去负责保留 NonConfigurationInstances 对象。

在 ActivityThread 类的以下方法存在调用,该方法用于回调 Activity 的 onDestroy 方法,在回调前会先将数据保存到 ActivityClientRecord 的 lastNonConfigurationInstances 字段中。

在重新启动 Activity 时,又会将数据 attach 到新的 Activity 实例上,将其作为 getLastNonConfigurationInstance() 方法的返回值。通过这种数据交接,重建前的 ViewModelStore 实例就会被重建后的 Activity 拿到,当中就保留了重建前 Activity 初始化的所有 ViewModel 实例,从而保障了 ViewModel 实例的不变性。

ViewModelProvider 提供的 Factory 接口实现类有两个:

如果想要通过其它类型的构造函数来初始化 ViewModel 的话,就需要我们自己来实现 ViewModelProvider.Factory 接口声明初始化逻辑了:

如果想使用同一个ViewModel类对应不同的实例对象,那么就需要在初始化的时候主动为它们指定不同的 Key,这样它们就可以一起被存到 ViewModelStore 的 HashMap 中了。

ViewModel回收是由于ViewModelStore清空 HashMap。

在 ComponentActivity 中通过监听Lifecycle状态,在Activity 在收到 ON_DESTROY 事件时,如果判断到是由于配置项更改导致了 Activity 被销毁,那么就不会调用 getViewModelStore().clear() 。如果是正常退出 Activity,那就会调用 getViewModelStore().clear() 方法,这样就会清空掉所有缓存的 ViewModel 实例了,ViewModel 的 clear() 方法也同时会被调用。

参考: 从源码看Jetpack

Android Jetpack架构组件——Lifecycle使用篇

概述

Lifecycle是一个持有组件生命周期状态的class,并且允许其他组件来观察生命周期的变化。并不局限于Activity或者Fragment。我们只知道生命周期是由操作系统或者进程中运行的代码进行管理。而且生命周期是Android工作原理的核心,所以应用必须遵循它们。否则会引起OOM或者Crash。

为什么需要使用Lifecycle管理生命周期

在此我们用官网提供的一个示例:

 internal class MyLocationListener(
        private val context: Context,
        private val callback: (Location) -> Unit
) 

    fun start() 
        // connect to system location service
    

    fun stop() 
        // disconnect from system location service
    


class MyActivity : AppCompatActivity() 
    private lateinit var myLocationListener: MyLocationListener

    override fun onCreate(...) 
        myLocationListener = MyLocationListener(this)  location ->
            // update UI
        
    

    public override fun onStart() 
        super.onStart()
        myLocationListener.start()
        // manage other components that need to respond
        // to the activity lifecycle
    

    public override fun onStop() 
        super.onStop()
        myLocationListener.stop()
        // manage other components that need to respond
        // to the activity lifecycle
    

从逻辑上看这段代码其实没什么问题,但是在真实应用场景中,我们需要管理很多组件和当前页面的调用,以响应当前生命周期的状态。所以会导致我们在onStop和onStart中存放大量的代码,导致它们难以维护。

所以官方提供lifecycle就是为了可以帮助我们以弹性和隔离的方式解决这些问题。

如何使用Lifecycle

lifecycle依赖

如果需要使用lifecycle的依赖,需要家google的maven仓库添加到项目中,在项目中的build.gradle新增:

allprojects 
    repositories 
        google()
    

在你的工程目录中的build.gradle中新增如下依赖:

dependencies 
    def lifecycle_version = "2.2.0"
    def arch_version = "2.1.0"

    // ViewModel
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
    // LiveData
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
    // Lifecycles only (without ViewModel or LiveData)
    implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"

    // Saved state module for 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
    implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"

    // optional - helpers for implementing LifecycleOwner in a Service
    implementation "androidx.lifecycle:lifecycle-service:$lifecycle_version"

    // optional - ProcessLifecycleOwner provides a lifecycle for the whole application process
    implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"

    // optional - ReactiveStreams support for LiveData
    implementation "androidx.lifecycle:lifecycle-reactivestreams-ktx:$lifecycle_version"

    // optional - Test helpers for LiveData
    testImplementation "androidx.arch.core:core-testing:$arch_version"

使用方法

  • 生命周期拥有者使用getLifecycle()或者实例,然后通过addObserver添加观察者。
  • 观察者实现LifecycleObserver接口,通过使用OnLifecycleEvent注解关注相应的生命周期。

使用示例

我们这里依然使用官方的例子:

class MyObserver : LifecycleObserver 

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun connectListener() 
        ...
    

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun disconnectListener() 
        ...
    


myLifecycleOwner.getLifecycle().addObserver(MyObserver())

首先MyObserver实现LifecycleObserver接口,并使用ON_RESUME和ON_PAUSE注解对方法加上了生命周期的限制。然后拥有者直接通过添加观察者的形式进行调用即可。

实现自定义的LifecycleOwner

在26.1.0及更高版本中的Fragment和Activity已经默认实现了LifecycleOwner接口。
如果你需要自定义类并希望它成为LifecycleOwner。你可以使用 LifecycleRegistry。示例代码如下:

class MyActivity : Activity(), LifecycleOwner 

    private lateinit var lifecycleRegistry: LifecycleRegistry

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)

        lifecycleRegistry = LifecycleRegistry(this)
        lifecycleRegistry.markState(Lifecycle.State.CREATED)
    

    public override fun onStart() 
        super.onStart()
        lifecycleRegistry.markState(Lifecycle.State.STARTED)
    

    override fun getLifecycle(): Lifecycle 
        return lifecycleRegistry
    

通过makeState设置Lifecycle的各种状态,然后通过getLifecycle返回该实例。

实战

我们简单通过lifecycle对应用的前后台进行一个监听。我们先看下我们使用lifecycle之前是怎么对应用的前后台监听的。

/**
 * @date:2020/12/30
 * @author:Silence
 * @describe:
 **/
 open class BaseActivityLifecycleCallback : Application.ActivityLifecycleCallbacks 

    private var currentResumedActivity: WeakReference<Activity>? = null

    //监听app前后台的监听器
    private var listener: OnAppStatusListener? = null

    //打开的Activity数量统计
    private var activityStartCount = 0

    fun registerAppStatusListener(listener: OnAppStatusListener) 
        this.listener = listener
    

    override fun onActivityPaused(activity: Activity) 
        currentResumedActivity == null
    

    override fun onActivityStarted(activity: Activity) 
        activityStartCount++
        if (activityStartCount == 1) 
            listener?.onAppFront()
        
    

    override fun onActivityDestroyed(activity: Activity) = Unit

    override fun onActivitySaveInstanceState(activity: Activity, bundle: Bundle) = Unit

    override fun onActivityStopped(activity: Activity) 
        activityStartCount--
        if (activityStartCount == 0) 
            listener?.onAppBackground()
        
    

    override fun onActivityCreated(activity: Activity, bundle: Bundle?) = Unit

    override fun onActivityResumed(activity: Activity) 
        currentResumedActivity = WeakReference(activity)
    

    fun getCurrentActivity(): Activity? 
        return currentResumedActivity?.get()
    

是不是感觉代码有点繁琐?需要通过对activity记数来判断应用是否在前后台。那么如果我们通过lifecycle会变成怎样呢?那我们来看一下:

/**
 * @date:2021/02/08
 * @author:Silence
 * @describe:
 **/
class AppLifeObserver : LifecycleObserver 

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun onForeground() 
    

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun onBackground() 
    

然后我们在application直接调用如下代码:

ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifeObserver())

是不是相对之前的记数方法简单很多。而且通过lifecycle的实现可读性相对而言会更高。

总结

本篇我们简单介绍了lifecycle的使用以及通过尝试使用lifecycle这个歌简单的例子对app前台后做监听。可以发现lifecycle的使用很简单。但是本篇并没有讲到lifecycle的原理。因为你只有会使用了才会愿意了解原理。那么下一篇我们讲lifecycle的实现原理。

参考

官方文档

本文首发于我的个人博客:Android Jetpack架构组件——Lifecycle使用篇
更多文章请关注我的公众号:码农职场

以上是关于Android Jetpack架构组件(三)—ViewModel的主要内容,如果未能解决你的问题,请参考以下文章

Android Jetpack架构组件——什么是Jetpack?

Android Jetpack架构组件——什么是Jetpack?

Android高级Jetpack架构组件+Jetpack compose强化实战

Android Jetpack架构组件带你了解Android Jetpack

Android Jetpack架构组件(入门教程及进阶实战)独家首发

Android Jetpack中DataBinding将布局视图绑定到架构组件