• Android Jetpack组件架构:ViewModel的原理


    Android Jetpack组件架构:ViewModel的原理

    在这里插入图片描述

    导言

    本篇文章是关于介绍ViewModel的,由于ViewModel的使用还是挺简单的,这里就不再介绍其的基本应用,我们主要来分析ViewModel的原理。

    ViewModel的生命周期

    众所周知,一般使用ViewModel是用来解决两个问题的,第一个就是关于设备配置发生改变时Activity先前状态的保存,在ViewModel出来之前我们一般会使用saveInstanceState这个Bundle来进行状态的保存,但是这样做能存储的数据是有限的,结构也不够明确,ViewModel作为一个生命周期大于Activity的组件就可以帮我们实现状态的存储,下面是ViewModel生命周期与Activity对比的图:
    在这里插入图片描述
    可以看到直到Activity被完全Destory时ViewModel中的数据才会被清除。

    我们使用ViewModel的第二个原因就是用来实现MVVM架构,可以通过DataBinding组件和ViewModel组件以及LiveData组件一起实现MVVM架构,这样可以减轻Activity的职责,避免Activity过于臃肿。

    获得ViewModel的提供者

    我们从ViewModel的创建开始分析其原理,这里用我们上一篇文章的例子:

    mViewModel = ViewModelProvider(this).get(SimpViewModel::class.java)
    
    • 1

    这里首先会通过ViewModelProvider的构造方法获得一个ViewModelProvider的实例,其构造方法如下所示:

    public constructor(
        owner: ViewModelStoreOwner
    ) : this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))
    
    constructor(
        private val store: ViewModelStore,
        private val factory: Factory,
        private val defaultCreationExtras: CreationExtras = CreationExtras.Empty,
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    可以看到我们调用的是第一个构造方法,最终会调用到第二个构造方法中,也就是主构造方法中,这个构造来说就是指定了三个成员变量,分别是ViewModelStore,FactoryCreationExtras三个类型的参数,我们先来分别介绍一下这三个类型。

    ViewModelStore – ViewModel的拥有者

    着整个类比较小,但是也是有注释的,我们先来看看注释:
    在这里插入图片描述
    这段注释中比较重要的信息就是ViewModel是真正用来存储ViewModel的类并且在configuration changes就是配置发生改变时新的实例和旧的实例中的信息是一致的。只有当持有者不再会被recreated时里面的数据才会通过clear清除。

    接下来我们来看该类的源码:

    public class ViewModelStore {
    
        private final HashMap<String, ViewModel> mMap = new HashMap<>();
    
        final void put(String key, ViewModel viewModel) {
            ViewModel oldViewModel = mMap.put(key, viewModel);
            if (oldViewModel != null) {
                oldViewModel.onCleared();
            }
        }
    
        final ViewModel get(String key) {
            return mMap.get(key);
        }
    
        Set<String> keys() {
            return new HashSet<>(mMap.keySet());
        }
    
        public final void clear() {
            for (ViewModel vm : mMap.values()) {
                vm.clear();
            }
            mMap.clear();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    可以看到具体是用一个哈希表来存储viewModel的实例的,里面唯一比较大的方法就是put方法,里面做的处理就是将替换出来的旧的viewModel实例给清理掉。

    Factory – ViewModel的创建工厂

    这个Factory是一个定义在ViewModelProvider的内部接口,它的主要职责是用来初始化ViewModel,说实话就是一个工厂。我们来看其定义:

        public interface Factory {
            public fun <T : ViewModel> create(modelClass: Class<T>): T {
                throw UnsupportedOperationException(
                    "Factory.create(String) is unsupported.  This Factory requires " +
                        "`CreationExtras` to be passed into `create` method."
                )
            }
    
            public fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T =
                create(modelClass)
    
            companion object {
                @JvmStatic
                fun from(vararg initializers: ViewModelInitializer<*>): Factory =
                    InitializerViewModelFactory(*initializers)
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    不过这里是一个抽象的,我们来找一个具体的,也就是defaultFactory方法获取的工厂:

    public companion object {
        internal fun defaultFactory(owner: ViewModelStoreOwner): Factory =
            if (owner is HasDefaultViewModelProviderFactory)
                owner.defaultViewModelProviderFactory else instance
         	......       
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这个方法的逻辑就是判断ViewModel的持有者是不是有默认的工厂方法,如果有的话就获取持有者的默认工厂,否则返回的是自身的instance实例,至于这个instance实例是在NewInstanceFactory这个实现了Factory接口的工厂类中定义的伴生变量,具体逻辑是:

    @JvmStatic
    public val instance: NewInstanceFactory
        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        get() {
            if (sInstance == null) {
                sInstance = NewInstanceFactory()
            }
            return sInstance!!
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    可以看到instance是一个静态的单例,他具体指向的还是这个NewInstanceFactory类,至于它是如何初始化/创建ViewModel的实例的我们可以看一眼它的create方法:

    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return try {
            modelClass.newInstance()
        } catch (e: InstantiationException) {
            throw RuntimeException("Cannot create an instance of $modelClass", e)
        } catch (e: IllegalAccessException) {
            throw RuntimeException("Cannot create an instance of $modelClass", e)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这个传入的modelClass就是我们传入的.class文件:

    ViewModelProvider(this).get(SimpViewModel::class.java)
    
    • 1

    所以可以看到,这个默认情况下的工厂就是直接用反射生成了对应ViewModel的实例。

    CreationExtras – 构建ViewModel时的额外参数

    首先我们来看一看注释的内容:
    在这里插入图片描述
    简单来说它就是为工厂生成实例的时候提供额外参数的,这些参数使用一个Map来存储的了,不过默认情况下我们并不需要实现额外的工厂,所以这个类型我们先略过。

    获得ViewModel的实例

    前面我们已经知道了通过构造ViewModelProvider我们可以获得其Factory了,接下来继续向下看:

    ViewModelProvider(this).get(SimpViewModel::class.java)
    
    • 1

    我们看一看get方法做了什么:

        public open operator fun <T : ViewModel> get(modelClass: Class<T>): T {
            val canonicalName = modelClass.canonicalName
                ?: throw IllegalArgumentException("Local and anonymous classes can not be ViewModels")
            return get("$DEFAULT_KEY:$canonicalName", modelClass)
        }
    
        public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
            val viewModel = store[key]
            if (modelClass.isInstance(viewModel)) {
                (factory as? OnRequeryFactory)?.onRequery(viewModel)
                return viewModel as T
            } else {
                @Suppress("ControlFlowWithEmptyBody")
                if (viewModel != null) {
                    // TODO: log a warning.
                }
            }
            val extras = MutableCreationExtras(defaultCreationExtras)
            extras[VIEW_MODEL_KEY] = key
            return try {
                factory.create(modelClass, extras)
            } catch (e: AbstractMethodError) {
                factory.create(modelClass)
            }.also { store.put(key, it) }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    可以看到第一个方法会调用第二个方法,其中第一个方法向第二个方法传入的的第一个String类型的参数是通过DEFAULT_KEY和我们传入的类的类名拼接而成的。然后跳转到第二个方法之中去,首先会尝试从ViewModleStroe中获取对应Key对应的ViewModel,但是一般第一次创建时应该会为null,所以之后跳转的应该是最后return块中的factory.create方法之中,这个方法我们在之前Factory的介绍中提到过了,具体就是通过反射实例化ViewModel的,并且最后将其放入到ViewModelStore对象之中去。

    至于多次获取同一个ViewModel实例是会跳转到:

     if (modelClass.isInstance(viewModel)) {
                (factory as? OnRequeryFactory)?.onRequery(viewModel)
                return viewModel as T
            } 
    
    • 1
    • 2
    • 3
    • 4

    这一段中去,这里onRequery默认是无实现的,也就是说并不会对viewModel做任何的处理。

    ViewModelStore在哪里被创建

    既然ViewModel是被存储在ViewModelStore之中的,那ViewModelStore究竟是在哪里被创建出来的呢?我们可以在ComponentActivity之中找到答案:

    public ViewModelStore getViewModelStore() {
        if (getApplication() == null) {
            throw new IllegalStateException("Your activity is not yet attached to the "
                    + "Application instance. You can't request ViewModel before onCreate call.");
        }
        ensureViewModelStore();
        return mViewModelStore;
    }
    
    void ensureViewModelStore() {
        if (mViewModelStore == null) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance(); //获得上次配置更改的相关参数
            if (nc != null) { //当存在上次的参数时
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore; //恢复上次的参数
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore(); //创建一个新的ViewModelStore
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    可以看到这里在获得ViewModelStore时主要是通过一个NonConfigurationInstances ,而该参数是一个静态的对象,也就是说,它是一个单例,这样就保证了Activity在整个生命周期之中只有一个ViewModelStore实例,从而实现配置改变时也可以恢复数据的作用。

    Activity的默认工厂

    在看ComponentActivity的源码时,发现了原来Activity也是有默认工厂的,它的具体实现如下:

        constructor(application: Application?, owner: SavedStateRegistryOwner, defaultArgs: Bundle?) {
            savedStateRegistry = owner.savedStateRegistry
            lifecycle = owner.lifecycle
            this.defaultArgs = defaultArgs
            this.application = application
            factory = if (application != null) getInstance(application)
                else ViewModelProvider.AndroidViewModelFactory()
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这个方法最终设置到的工厂类都是一个名为AndroidViewModelFactory的工厂类:

        public open class AndroidViewModelFactory
        private constructor(
            private val application: Application?,
            // parameter to avoid clash between constructors with nullable and non-nullable
            // Application
            @Suppress("UNUSED_PARAMETER") unused: Int,
        ) : NewInstanceFactory() {
        
            @Suppress("SingletonConstructor")
            public constructor() : this(null, 0)
    
            @Suppress("SingletonConstructor")
            public constructor(application: Application) : this(application, 0)
    
            @Suppress("DocumentExceptions")
            override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
                return if (application != null) {
                    create(modelClass)
                } else {
                    val application = extras[APPLICATION_KEY]
                    if (application != null) {
                        create(modelClass, application)
                    } else {
                        // For AndroidViewModels, CreationExtras must have an application set
                        if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {
                            throw IllegalArgumentException(
                                "CreationExtras must have an application by `APPLICATION_KEY`"
                            )
                        }
                        super.create(modelClass)
                    }
                }
            }
    
            @Suppress("DocumentExceptions")
            override fun <T : ViewModel> create(modelClass: Class<T>): T {
                return if (application == null) {
                    throw UnsupportedOperationException(
                        "AndroidViewModelFactory constructed " +
                            "with empty constructor works only with " +
                            "create(modelClass: Class, extras: CreationExtras)."
                    )
                } else {
                    create(modelClass, application)
                }
            }
    
            @Suppress("DocumentExceptions")
            private fun <T : ViewModel> create(modelClass: Class<T>, app: Application): T {
                return if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {//如果传入的Class类型实现的接口和AndroidViewModel::class一致,及说明也有生命周期,调用构造犯法并且传入application对象
                    try {
                        modelClass.getConstructor(Application::class.java).newInstance(app)
                    } catch (e: NoSuchMethodException) {
                        throw RuntimeException("Cannot create an instance of $modelClass", e)
                    } catch (e: IllegalAccessException) {
                        throw RuntimeException("Cannot create an instance of $modelClass", e)
                    } catch (e: InstantiationException) {
                        throw RuntimeException("Cannot create an instance of $modelClass", e)
                    } catch (e: InvocationTargetException) {
                        throw RuntimeException("Cannot create an instance of $modelClass", e)
                    }
                } else super.create(modelClass)
            }
    
            public companion object {
                internal fun defaultFactory(owner: ViewModelStoreOwner): Factory =
                    if (owner is HasDefaultViewModelProviderFactory)
                        owner.defaultViewModelProviderFactory else instance
    
                internal const val DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey"
    
                private var sInstance: AndroidViewModelFactory? = null
                
                @JvmStatic
                public fun getInstance(application: Application): AndroidViewModelFactory {
                    if (sInstance == null) {
                        sInstance = AndroidViewModelFactory(application)
                    }
                    return sInstance!!
                }
                
                private object ApplicationKeyImpl : Key<Application>
                
                @JvmField
                val APPLICATION_KEY: Key<Application> = ApplicationKeyImpl
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    具体通过getInstance方法就跳转到了这里,可以发现似乎工厂类都是一个单例的模式,这个工厂的特殊之处就是他的create方法涉及到了Application对象的传入,比如说这里的newInstance方法:

    modelClass.getConstructor(Application::class.java).newInstance(app)
    
    public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
    {
        if (serializationClass == null) {
            return newInstance0(initargs);
        } else {
            return (T) newInstanceFromSerialization(serializationCtor, serializationClass);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    也就是说整个被传入Application的生命周期内都只有一个实例,这样由于创建的实例在生命周期范围内的单例性和ViewModelStore的单例性,整个ViewModel就可以实现在整个Activity的生命周期内(发生意外,比如说配置改变时)数据不变更的作用。

  • 相关阅读:
    RabbitMQ的入门篇
    QFile文件读写操作QFileInFo文件信息读取
    Linux系统调优详解(十二)——IO调优之磁盘测速
    Java方法的重载
    在Linux安装nginx
    数据挖掘可视化+机器学习初探
    IPv6静态路由与路由汇总【下一代互联网01】
    【编译原理】概述
    简易步骤控制器
    ubuntu搭建安卓开发环境
  • 原文地址:https://blog.csdn.net/Tai_Monster/article/details/133313405