JetPack—ViewModel原理分析

目录

  • 1.前言
  • 2.源码分析
  • 3.常见问题
  • 4.总结

一、前言

在ViewModel出现之前,在Android开发过程中一定绕不开的是处理Activity/Fragment重建后恢复数据,以及处理可能的内存泄漏问题。那么ViewModel就是能让开发者更为简单处理此类场景的能手。
ViewModel作为JetPack中重要的一员,是用于在Activity/Fragment中管理数据的容器。不会因为设置变更而销毁,同时还不会产生内存泄漏问题。

二、源码分析

在讲解原理前首先看看ViewModel是如何使用的。首先定义一个ViewModel

class MyViewModel: ViewModel() {
    val downloadStatus = MutableLiveData<String>()
    
    fun updateDownloadStatus() {
       //网络请求等耗时操作
       downloadStatue.value = "8%"
    }
}

复制代码

然后在Activity中使用ViewModel类。

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same MyViewModel instance created by the first activity.
    
        // Use the 'by viewModels()' Kotlin property delegate
        // from the activity-ktx artifact
        private val mViewModel by viewModels()
        mViewModel.downloadStatus.observe(this, Observer {
            //更新UI
            updateDownloadButtonStatus(it)
        })
    }
}
复制代码

通过以上代码,即可实现一个无内存泄漏的数据获取。且在Activity重建时,ViewModel获取到的仍然是一开始创建的对象,因此其保存数据也不会丢失。当Activity被销毁时,会自动调用ViewModel的onCleared方法。清理ViewModel对象。

到这里我们会产生几个疑问
1.ViewModel是如何创建的,为什么不能直接调用构造方法?
2.ViewModel是如何在Activity重建后保存其状态的?
3.ViewModel为什么无需手动销毁?

下面我们来深入到相关源码中找出答案。首先查看ViewModel是如何被创建的。

viewModels()实际上是ComponentActivity的扩展方法。
@MainThread
public inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
    noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
    //来自ComponentActivity 的getDefaultViewModelProviderFactory()
    //版本:androidx.activity.activity:1.1.0
    val factoryPromise = factoryProducer ?: {
        defaultViewModelProviderFactory
    }
    //来自ComponentActivity 的getViewModelStore()
    return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
}

public class ViewModelLazy<VM : ViewModel> (
    private val viewModelClass: KClass<VM>,
    private val storeProducer: () -> ViewModelStore,
    private val factoryProducer: () -> ViewModelProvider.Factory
) : Lazy<VM> {
    private var cached: VM? = null

    override val value: VM
        get() {
            //创建ViewModel
            val viewModel = cached
            return if (viewModel == null) {
                val factory = factoryProducer()
                val store = storeProducer()
                //ViewModelProvider [1]
                //ViewModelStore [2]
                //ViewModelProvider.Factory[3]
                ViewModelProvider(store, factory).get(viewModelClass.java).also {
                    cached = it
                }
            } else {
                viewModel
            }
        }

    override fun isInitialized(): Boolean = cached != null
}
复制代码

从代码中可以看到在创建ViewModel的过程中涉及到了以下几个类
ViewModel, ViewModelProvider, ViewModelProvider.Factory, ViewModelStore, ViewModelStoreOwner
那么我们来逐个分析各个类的作用:
ViewModelProvider:提供方法给到外部创建ViewModel实例。
ViewModelProvider.Factory:提供创建ViewModel的方式
ViewModelStore:缓存了ViewModel,里边使用了hashMap来存储ViewModel实例
ViewModelStoreOwner: 作为ViewModelStore的拥有者
首先从入口类ViewModelProvider进行分析。

//ViewModeProvider.java
    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
        String canonicalName = modelClass.getCanonicalName();
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }
    
        @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        //从ViewModelStore缓存中获取。因此ViewModel的状态保存就将职责放到了ViewModelStore
        ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
            //noinspection unchecked
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            //当缓存中没有时,就调用Factory的create方法创建ViewModel对象
            viewModel = (mFactory).create(modelClass);
        }
        //将ViewModel放入ViewModelStore缓存
        mViewModelStore.put(key, viewModel);
        //noinspection unchecked
        return (T) viewModel;
    }
