Android Jetpack(4):ViewModel

ViewModel介绍

  • ViewModel类是被设计用来以可感知生命周期的方式存储和管理 UI相关数据为了更好的以生命周期的方式管理界面相关的数据。ViewModel中数据会一直存活,即使configuration发生改变(比如旋转屏幕),数据仍然可以存在不会销毁。
  • Android中的ViewModel是一个可以用来存储UI相关的数据的类

那ViewModel为什么可以管理这些数据呢?

主要还是因为ViewModel的生命周期比Activtiy、Fragment生命周期来的更长。

  • ViewModel类允许数据在配置变化(例如屏幕旋转)后存活。
  • 还记得MVP中的Model吗。这里的ViewModel有点类似MVP中的Model的作用。但是google出了一套AAC组件。这些组件让开发者能开发高效的项目。其中ViewModel也是其中组件之一。
  • AAC(Android Architecture Components) :实际上是android官方提供的一系列组件,用来实现MVVM架构的。

引入ViewModel的原因

  1. Activity或Fragment这类应用组件都有自己的生命周期,他们的生命周期都是被Framework所管理。Framework可能会根据用户的一些操作以及设备的状态对Activity或Fragment进行销毁和重建。作为开发者,这些行为我们是无法干预的。伴随着Activity或Fragment的销毁和重建,它们当中的数据也会随着一起销毁和重建。对于一些简单的数据,Activity可以使用onSaveInstanceState()方法,并从onCreate的bundle中重新获取,但这一方法仅仅适合一些简单的UI状态,对于列表型这种庞大的数据类型并不适合。

  2. Activity或Fragment经常会做一些异步的耗时操作,随之就需要管理这些异步操作得到的数据,并在destroyed的时候清理它们,从而避免内存溢出这类问题的发生。但是这样的处理会随着项目扩大而变得十分复杂。

  3. Activity或Fragment本身需要处理很多用户的输入事件并和操作系统打交道,当它们还要花时间管理那些数据资源时,它们所在的类就会变得异常庞大,造就出所谓的god activities和god fragments,这样很尴尬。

所以引入ViewModel之后,数据就可以从UI中分离出来,让每个模块的职责更加清晰合理。并且当Activity或Fragment重建的时候,ViewModel会自动保留之前的数据并给新的Activity或Fragment使用。

ViewModel可以解决以下痛点

1. 数据持久化

在屏幕旋转的时候会经历 Activity 的销毁与重新创建,这里就涉及到数据保存的问题,显然重新请求或加载数据是不友好的。在ViewModel 出现之前我们可以用 Activity 的 onSaveInstanceState() 机制保存和恢复数据,但缺点很明显,onSaveInstanceState只适合保存少量的可以被序列化、反序列化的数据,这种机制明显不合适。

ViewModel 生命周期图如下:

Android Jetpack(4):ViewModel_第1张图片
由图可知,ViewModel 生命周期是贯穿整个 activity 生命周期,包括 Activity 因旋转造成的重创建,直到 Activity 真正意义上销毁后才会结束。既然如此,用来存放数据再好不过了。

2. 异步回调问题

通常我们 App 需要频繁异步请求数据,比如调接口请求服务器数据。当然这些请求的回调都是相当耗时的,之前我们在 Activity 或Fragment里接收这些回调。所以不得不考虑潜在的内存泄漏情况,比如 Activity被销毁后接口请求才返回。处理这些问题,会给我们增添好多复杂的工作。但现在我们利用 ViewModel处理数据回调,可以完美的解决此痛点。意思只要继承我们的ViewModel后,可能会出现的bug,google都帮我们处理了。

3. 分担 UI controller 负担

从最早的 MVC 到目前流行的 MVP、MVVM,目的无非是 明确职责,分离 UI Controller 负担。 UI Controller比如 Activity 、Fragment是设计用来渲染展示数据、响应用户行为、处理系统的某些交互。如果再要求他去负责加载网络或数据库数据,会让其显得臃肿和难以管理。

所以为了简洁、清爽、丝滑,我们可以分离出数据操作的职责给 ViewModel。

4. Fragments 间共享数据

