共计 12932 个字符,预计需要花费 33 分钟才能阅读完成。
提醒:本文最后更新于2023-05-27 19:07,文中所关联的信息可能已发生改变,请知悉!
0. 前言
ViewModel作为MVVM中最重要的一层,他的作用就是对数据状态的持有与维护。
根据源码里面的注释,我们可以知道,它在Android中事实上是为了解决一下两个问题:
- UI组件间实现数据共享
- Activity配置更改重建时保留数据
对于第一条,如果不同VM,那么各个UI组件都需要持有共享数据的引用,这样会带来两个麻烦:
- 如果新增共享数据,则各个UI组件需要再次声明并初始化新增的共享数据
- 某个组件对于数据的修改,没办法直接通知其他UI组件,需手动实现观察者模式
对于第二条,如果不使用VM,那么还是可以通过onSaveInstanceState保存的,但是如果数据量比较大,数据的序列化和反序列化都会产生一定的性能开销。
所以我们看ViewModel的源码,就需要从这两个问题入手:
- ViewModel是如何解决UI组件间共享数据的
- ViewModel是怎么解决重建时保留数据的
1. ViewModel是什么
我们直接看下源码:
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
|
public abstract class ViewModel { @Nullable private final Map<String, Object> mBagOfTags = new HashMap<>(); private volatile boolean mCleared = false;
@SuppressWarnings("WeakerAccess") protected void onCleared() { } @MainThread final void clear() { mCleared = true; if (mBagOfTags != null) { synchronized (mBagOfTags) { for (Object value : mBagOfTags.values()) { closeWithRuntimeException(value); } } } onCleared(); }
@SuppressWarnings("unchecked") <T> T setTagIfAbsent(String key, T newValue) { T previous; synchronized (mBagOfTags) { previous = (T) mBagOfTags.get(key); if (previous == null) { mBagOfTags.put(key, newValue); } } T result = previous == null ? newValue : previous; if (mCleared) { closeWithRuntimeException(result); } return result; } @SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"}) <T> T getTag(String key) { if (mBagOfTags == null) { return null; } synchronized (mBagOfTags) { return (T) mBagOfTags.get(key); } }
private static void closeWithRuntimeException(Object obj) { if (obj instanceof Closeable) { try { ((Closeable) obj).close(); } catch (IOException e) { throw new RuntimeException(e); } } } }
|
上面基本上对ViewModel这个抽象类有了一个大致的了解,那接下来我们来看下ViewModel是如何被创建的。
2. ViewModel的创建
一般情况下,我们都会通过这个代码创建ViewModel:
1
|
mViewModel = ViewModelProvider(this).get(MvvmViewModel::class.java)
|
这里的this要求传入ViewModelStoreOwner,一般我们的Activity和Fragment都实现了这个接口。我们来看下这个ViewModelProvider的构造方法:
2.1 ViewModelProvider
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
|
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) { this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory() : NewInstanceFactory.getInstance()); }
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) { this(owner.getViewModelStore(), factory); }
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) { mFactory = factory; mViewModelStore = store; }
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() { 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 (mDefaultFactory == null) { mDefaultFactory = new SavedStateViewModelFactory( getApplication(), this, getIntent() != null ? getIntent().getExtras() : null); } return mDefaultFactory; }
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; }
|
我们调用的是第一个构造方法,这个方法中调用了第三个构造方法,然后通过getViewModelStore()获取到了ViewModelStore,并判断我们这个owner是不是HasDefaultViewModelProviderFactory子类,如果是就调用HasDefaultViewModelProviderFactory的getDefaultViewModelProviderFactory()否则调用NewInstanceFactory.getInstance()。
这块还是别看AndroidStudio自带的SDK的源码了,这个时候最新的源码是API30,但是ComponentActivity并没有实现HasDefaultViewModelProviderFactory,而从官方Github看到的源码是已经实现了的。
而我们的Activity和Fragment都是实现了这个接口的。
他们的getDefaultViewModelProviderFactory()最后都创建了这样一个东西:
1
2 3 4
|
mDefaultFactory = new SavedStateViewModelFactory( getApplication(), this, getIntent() != null ? getIntent().getExtras() : null);
|
那我们再来看下get()方法:
2.1 ViewModelProvider # get()
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
|
@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) { ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) { if (mFactory instanceof OnRequeryFactory) { ((OnRequeryFactory) mFactory).onRequery(viewModel); } return (T) viewModel; } else { if (viewModel != null) { } } if (mFactory instanceof KeyedFactory) { viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass); } else { viewModel = (mFactory).create(modelClass); } mViewModelStore.put(key, viewModel); return (T) viewModel; }
@NonNull @Override public <T extends ViewModel> T create(@NonNull Class<T> modelClass) { try { return modelClass.newInstance(); } catch (InstantiationException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } catch (IllegalAccessException e) { throw new RuntimeException("Cannot create an instance of " + modelClass, e); } }
|
到这块,我们的创建ViewModel就说完了,最后总结下:
- ViewModelStore是用来保存ViewModel对象的HashMap,Activity和Fragment都会构造这个的对象;
- ViewModelProvider中会通过
get()方法创建一个ViewModel,创建之前会检测ViewModelStore中有没有缓存了的,如果有直接返回,没有就通过反射去创建。
3. 重建时如何保证ViewModel不会重建
了解到VieModel的创建后,我们现在来看第一个问题:ViewModel是怎么解决重建时保留数据的?
我们回顾下刚刚创建的ViewModel的流程,在get()方法中主要是通过ViewModelStore来帮我们保存ViewModel的,那么ViewModelStore是怎么创建的?
3.1 ViewModelStore的创建
3.1.1 Activity中
在ComponentActivity中有getViewModelStore()这样的一个方法:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
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) { mViewModelStore = nc.viewModelStore; } if (mViewModelStore == null) { mViewModelStore = new ViewModelStore(); } } return mViewModelStore; }
|
这块就涉及到了一个NonConfigurationInstances,而熟悉Activity重建机制的小伙伴应该会很熟悉这个,这个就是与我们的重建机制有关。
当我们需要重建Activity的时候,除了通过onSaveInstanceState()保存数据之外,也可以通过onRetainNonConfigurationInstance()这个方法:
ComponentActivity # onRetainNonConfigurationInstance()
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
|
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; }
|
那么onRetainNonConfigurationInstance()什么时候回被调用呢
在Activity的启动流程中,当ActivityThread执行到performDestroyActivity()这个方法时,就会调用Activity的retainNonConfigurationInstances()方法将保存的数据保存到ActivityClientRecord中:
1
2 3 4 5 6 7 8 9 10 11 12 13
|
NonConfigurationInstances retainNonConfigurationInstances() { Object activity = onRetainNonConfigurationInstance();
NonConfigurationInstances nci = new NonConfigurationInstances(); nci.activity = activity; nci.children = children; nci.fragments = fragments; nci.loaders = loaders; return nci; }
|
可以看到他先调用onRetainNonConfigurationInstance()获取到ComponentActivity返回来的nci,然后由构建了一个nci,并将之前ComponentActivity的那个nci保存到了这个nci的activity中。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing, int configChanges, boolean getNonConfigInstance, String reason) { ActivityClientRecord r = mActivities.get(token); if (r != null) { if (getNonConfigInstance) { try { r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances(); } catch (Exception e) { if (!mInstrumentation.onException(r.activity, e)) { throw new RuntimeException( "Unable to retain activity " + r.intent.getComponent().toShortString() + ": " + e.toString(), e); } } } } return r; }
|
那么什么时候恢复呢
当页面重构完成,就会调用ActivityThread的performLaunchActivity():
1
2 3 4 5 6 7 8 9 10
|
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window, r.configCallback, r.assistToken); return activity; }
|
Activity # attach()
1
2 3 4 5 6 7 8 9 10 11
|
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) { mLastNonConfigurationInstances = lastNonConfigurationInstances; }
|
这块就将值赋给了Activity的mLastNonConfigurationInstances。
然后我们在回到3.1.1节的getViewModelStore()方法中,如果调用getLastNonConfigurationInstance()返回的nc不为null的话,就直接取出他的viewModelStore,这样我们不就实现了Activity重建但是ViewModel仍然不会重建的问题嘛。
3.1.2 Fragment中
一样的,也来看下Fragment的getViewModelStore()方法
1
2 3 4 5 6
|
public ViewModelStore getViewModelStore() { if (mFragmentManager == null) { throw new IllegalStateException("Can't access ViewModels from detached fragment"); } return mFragmentManager.getViewModelStore(this); }
|
可以看到他里面调用了FragmentManager的getViewModelStore()方法:
FragmentManagerImpl # getViewModelStore()
1
2 3
|
ViewModelStore getViewModelStore(@NonNull Fragment f) { return mNonConfig.getViewModelStore(f); }
|
这里面直接调用了mNonConfig的getViewModelStore方法。
那么这个mNonConfig是个啥呢?
1
|
private FragmentManagerViewModel mNonConfig;
|
就是一个FragmentManagerViewModel,那么他是在哪赋值的:
FragmentManagerImpl # attachController()
1
2 3 4 5 6 7 8 9 10 11 12 13 14
|
public void attachController(@NonNull FragmentHostCallback host, @NonNull FragmentContainer container, @Nullable final Fragment parent) { if (parent != null) { mNonConfig = parent.mFragmentManager.getChildNonConfig(parent); } else if (host instanceof ViewModelStoreOwner) { ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore(); mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore); } else { mNonConfig = new FragmentManagerViewModel(false); } }
|
这个方法中就是根据不同的分支返回不同的mNonConfig,而这个方法则会被FragmentController的attachHost调用:
FragmentController # attachHost()
1
2 3 4
|
public void attachHost(@Nullable Fragment parent) { mHost.mFragmentManager.attachController( mHost, mHost , parent); }
|
而这个方法接着会被FragmetActivity中的onCreate调用。
FragmentActivity # onCreate()
1
2 3 4 5
|
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { mFragments.attachHost(null ); }
|
而attachHost中的那个host则是在FragmentController构造方法中传入的:
1
2 3 4 5 6 7 8 9
|
private FragmentController(FragmentHostCallback<?> callbacks) { mHost = callbacks; }
public static FragmentController createController(@NonNull FragmentHostCallback<?> callbacks) { return new FragmentController(checkNotNull(callbacks, "callbacks == null")); }
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
|
而这个HostCallbacks则实现了ViewModelStoreOwner:
1
2 3
|
class HostCallbacks extends FragmentHostCallback<FragmentActivity> implements ViewModelStoreOwner, OnBackPressedDispatcherOwner {
|
4. UI组件间数据共享
只要我们在调用ViewModelProvider(this).get(MvvmViewModel::class.java)时传入的this是同一个,那么就能获取到同一个ViewModelStore,对应的返回的ViewModel也是同一个了。