复制代码

从上面代码可以看出如果我们在代码中直接调用ViewModel的构造方法,会发现状态并无法保存(必然的,因为每次重建都会新建一个ViewModel对象)。因此我们在获取ViewModel对象时,应当通过ViewModelProvider类来创建,其处理了ViewModel的创建方式和缓存策略。
通过代码可以看出要保存ViewModel对象,就是要将ViewModelStore对象在Activity/Fragment状态变化时,做好保存和恢复工作即可。那么下面我们回到ComponentActivity中,分析在Activity中是如何保存和恢复的。

//androidx.activity.ComponentActivity
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        LifecycleOwner,
        ViewModelStoreOwner,
        SavedStateRegistryOwner,
        OnBackPressedDispatcherOwner {
        
        static final class NonConfigurationInstances {
            Object custom;
            ViewModelStore viewModelStore;
        }
        ....省略
        
        getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    if (!isChangingConfigurations()) {
                        //当处于销毁状态且不是由于Configuration改变时,清空ViewModel对象
                        getViewModelStore().clear();
                    }
                }
            }
        });
        
   /**
     * Retain all appropriate non-config state.  You can NOT
     * override this yourself!  Use a {@link androidx.lifecycle.ViewModel} if you want to
     * retain your own non config state.
     */
    @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
            //获取的即是最后一次onRetainNonConfigurationInstance的返回值
            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;
    }
      
}
复制代码

这里代码就可以解决问题二和问题三,在ComponentActivity中ViewModelStore的保存时是通过onRetainNonConfigurationInstance机制来实现的。通过将ViewModelStore传入自定义NonConfigurationInstances对象,并在其方法中返回。即可实现ViewModel对象的保存。
然后再通过getLastNonConfigurationInstance方法获取保存的对象。

三、常见问题

1.如何使用ViewModel在两个Fragment之间共享数据?
答:在使用ViewModelProvider创建时,传入Activity即可。因为实际上传入的是ViewModelStoreOwner,其实现了getViewModelStore方法。因此生成的对象是存储在Activity中的。所以在同一Activity下的fragment获取到的就是同一个ViewModel对象。
例:

//内部传的是Activity的ViewModelStore和Factory
private val mViewModel by activityViewModels()
复制代码

2.如果想在Activity之间共享数据呢?
答:本质上我们让不同的Activity之间获取到同一个ViewModel对象即可实现共享。从上面的代码分析及Fragment之间共享数据的实现方式来看,可以通过传入的ViewModelStore来控制其访问范围。因此我们在Application中实现ViewModelStoreOwner接口。并在创建ViewModel时传入Application即可。
又或者通过自定义的ViewModelProvider.Factory来实现,使得不同的Activity获取ViewModel时返回相同的对象即可。

3.ViewModel中为什么不能持有View或者Activity等对象呢?
答:因为Activity/Fragment的生命周期与ViewModel并不相同。如果持有View或者Activity,那么实际上此时Activity可能处于任何的生命周期。此时去操作view的方法就可能出现crash.下面是在屏幕旋转时,Activity和ViewModel生命周期的对照图。

ViewModel屏幕旋转后的生命周期

4.ViewModel在什么情况下会保存其状态?
答:在ConfigurationChange的情况下,才会保存状态。如果是由于系统原因被kill掉时,则不会保存。这个因为onRetainNonConfigurationInstance与onSaveInstanceState机制上的区别。如果业务场景需要ViewModel在此种场景下保存状态,那么可以查看SavedStateHandle(viewmodel-savedstate库)相关内容

四、总结

本篇通过跟踪ViewModel的创建代码,引出了ViewModel创建过程中各个类(ViewModelProvider,
ViewModelProvider.Factory,ViewModelStore,ViewModelStoreOwner)的用途,以及其实现状态保存的实现方式。从实现方式中可以知道ViewModel在页面被系统kill掉时是不会保存其状态的。而如果要通过ViewModel实现则需要额外的使用viewmodel-savedstate库来实现此功能。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享