比如在一个 Activity 里有多个 Fragment,这 Fragment 之间需要做某些交互。我之前的做法是接口回调,需要统一在 Activity 里管理,并且不可避免的 Fragment 之间还得互相持有对方的引用。

ViewModel的生命周期

  • ViewModel对象的范围由获取ViewModel时传递至ViewModelProvider的Lifecycle所决定。ViewModel始终处在内存中,直到Lifecycle永久地离开—对于Activity来说,是当它终止(finish)的时候,对于Fragment来说,是当它分离(detached)的时候。
val mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)
  • Activity在生命周期中可能会触发多次onCreate(),而ViewModel则只会在第一次onCreate()时创建,然后直到最后Activity销毁。当ViewModel的实例生成之后,它会一直待在内存中,直到对应的Lifecycle彻底结束。
  • 下图展示了一个Activity经过旋转然后到结束运行这段期间各生命周期的状态,在Activity的生命周期旁边显示了其中ViewModel的生命周期。虽然这个图描述的是Activity的状态,但是Fragment和它是类似的。

Android Jetpack(4):ViewModel_第2张图片

  • 从图中可以看出,在第一次调用Activity对象的onCreate()方法时创建了一个ViewModel。在Activity运行过程中可能会多次调用onCreate()方法(比如当设备屏幕旋转时),但是ViewModel一直存在,直到Activity结束并销毁。这意味着ViewModel不会因为它的创建者的一个配置变化而被销毁,Activity的新实例将与现有的ViewModel重新连接。
  • 这里最大的亮点是以生命周期的方式。举例:假如在Activity里使用,他会贯穿整个Activity里的生命周期。
  • ViewModel只会在Activity存活,且只会创建一次。当销毁时,会主动调用onClered。
  • 因为在Activity存活时,只创建一次,那么在此Activity下的所有Fragment都可以共享一个ViewModel。
  • 由于 ViewModel 生命周期可能长与 activity生命周期,所以为了避免内存泄漏Google禁止在ViewModel中持有Context或activity或view的引用。如果非得使用Context,可以继承AndroidViewModel类中获取ApplicationContext。
  • 这张图也解释了为什么ViewModel中不能持有Activity、Fragment、view的引用。因为Activity在重建后是一个新的对象,如果ViewModel中持有旧对象的引用,这个旧对象可能就等不到释放,造成泄漏。
  • 之前我们在activity销毁重建时,可以用activity的onSaveInstanceState()机制保存和恢复数据,但缺点明显,只适合保存少量的可以被序列化、反序列化的数据。假如我们需要保存一个比较大的数据,这个时候ViewModel就可以实现。

Android Jetpack(4):ViewModel_第3张图片

ViewModel使用流程

  • UI,也就是Activity或Fragment,它的职责仅仅是视图的管理(大部分是刷新工作),相当于是一个ViewController的角色。ViewModel类相当于数据集散地,UI要这个数据了,ViewModel就去帮它在仓库找好,无论是数据库还是网络都行。ViewModel拿到数据之后就通知UI,通常情况下这个通知由LiveData来完成。最后通过LiveData去找DataBinding,完成数据的刷新。

Android Jetpack(4):ViewModel_第4张图片

ViewModel依赖

  • 目前Jetpack组件已经包含在AndroidX中,AndroidX是Jetpack的包含的支持库的具体位置,所以如果项目以及迁移到了AndroidX了,就不需要额外导包。如果没有兼容AndroidX那么需要在gradle中进行配置。
  • ViewModel 的依赖,可以查看官方文档,导入最新的版本。

官网地址

java

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

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

kotlin

    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:$lifecycle_version"

ViewModel的基本使用

1. 创建一个ViewModel类

继承ViewModel,需要注意的是ViewModel类中不应该持有Activity、Fragment、view的引用。

架构组件为UI控制器提供ViewModel助手类,ViewModel对象在配置更改期间会自动保留,以便它们保存的数据立即可用于下一个Activity或fragment实例。例如,如果您需要在应用中显示用户列表,请明确分配职责来获取数据并将用户列表保存到ViewModel,而不是Activity或fragment。

ViewModel一般配合 LiveData 使用。

class MainViewModel : ViewModel() {

    val mutableLiveData = MutableLiveData<String>()

    fun loadData() {
        mutableLiveData.value = "zly"
    }
}

