JectPack组件原理分析 ---- ViewModel

JectPack组件中,ViewModel主要用来封装与界面相关的数据,同样ViewModel是具备生命周期感知能力,在Activity销毁之前,或者Fragment onDetach 之前,是一直存在内存中;尤其是在屏幕旋转等系统配置更改后,ViewModel保存的界面数据依然存在

ViewModel源码分析

1 ViewModel的创建

ViewModel的创建,如果直接new出来一个对象也是可以的,但是官方提供的方式是通过ViewModelProvider + get的方式获取,这种方式在Kotlin中中,被封装成了viewModels和activityViewModels的语法糖,通过这两种方式创建ViewModel

@MainThread
public inline fun <reified VM : ViewModel> Fragment.viewModels(
    noinline ownerProducer: () -> ViewModelStoreOwner = { this },
    noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> = createViewModelLazy(VM::class, { ownerProducer().viewModelStore }, factoryProducer)

这里就拿viewModels来说,这是一个内联高级函数,在Fragment的onCreateView方法中调用时,是直接完成ViewModel的创建

val viewModel = viewModels<MyViewModel>()

viewModels存在两个参数,ownerProducer,返回是一个ViewModelStoreOwner,默认是this,就是调用该函数当前的Fragment;factoryProducer,创建ViewModel的工厂,可以为空不传
如果想要使用自己的工厂去创建ViewModel,需要实现ViewModelProvider.Factory接口

class ViewModelFactory : ViewModelProvider.Factory {


	override fun <T : ViewModel?> create(modelClass: Class<T>): T {
	
	    return modelClass.getConstructor(MyViewModel::class.java).newInstance()
	}
}

使用方式如下

val viewModel = viewModels<MyViewModel>{
	ViewModelFactory()
}

另外一种方式activityViewModels创建ViewModel,需要一个上下文,其他的和viewModels一致,两者的区别后续会讲解。

2 ViewModel源码分析

不管是viewModels还是activityViewModels,都会走到createViewModelLazy方法,

@MainThread
public fun <VM : ViewModel> Fragment.createViewModelLazy(
    viewModelClass: KClass<VM>,
    storeProducer: () -> ViewModelStore,
    factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
    val factoryPromise = factoryProducer ?: {
        defaultViewModelProviderFactory
    }
    return ViewModelLazy(viewModelClass, storeProducer, factoryPromise)
}

对于viewModels,如果传入的factoryProducer为空,那么就会个一个默认的ViewModelProviderFactory

Fragment # getDefaultViewModelProviderFactory

 public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
        if (mFragmentManager == null) {
            throw new IllegalStateException("Can't access ViewModels from detached fragment");
        }
        if (mDefaultFactory == null) {
            Application application = null;
            Context appContext = requireContext().getApplicationContext();
            while (appContext instanceof ContextWrapper) {
                if (appContext instanceof Application) {
                    application = (Application) appContext;
                    break;
                }
                appContext = ((ContextWrapper) appContext).getBaseContext();
            }
            if (application == null && FragmentManager.isLoggingEnabled(Log.DEBUG)) {
                Log.d(FragmentManager.TAG, "Could not find Application instance from "
                        + "Context " + requireContext().getApplicationContext() + ", you will "
                        + "not be able to use AndroidViewModel with the default "
                        + "ViewModelProvider.Factory");
            }
            mDefaultFactory = new SavedStateViewModelFactory(
                    application,
                    this,
                    getArguments());
        }
        return mDefaultFactory;
    }

最终返回一个SavedStateViewModelFactory,这是默认的工厂,这里会判断上下文application是否为空,如果为空,那么就mFactory = NewInstanceFactory;否则就是AndroidViewModelFactory,在activityViewModels调用时,就会传入一个上下文,源码可自己去看

public SavedStateViewModelFactory(@Nullable Application application,
         @NonNull SavedStateRegistryOwner owner,
         @Nullable Bundle defaultArgs) {
     mSavedStateRegistry = owner.getSavedStateRegistry();
     mLifecycle = owner.getLifecycle();
     mDefaultArgs = defaultArgs;
     mApplication = application;
     mFactory = application != null
             ? ViewModelProvider.AndroidViewModelFactory.getInstance(application)
             : ViewModelProvider.NewInstanceFactory.getInstance();
 }

最终获取ViewModel,就是在ViewModelLazy方法

ViewModelProvider # ViewModelLazy

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() {
            val viewModel = cached
            return if (viewModel == null) {
                val factory = factoryProducer()
                val store = storeProducer()
                ViewModelProvider(store, factory).get(viewModelClass.java).also {
                    cached = it
                }
            } else {
                viewModel
            }
        }

    override fun isInitialized(): Boolean = cached != null
}

首先是从缓存里拿ViewModel,如果缓存中不存在ViewModel,那么就通过ViewModelProvider + get去拿,这里主要看get方法

