美文网首页Android开发经验谈
ViewModel原理解析,人人都能看得懂!

ViewModel原理解析,人人都能看得懂!

作者: 刨坑 | 来源:发表于2022-03-28 13:45 被阅读0次

    作者:不说话的匹诺槽
    转载地址:https://juejin.cn/post/7079775840757612558

    前言

    今天想跟大家聊的是Jetpack 组件之ViewModel , 之前对ViewModel 也是一知半解,只是从博客上大概了解过它的原理,自己并没有深入了解过,所以打算自己由浅入深的领略一下,本人自己感觉不算是聪明的那类人,连我都能看得懂,相信各位肯定没问题,人人都能看得懂!

    先来看看ViewModel是什么?

    ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel 类让数据可在发生屏幕旋转等配置更改后继续留存。

    架构组件为界面控制器提供了 ViewModel 辅助程序类,该类负责为界面准备数据。在配置更改期间会自动保留 ViewModel 对象,以便它们存储的数据立即可供下一个 activity 或 fragment 实例使用。

    developer.android.com/topic/libra…

    这两个介绍摘自于官网文档,其中说明ViewModel的作用,主要是保存UI数据的。我们来看看它的生命周期:

    可以看到ViewModel在UI整个生命周期范围内都是同一个状态内的,当UI销毁的时候才会执行onCleard()操作,清除数据。

    使用

    接下来,我们看下ViewModel 的简单使用

    首先我们创建一个ViewModel

    class MainViewModel : ViewModel() {
        val liveData = MutableLiveData<String>("这是LiveData的数据")
    }
    

    然后在UI中进行获取到MainViewModel,并进行数据订阅

    class MainActivity3 : AppCompatActivity() {
        private lateinit var viewModel: MainViewModel
        private lateinit var binding:MainFragmentBinding
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.main_fragment)
    
            viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
    
            viewModel.liveData.observe(this){
                binding.message.text = it
            }
        }
    }
    

    先实例化ViewModelProvider,通过ViewModelProvider::get 方法来获取MainViewModel 的实例对象,通过这种创建形式,不难看出,ViewModel 的创建是通过反射来创建的。

    那么 ViewModle 是如何保存数据的呢?今天就由浅入深的带大家来领略一下 ViewModel 的奥妙之处,保证人人都能看得懂!

    原理

    首先我们从ViewModelProvider 看起,先了解一下它的面目。

        public constructor(
            owner: ViewModelStoreOwner
        ) : this(owner.viewModelStore, defaultFactory(owner))
    

    在这里,这个构造方法 新建了一个默认的工厂,然后调用了自己默认的构造方法。其中这个工厂就是用来创建ViewModel的。

    接下来看看get方法做了点什么?

        @MainThread
        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)
        }
    

    这里就是增加了一个默认的Key值,然后调用另外一个get方法,这个默认Key

    internal const val DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey"
    

        @MainThread
        public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
            var viewModel = store[key]
            if (modelClass.isInstance(viewModel)) {
                (factory as? OnRequeryFactory)?.onRequery(viewModel)
                return viewModel as T
            } else {
               ........
            }
            viewModel = if (factory is KeyedFactory) {
                factory.create(key, modelClass)
            } else {
                factory.create(modelClass)
            }
            store.put(key, viewModel)
            return viewModel
        }
    

    在这个方法中,可以看到,根据key值从store中获取ViewModel 对象,如果 类型正确 ,当即返回当前对象, 如果不正确的话,通过工厂创建新的ViewModel对象,存储到store中并返回。

    我们先看看工厂是如何创建VIewModel

        public open class NewInstanceFactory : Factory {      
            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)
                }
            }
        }
    

    这是前面 ViewModelProvider 创建的默认工厂,最后通过 modelClass.newInstance() 创建了 ViewModel的实例化对象。

    接下来我们看看看 store 是什么?

    store的类型是ViewModelStore

    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();
        }
    }
    

    可以看到它里面维护了一个HashMap,根据Key 值 来对ViewModel 进行存储。同时提供了clear()方法,将所有ViewModel 清除。

    这个ViewModelStore 是在什么时候创建的呢?

    在上文提到的创建ViewModelProvider的时候,可以看到,ViewModelStoreOwner 是由Activity 创建ViewModelProvider 的时候 传入的,然后调用owner中的 getViewModelStore() 方法,获取ViewModelStore ,并传到构造方法里面的。

        public constructor(
            owner: ViewModelStoreOwner
        ) : this(owner.viewModelStore, defaultFactory(owner))
    

    这是因为 AppCompatActivity 的父类 ComponentActivity 实现了 ViewModelStoreOwner 接口。

    public interface ViewModelStoreOwner {
        ViewModelStore getViewModelStore();
    }
    

    ViewModelStoreOwner 接口就很简单,只提供了一个 getViewModelStore() 方法 来获取ViewModelStore ,我们来看看它的实现。

        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;
        }
    

    这里面只是简单返回了 mViewModelStore 对象 ,重要的是ensureViewModelStore()方法。

        void ensureViewModelStore() {
            if (mViewModelStore == null) {
                NonConfigurationInstances nc =
                        (NonConfigurationInstances) getLastNonConfigurationInstance();
                if (nc != null) {
                    mViewModelStore = nc.viewModelStore;
                }
                if (mViewModelStore == null) {
                    mViewModelStore = new ViewModelStore();
                }
            }
        }
    
        static final class NonConfigurationInstances {
            Object custom;
            ViewModelStore viewModelStore;
        }
    

    这里主要是获取 NonConfigurationInstances 对象,然后从中获取到 viewModelStore ,如果NonConfigurationInstances为空的话,就新建一个 ViewModelStore 对象。

    接下来我们主要看下 getLastNonConfigurationInstance(); 方法。

        public Object getLastNonConfigurationInstance() {
            return mLastNonConfigurationInstances != null
                    ? mLastNonConfigurationInstances.activity : null;
        }
    

    我将这个方法的注释做了翻译:

    检索之前由onRetainNonConfigurationInstance返回的非配置实例数据。可以从对新实例的初始{@link #onCreate}和{@link #onStart}调用中获得,允许你从先前的实例中提取任何有用的动态状态。

    简单来说,这个方法就是用来获取 onRetainNonConfigurationInstance () 方法中存储的内容。

        public Object onRetainNonConfigurationInstance() {
            return null;
        }
    

    由系统调用,作为由于配置更改而销毁活动的一部分,当已知将立即为新配置创建新实例时。您可以在此处返回您喜欢的任何对象,包括活动实例本身,稍后可以通过调用 getLastNonConfigurationInstance()新的活动实例来检索这些对象。

    这样对比来看不难看出:

    onRetainNonConfigurationInstance() 是在Activity 销毁的时候 进行存储信息。

    getLastNonConfigurationInstance() 的作用是获取 存储的信息的。

    当屏幕发生旋转的时候 ,会先调用 onRetainNonConfigurationInstance先将数据进行保存,然后再通过 getLastNonConfigurationInstance 将保存的数据获取到。

    我们来看看ComponentActivityonRetainNonConfigurationInstance 的实现:

        public final Object onRetainNonConfigurationInstance() {
            Object custom = onRetainCustomNonConfigurationInstance();
            ViewModelStore viewModelStore = mViewModelStore;
            if (viewModelStore == null) {          
                NonConfigurationInstances nc =
                        (NonConfigurationInstances) getLastNonConfigurationInstance();
                if (nc != null) {
                    viewModelStore = nc.viewModelStore;
                }
            }
    
            if (viewModelStore == null && custom == null) {
                return null;
            }
    
            NonConfigurationInstances nci = new NonConfigurationInstances();
            nci.custom = custom;
            nci.viewModelStore = viewModelStore;
            return nci;
        }
    

    mViewModelStore 是在ensureViewModelStore 方法中获取的对象( 可能是通过 getLastNonConfigurationInstance()获取,可能是重新创建 ) ,如果这个 mViewModelStore 是空的话,就会尝试从NonConfigurationInstances 中获取,如果仍然是空,直接返回null,如果不是空的话, 重新创建nci 进行存储。

    那这些数据什么时候才会清除呢?

    ComponentActivity 的无参构造中,对生命周期做了一个监听,当页面进行销毁的时候,并且没有配置更改的时候,会执行

    mViewModelStoreclear() 方法,进行数据的释放操作。

        public ComponentActivity() {
            Lifecycle lifecycle = getLifecycle();
            ..........
            getLifecycle().addObserver(new LifecycleEventObserver() {
                @Override
                public void onStateChanged(@NonNull LifecycleOwner source,
                        @NonNull Lifecycle.Event event) {
                    if (event == Lifecycle.Event.ON_DESTROY) {
                        // Clear out the available context
                        mContextAwareHelper.clearAvailableContext();
                        // And clear the ViewModelStore
                        if (!isChangingConfigurations()) {
                            getViewModelStore().clear();
                        }
                    }
                }
            });
            ..........
        }
    

    整个流程,大概就是这样

    总结

    分析了这么多,我们捋一下:

    • 我们的Activity 的父类 ComponentActivity 实现了 ViewModelStoreOwner 接口,通过 ViewModelProvider 使用默认工厂 创建了 viewModel ,并通过唯一Key值 进行标识,存储到了 ViewModelStore 中。等下次需要的时候即可通过唯一Key值进行获取。

    • 由于ComponentActivity 实现了ViewModelStoreOwner 接口,实现了 getViewModelStore 方法,当屏幕旋转的时候,会先调用

    onRetainNonConfigurationInstance() 方法将 viewModelStore 保存起来,然后再调用 getLastNonConfigurationInstance 方法将数据恢复,如果为空的话,会重新创建 viewModelStore ,并存储在全局中,以便以下次发生变化的时候,能够通过onRetainNonConfigurationInstance 保存起来。

    • 最后当页面销毁并且没有配置更改的时候,会将viewModelStore 中的数据 进行清除操作。

    相关文章

      网友评论

        本文标题:ViewModel原理解析,人人都能看得懂!

        本文链接:https://www.haomeiwen.com/subject/fljpjrtx.html