2. 然后你可以从一个Activity中加载数据,如下所示:

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

        //获取ViewModel
        val viewModel = ViewModelProvider(this).get(MainViewModel::class.java)

        //加载数据
        viewModel.loadData()

        viewModel.mutableLiveData.observe(this, Observer {
            Log.e("xyh", "onCreate: $it")
        })
    }
}

如果Activity重新创建,它将接收由第一个Activity创建的相同的ViewModel实例。当持有ViewModel的Activity finish后,框架将调用ViewModel对象的onCleared()方法,以便它可以清理资源。

获取ViewModel的几种方式

方式1

2.2.0-alpha02 以前的版本是通过下面的方式,新版本已弃用。

val viewModel1 = ViewModelProviders.of(this).get(MainViewModel::class.java)

需要加入依赖 :

implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'

方式2

代码中只有一个this参数。

val mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)

this参数一般为Activity或Fragment,因此ViewModelProvider可以获取组件的生命周期。

方式3

val viewMode = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory())
            .get(MainViewModel::class.java)

方式4

 val viewModel = ViewModelProvider(this,ViewModelProvider.AndroidViewModelFactory
 .getInstance(application)).get(MainViewModel::class.java)

方式5

 val viewModel = ViewModelProvider.AndroidViewModelFactory.getInstance(application)
            .create(MainViewModel::class.java)

方式6

val viewModel = ViewModelProvider(viewModelStore,defaultViewModelProviderFactory).get(MainViewModel::class.java)

构造器有参数的ViewModel

  • 当我们的ViewModel需要进行构造器需要传参数的时候,就不能像上面一样进行实例化了。而需要借助于ViewModelProvider的Fatory来进行构造。
class MainViewModel(val name: String) : ViewModel() {

    val mutableLiveData = MutableLiveData<String>()

    fun loadData() {
        mutableLiveData.value = "zly$name"
    }

    class MainViewModelFactory(private val name: String) : ViewModelProvider.Factory {
        override fun <T : ViewModel?> create(modelClass: Class<T>): T {
            return MainViewModel(name) as T
        }
    }
}
  • 可以看到在MainViewModel有一个内部类基础自ViewModelProvider.Factory接口。并重写了create方法。这个方法返回一个ViewModel这里我们就是要实例化MainViewModel对象,因此这里返回一个MainViewModel对象,可以看到参数这时候就通过MainViewModelFactory传给了MainViewModel了。
  • 这时候不光是MainViewModel要进行修改,在进行MainViewModel的获取的时候也是需要进行相应的修改的:
  val viewModel = ViewModelProvider(this,MainViewModel.MainViewModelFactory("20"))
            .get(MainViewModel::class.java)

封装工厂类获取ViewModel

VmFactory


/**
 * Cerated by xiaoyehai
 * Create date : 2021/1/25 17:36
 * description :  封装工厂类获取ViewModel
 */
class VmFactory(private val application: Application) : ViewModelProvider.Factory {

    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(OneViewModel::class.java)) {
            return OneViewModel() as T
        } else if (modelClass.isAssignableFrom(TwoViewModel::class.java)) {
            return TwoViewModel(application) as T
        } else {
            throw ClassNotFoundException("class $modelClass 没有注册到工厂类这个viewModel啊")
        }
    }
}

OneViewModel

class OneViewModel : ViewModel() {

    val mutableLiveData = MutableLiveData<String>()

    fun loadData() {
        mutableLiveData.value = "zly"
    }
}

TwoViewModel

class TwoViewModel(application: Application):AndroidViewModel(application) {
}

使用

      val viewModel = VmFactory(application).create(OneViewModel::class.java)

        //加载数据
        viewModel.loadData()

        viewModel.mutableLiveData.observe(this, Observer {
            Log.e("xyh", "onCreate: $it")
        })

AndroidViewModel

  • 使用ViewModel的时候,需要注意的是ViewModel不能够持有View、Lifecycle、Acitivity引用,而且不能够包含任何包含前面内容的类。因为这样很有可能会造成内存泄漏。
  • 那如果需要使用Context对象改怎么办。这时候我们可以给ViewModel一个Application。Application是一个Context,而且一个应用也只会有Application。
  • 我们自己添加Application?其实没必要Google还有一个AndroidViewModel。这是一个包含Application的ViewModel。
  • AndroidViewModel 是ViewModel的一个子类,可以直接调用getApplication(),由此来访问应用的全局资源。