ViewModelProvider # get

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

这里是拿到了类的全类名和DEFAULT_KEY拼接成一个key,去mViewModelStore里面取,mViewModelStore就是在viewModels里默认的this,每个Activity和Fragment都有一个mViewModelStore

Activity和Fragment都实现了ViewModelStoreOwner接口,这个接口中存在一个方法

public interface ViewModelStoreOwner {
    @NonNull
    ViewModelStore getViewModelStore();
}

在调用createViewModelLazy时,其实已经传入了ViewModelStore,ViewModelStore其实就是一个HashMap

ComponentActiivty # getViewModelStore

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

@SuppressWarnings("WeakerAccess") /* synthetic access */
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();
        }
    }
}

通过拼接好的key,去mViewModelStore中获取ViewModel,如果第一次没有创建过ViewModel,肯定获取不到,那么走到1的位置

public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);
        //2 第二次进来走这个位置
        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.
            }
        }
        //1  第一个进来走这个位置
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
        } else {
            viewModel = mFactory.create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

这里会判断传入的工厂类型,一般情况下,会走else,走我们自己创建的工程的create方法

ViewModelProvider # Factory – create

public interface Factory {
    @NonNull
    <T extends ViewModel> T create(@NonNull Class<T> modelClass);
}

这样就创建了一个ViewModel,然就保存到mViewModelStore中;

所以viewModels在创建ViewModel时,先是从缓存中拿,如果缓存中没有,就从mViewModelStore中获取,如果mViewModelStore也没有,那么就重新创建,相当于一个三级缓存,是一个单例模式。

3 ViewModel存储数据

当屏幕旋转或者其他配置改变的时候,Activity会被销毁重建,页面数据的保存,通常在页面销毁时调用onSaveInstanceState,重建时调用onRestoreInstanceState,这种只能保存轻量级的数据(因为Bundle),所以ViewModel的出现,就解决了传统方式不能存储大量数据的问题

在activity中,存在两个非生命周期方法,onRetainNonConfigurationInstance和getLastNonConfigurationInstance

ComponentActivity # onRetainNonConfigurationInstance

public final Object onRetainNonConfigurationInstance() {
        // Maintain backward compatibility.
        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;
            }
        }
		//如果viewModelStore为空,
		//或者自定义的状态为空,没有自定义保存状态,就是不需要保存
        if (viewModelStore == null && custom == null) {
            return null;
        }

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = viewModelStore;
        return nci;
    }

在屏幕旋转时,会调用onRetainNonConfigurationInstance,当页面准备销毁时,调用onRetainCustomNonConfigurationInstance,这个方法可以重写,自定义页面数据的存储状态,这个时候,拿到mViewModelStore,这个时候还没有从内存清除,就创建NonConfigurationInstances,将viewModelStore以及数据保存状态保存到NonConfigurationInstances中

Activity # retainNonConfigurationInstances

NonConfigurationInstances retainNonConfigurationInstances() {
		// 调用 ComponentActivity中的onRetainNonConfigurationInstance
		// activity中保存的就是viewModel和数据存储状态
		
        Object activity = onRetainNonConfigurationInstance();
        HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
        FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

        mFragments.doLoaderStart();
        mFragments.doLoaderStop(true);
        ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();

        if (activity == null && children == null && fragments == null && loaders == null
                && mVoiceInteractor == null) {
            return null;
        }

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.activity = activity;
        nci.children = children;
        nci.fragments = fragments;
        nci.loaders = loaders;
        if (mVoiceInteractor != null) {
            mVoiceInteractor.retainInstance();
            nci.voiceInteractor = mVoiceInteractor;
        }
        return nci;
    }

最终,Activity销毁之前,将Activity相关的信息,全部保存在了NonConfigurationInstances中

当Activity销毁之后,重新启动后,在attach方法中,传入了lastNonConfigurationInstances,就是页面销毁之前保存的状态信息

final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            //保存的页面 数据信息
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
            IBinder shareableActivityToken) 

这里具体的怎么传递的,加一个TODO,之后在AMS中,会着重看一下源码,这样,在Activity重新启动之后,再次去getViewModelStore,会调用getLastNonConfigurationInstance

Activity # getLastNonConfigurationInstance

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

这里mLastNonConfigurationInstances已经不为空(attach中传入),返回 mLastNonConfigurationInstances.activity,这个activity中就保存了viewModelStore以及页面保存的数据状态,实现了页面数据的保存

至于页面在销毁后,ViewModel是怎么销毁的,就是LifeCycle的功劳了

ComponentActivity的构造方法

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的ON_DESTROY后,就将ViewModelStore里的全部ViewModel都清空,当然是保存数据之后了…

上一篇:AISing Programming Contest 2021(AtCoder Beginner Contest 202)E - Count Descendants


下一篇:深入了解架构组件之ViewModel,android高级面试题汇总