使用AndroidViewModel:

class MainViewModel(application: Application) : AndroidViewModel(application) {

    fun login() {
        Toast.makeText(getApplication(),"登陆成功",Toast.LENGTH_SHORT).show()
    }
}

获取viewModel的方式不变:

val viewModel = ViewModelProvider(this).get(MainViewModel::class.java);

viewModel.login()

下面是AndroidViewModel的源码:

public class AndroidViewModel extends ViewModel {
    @SuppressLint("StaticFieldLeak")
    private Application mApplication;

    public AndroidViewModel(@NonNull Application application) {
        mApplication = application;
    }

    /**
     * Return the application.
     */
    @SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"})
    @NonNull
    public <T extends Application> T getApplication() {
        return (T) mApplication;
    }
}

  • 因为ViewModel在指定的Activity或Fragment实例外存活,它应该永远不能引用一个View,或持有任何包含Activitycontext引用的类。如果ViewModel需要Application的context(如获取系统服务),可以扩展AndroidViewmodel,并拥有一个构造器接收Application。

使用ViewModel在Fragment间共享数据

  • 一个Activity中的多个Fragment相互通讯是很常见的。之前每个Fragment需要定义接口描述,所属Activity将二者捆绑在一起。此外,每个Fragment必须处理其他Fragment未创建或不可见的情况。通过使用ViewModel可以解决这个痛点,这些Fragment可以使用它们的Activity共享ViewModel来处理通讯。
class ShareViewModel:ViewModel()  {

    private val mutableLiveData = MutableLiveData<String>()

    fun setData(value:String) {
        mutableLiveData.value = value
    }

    fun getData(): LiveData<String> {
        return mutableLiveData
    }
}
class Fragment1 : Fragment() {

    private lateinit var mBinding: Fragment1Binding

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        mBinding = DataBindingUtil.inflate<Fragment1Binding>(
            inflater,
            R.layout.fragment1,
            container,
            false
        )
        return mBinding.root
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        val viewModel = ViewModelProvider(activity!!).get(ShareViewModel::class.java)

        mBinding.btn.setOnClickListener {
            viewModel.setData("xyh")
        }
    }
}
class Fragment2 : Fragment() {

    private lateinit var mBinding: Fragment2Binding

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        mBinding = DataBindingUtil.inflate<Fragment2Binding>(
            inflater,
            R.layout.fragment2,
            container,
            false
        )
        return mBinding.root
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        val viewModel = ViewModelProvider(activity!!).get(ShareViewModel::class.java)

        viewModel.getData().observe(viewLifecycleOwner, Observer {
            mBinding.tv.text = it
        })
    }

}
  • 注意:上面两个Fragment都用到了如下代码来获取ViewModel,activity返回的是同一个宿主Activity,因此两个Fragment之间返回的是同一个SharedViewModel对象。
val viewModel = ViewModelProvider(activity!!).get(ShareViewModel::class.java)

这种方式的好处包括:

  • Activity不需要做任何事情,也不需要知道通讯的事情。
  • Fragment不需要知道彼此,除了SharedViewModel进行联系。如果它们(Fragment)其中一个消失了,其余的仍然能够像往常一样工作。
  • 每个Fragment有自己的生命周期,而且不会受其它Fragment生命周期的影响。事实上,一个Fragment替换另一个Fragment,UI的工作也不会受到任何影响。

总结

  • ViewModel职责是为Activity或Fragment管理、请求数据,具体数据请求逻辑不应该写在ViewModel中,否则ViewModel的职责会变得太重,此处需要一个引入一个Repository,负责数据请求相关工作。具体请参考 Android架构组件。

  • ViewModel可以用于Activity内不同Fragment的交互,也可以用作Fragment之间一种解耦方式。

  • ViewModel也可以负责处理部分Activity/Fragment与应用其他模块的交互。

  • ViewModel生命周期(以Activity为例)起始于Activity第一次onCreate(),结束于Activity最终finish时。

ViewModel原理和源码分析

系统配置发生变化(如屏幕旋转)的时候会回调的方法

    override fun onRetainCustomNonConfigurationInstance(): Any? {
        Log.e("xyh", "onRetainCustomNonConfigurationInstance: ")
        return super.onRetainCustomNonConfigurationInstance()
    }

    override fun getLastCustomNonConfigurationInstance(): Any? {
        Log.e("xyh", "getLastCustomNonConfigurationInstance: ")
        return super.getLastCustomNonConfigurationInstance()
    }
    @Override
    @Nullable
    public final Object onRetainNonConfigurationInstance() {
        Object custom = onRetainCustomNonConfigurationInstance();

        ViewModelStore viewModelStore = mViewModelStore;
        if (viewModelStore == null) {
            // No one called getViewModelStore(), so see if there was an existing
            // ViewModelStore from our last NonConfigurationInstance
            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; //保存的内容
    }
    NonConfigurationInstances mLastNonConfigurationInstances;
    
    //获取上次切换时的内容
    @Nullable
    public Object getLastNonConfigurationInstance() {
        return mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.activity : null;
    }

   static final class NonConfigurationInstances {
        Object activity;
        HashMap<String, Object> children;
        FragmentManagerNonConfig fragments;
        ArrayMap<String, LoaderManager> loaders;
        VoiceInteractor voiceInteractor;
    }
  • Android横竖屏切换时会触发onSaveInstanceState,而还原时会产生onRestoreInstanceState,但是Android的Activity类还有一个方法名为onRetainNonConfigurationInstance和getLastNonConfigurationInstance这两个方法。
  • 我们可以通过 onRetainNonConfigurationInstance 代替 onSaveInstanceState。
  • 在恢复窗口时,我们可以不使用 onRestoreInstanceState,而代替的是getLastNonConfigurationInstance 方法。

第一次执行获取viewModel 的执行流程

val viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
  • 首先是通过ViewModelProvider(this)构造方法,创建一个ViewModelProvider,并在其构造方法中,创建保存ViewModel的ViewModelStore对象。
    public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
    }
   public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    }
  • owner就是我们传入的this(FragmentActivity、AppCompatActivity、Fragment)。
  • Activity实现了ViewModelStoreOwner 接口。

Android Jetpack(4):ViewModel_第5张图片
ViewModelStoreOwner :

public interface ViewModelStoreOwner {
    /**
     * Returns owned {@link ViewModelStore}
     *
     * @return a {@code ViewModelStore}
     */
    @NonNull
    ViewModelStore getViewModelStore();
}

我们看看getViewModelStore方法:

  • 第一次:创建ViewModelStore对象
    @NonNull
    @Override
    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.");
        }
        //第一次:mViewModelStore 为null
        if (mViewModelStore == null) {
   
            //第一次:nc 为null
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            //第一次,mViewModelStore为空,就创建一个mViewModelStore 
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }

ViewModelStore :

用于缓存ViewModel的一个操作类。

public class ViewModelStore {
	//用于存储ViewModel的集合
    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());
    }

    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

  • 可见ViewModelStore还是比较简单的,就是用HashMap来存储的数据。

ViewModel的保存, 是在ViewModelProvider的get方法中:

    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
        //获取我们传入的ViewModel简称(就这样叫吧),作为key的一部分
        String canonicalName = modelClass.getCanonicalName();
        //判断是不是局部类与匿名类
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }
    
    //获取ViewModel实例
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        //从集合中获取ViewModel并检测是不是已经实例化,如果存在就不要新建
        ViewModel viewModel = mViewModelStore.get(key);

        //判断viewModel是否是modelClass类的实例
        if (modelClass.isInstance(viewModel)) {
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            //如果是则直接返回已存在的viewModel
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        //否则通过工厂来新生成一个viewModel实例,用反射获取到ViewModel的实例
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        //把新生成的viewModel实例存入mViewModelStore中
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

其实主要还是利用了反射得到了ViewModel的实例。我们才可以使用ViewModel里面的方法。

那么什么是反射呢? 这里简单介绍一下:

反射机制:反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法

优点:可以实现动态创建对象和编译,体现出很大的灵活性

缺点:对性能有影响,此类操作总是慢于直接执行相同的操作

系统配置发生变化(如屏幕旋转)的时候怎么保存数据

系统配置发生变化时会走回调下面的方法:

  • 保存viewModelStore 到 NonConfigurationInstances
   @Override
    @Nullable
    public final Object onRetainNonConfigurationInstance() {
        Object custom = onRetainCustomNonConfigurationInstance();

        ViewModelStore viewModelStore = mViewModelStore;
        if (viewModelStore == null) {
            //如果当前的mViewModelStore为空,会先向nc中取mViewModelStore,这里边存储的就是上一次Activity对应的实例的mViewModelStore
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                viewModelStore = nc.viewModelStore;
            }
        }

        if (viewModelStore == null && custom == null) {
            return null;
        }

       //保存viewModelStore 到 NonConfigurationInstances 
        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = viewModelStore;
        return nci;
    }
    static final class NonConfigurationInstances {
        Object custom;
        ViewModelStore viewModelStore;
    }
  • 当配置发生更改时一般会造成数据丢失,而NonConfigurationInstances实例则可以在配置发生变化时保存一些数据和状态,在oncreate方法恢复使数据不会丢失。

  • 当配置发生变化的时候它保存了ViewModelStore。所以这里先从NonConfigurationInstance实例中获取ViewModelStore。

  • 这里的关键是NonConfigurationInstances。在设备旋转的时候,当前Activity被销毁了,mViewModelStore等数据会被封装到NonConfigurationInstances中存储

怎么恢复数据

  • 先执行获取viewModel :
  val viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
  • 获取viewModelStore
    @NonNull
    @Override
    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.");
        }
        if (mViewModelStore == null) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // 获取到viewModelStore
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }
  • 获取ViewModel
   @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    	//获取到ViewModel
        ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

最后说一下VierModel是如何销毁的

    /**
    * 在FragmentActivity的onDestroy方法中调用了mViewModelStore.clear()
    */
     @Override
    protected void onDestroy() {
        super.onDestroy();
        //isChangingConfigurations:系统配置发生变化(如屏幕旋转)
        if (mViewModelStore != null && !isChangingConfigurations()) {
            mViewModelStore.clear();
        }
        mFragments.dispatchDestroy();
    }
 
    /**
     * 先遍历ViewModel实例 调用各自的clear() 
     * 再清除集合中的ViewModel实例
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
    /**
    * 清除一些标记 并调用onCleared()
    */
    @MainThread
    final void clear() {
        mCleared = true;
        // Since clear() is final, this method is still called on mock objects
        // and in those cases, mBagOfTags is null. It'll always be empty though
        // because setTagIfAbsent and getTag are not final so we can skip
        // clearing it
        if (mBagOfTags != null) {
            synchronized (mBagOfTags) {
                for (Object value : mBagOfTags.values()) {
                    // see comment for the similar call in setTagIfAbsent
                    closeWithRuntimeException(value);
                }
            }
        }
        onCleared();
    }
    //通过继承ViewModel 可以重写该方法。 该方法会在ViewMode被销毁前调用
    protected void onCleared() {
    }

通过源码分析ViewModel有三个重要的类:ViewModel 、ViewModelProvider 、 ViewModelStore

ViewModel :负责准备和管理数据的类,该抽象类其实是声明一些通用方法。

ViewModelProvider :ViewModel 的核心类,主要是利用反射实例化出ViewModel 对象。利用工厂模式生产出具体的ViewModel 实例。

ViewModelStore:缓存ViewModel实例的一些操作(存储、获取、清除)

核心原理简单通俗描述如下:

  1. ViewModel类存储了Actvity的UI数据;
  2. ViewModelStore又存储了ViewModel实例;
  3. 在配置发生变化的时候在FragmentActivity.onRetainNonConfigurationInstance()方法中利用NonConfigurationInstances保存了ViewModelStore实例
  4. 并在FragmentActivtiy.oncreate()方法中恢复了ViewModelStore。也就是保存了ViewModel。所以数据才不会在配置更改时丢失
  5. 最后在FragmentActivtiy.onDestroy()方法中清除存储在ViewModelStore中的ViewModel对象。

你可能感兴趣的:(Jetpack+MVVM,ViewModel,ViewModel的使用,android,Jetpack)