源码分析基于androidx
版本
ViewModel简介
ViewModel是用来存储和管理Lifecycle创建数据的组件,在配置发生改变或者屏幕旋转时数据仍然不会丢失。ViewModel可以负责组件间的通信,可以高效解决Activity与Fragment的通信问题。
主要的功能还是在非手动关闭以及系统回收条件下进行 Activity/Fragment的数据保存。
具有以下优势:
与UI层低耦合
可以在Fragment间共享ViewModel
数据一致保存在内存中,即使Activity重建
ViewModel使用示例
系统默认提供两种ViewModel
ViewModel
:抽象类实现
AndroidViewModel
:相对于ViewModel
多提供一个application
的入参,可以通过application
获取资源或者应用信息。
ViewModel绝不能引用View、Lifecycle或者Activity上下文引用的任何类。
在build.gradle
引入ViewModel
1 2 3 4 dependencies { implementation 'androidx.lifecycle:lifecycle-viewmodel:2.1.0' implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0' }
创建一个类实现ViewModel/AndroidViewModel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class MyViewModel : ViewModel () { private val users: MutableLiveData<List<User>> by lazy { MutableLiveData().also { loadUsers() } } fun getUsers () : LiveData<List<User>> { return users } private fun loadUsers () { } } class XXAndroidViewModel : AndroidViewModel (MyApp.getContext()){ }
创建完成后,需要在Activity/Fragment
中调用
1 2 3 4 5 6 7 8 9 10 class MyActivity : AppCompatActivity () { override fun onCreate (savedInstanceState: Bundle ?) { val viewModel = ViewModelProviders.of(this ).get (MyViewModel::class .java ) model.getUsers().observe(this , Observer<List<User>>{ users -> }) } }
上述为最简单的使用示例。
ViewModel源码解析
ViewModel-结构
ViewModel
抽象类,主要提供onCleared()
可以清理一些自定义数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public abstract class ViewModel { protected void onCleared () { } @MainThread final void clear () { mCleared = true ; if (mBagOfTags != null ) { synchronized (mBagOfTags) { for (Object value : mBagOfTags.values()) { closeWithRuntimeException(value); } } } onCleared(); } }
还提供了AndroidViewModel
,支持传入Application
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class AndroidViewModel extends ViewModel { @SuppressLint ("StaticFieldLeak" ) private Application mApplication; public AndroidViewModel (@NonNull Application application) { mApplication = application; } @SuppressWarnings ("TypeParameterUnusedInFormals" ) @NonNull public <T extends Application> T getApplication () { return (T) mApplication; } }
ViewModelProvider
为Activity、Fragment提供ViewModel的工具类,主要是创建ViewModel
内部参数 1 2 3 4 5 6 7 8 9 public class ViewModelProvider { private final Factory mFactory; private final ViewModelStore mViewModelStore; private static final String DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey" ; }
构造方法 1 2 3 4 5 6 7 8 public ViewModelProvider (@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) { this (owner.getViewModelStore(), factory); }public ViewModelProvider (@NonNull ViewModelStore store, @NonNull Factory factory) { mFactory = factory; mViewModelStore = store; }
提供两个构造方法,最终还是从ViewModelStore
获取缓存的ViewModel
获取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 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); }public <T extends ViewModel> T get (@NonNull String key, @NonNull Class<T> modelClass) { ViewModel viewModel = mViewModelStore.get(key); if (modelClass.isInstance(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; }
主要从ViewModelStore
中获取ViewModel
数据
Factory Factory
主要用来创建ViewModel
1 2 3 4 public interface Factory { @NonNull <T extends ViewModel> T create (@NonNull Class<T> modelClass) ; }
系统提供的Factroy
主要有以下几类
NewInstanceFactory
:通过Class.newInstance()
直接创建ViewModel实例
AndroidViewModelFactory
:针对AndroidViewModel
,通过Class.getConstructor()
传入Application
实例
ViewModelStore
内部主要缓存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 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(); } }
内部主要维护一个mMap
,缓存ViewModel。
ViewModelStoreOwner 1 2 3 4 public interface ViewModelStoreOwner { @NonNull ViewModelStore getViewModelStore () ; }
是一个接口,表示ViewModelStore
的作用域,主要实现类为ComponentActivity
和androidx.fragment.app.Fragment
。
ViewModel-生命周期绑定
ViewModel
对象存在的时间范围是获取ViewModel
时传递给ViewModelProvider
的Lifecycle
。ViewModel
通过Lifecycle
与Activity/Fragment
进行绑定的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 getLifecycle().addObserver(new LifecycleEventObserver() { @Override public void onStateChanged (@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { if (event == Lifecycle.Event.ON_DESTROY) { if (!isChangingConfigurations()) { getViewModelStore().clear(); } } } });
ViewModel-Activity销毁重建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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; }
当Activity创建时,并且调用ViewModelProvider().get(XXModel::class.java)
时,会调用到getViewModelStore()
此时会分为两种情况:
getLastNonConfigurationInstance()
不为null,直接从内部获取ViewModelStore
getLastNonConfigurationInstance()
为null,新建一个ViewModelStore
对象
如何判断getLastNonConfigurationInstance()
是否为null,就需要找到写入该数据的地方,判断在什么场景下会执行写入?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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; }
ComponentActivity
重写了onRetainNonConfigurationInstance()
方法,在其中存储了ViewModelStore
。
为什么NonConfiguationInstance
可以在Activity重建时不被销毁? Activity
重建时的生命周期过程:onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume
那就需要在onDestroy
时,存储NonConfigurationInstances
,才可以在后续启动时重新获取到数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ActivityClientRecord performDestroyActivity (IBinder token, boolean finishing, int configChanges, boolean getNonConfigInstance, String reason) { ActivityClientRecord r = mActivities.get(token); 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); } } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 NonConfigurationInstances retainNonConfigurationInstances () { Object activity = onRetainNonConfigurationInstance(); HashMap<String, Object> children = onRetainNonConfigurationChildInstances(); FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig(); ... ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig(); NonConfigurationInstances nci = new NonConfigurationInstances(); nci.activity = activity; nci.children = children; nci.fragments = fragments; return nci; }
Activity重启时
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); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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) { ... mWindow = new PhoneWindow(this , window, activityConfigCallback); mWindow.setWindowControllerCallback(this ); mWindow.setCallback(this ); mLastNonConfigurationInstances = lastNonConfigurationInstances; }
这样就回到了ComponentActivity
的getViewModelStore()
时,就可以获取写入的NonConfigurationInstances
。
由于NonConfigurationInstances
是写入到ActivityClientRecord
中,与Activity
生命周期无关。
ViewModel-Fragment销毁重建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 FragmentManagerImpl mFragmentManager;public ViewModelStore getViewModelStore () { if (mFragmentManager == null ) { throw new IllegalStateException("Can't access ViewModels from detached fragment" ); } return mFragmentManager.getViewModelStore(this ); } private FragmentManagerViewModel mNonConfig; @NonNull ViewModelStore getViewModelStore (@NonNull Fragment f) { return mNonConfig.getViewModelStore(f); }
mNonConfig
是一个FragmentManagerViewModel
类型,意味着也是ViewModel
的一种,对应的需要找到初始化的地方。
1 2 3 4 5 6 7 8 9 10 11 class FragmentManagerViewModel extends ViewModel { ... private final HashSet<Fragment> mRetainedFragments = new HashSet<>(); private final HashMap<String, FragmentManagerViewModel> mChildNonConfigs = new HashMap<>(); private final HashMap<String, ViewModelStore> mViewModelStores = new HashMap<>(); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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 ); } }
接下来寻找attachController
的调用
分为两处调用,一处位于Fragment
,另一处位于FragmentController
实际就是FragmentActivity
1 2 3 4 5 6 void performAttach () { mChildFragmentManager.attachController(mHost, new FragmentContainer() {...}, this ); mCalled = false ; onAttach(mHost.getContext()); }
当从Fragment
获取数据时,数据来源是getChildNonconfig()
1 2 3 4 5 protected void onCreate (@Nullable Bundle savedInstanceState) { mFragments.attachHost(null ); ... }
此时初始化完成FragmentManagerViewModel
并存入到FragmentActivity
的ViewModelStore
中。
总结 Fragment
声明的ViewModel
存储在FragmentManagerImpl.mNonConfig
实际存储于FragmentManagerViewModel
中,而FragmentManagerViewModel
实际存储于FragmentActivity.mViewModelStore
中。
所以ViewModel
在Fragment中也不会因为重建而被销毁。
原因就是Fragmen的ViewMode存储于FragmentManagerViewModel,而它存在于FragmentActivity的ViewModelStore中。FragmentActivity的ViewModelStore会在Activity销毁时存储于NonConfigurationInstances
中,当然也会存到ActivityyClienrtRecord中。
ViewModel-Fragment间数据共享
Fragment之间使用Activity范围共享的ViewModel
处理Fragment间的数据共享。
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 class SharedViewModel : ViewModel () { val selected = MutableLiveData<Item>() fun select (item: Item ) { selected.value = item } } class OneFragment : Fragment () { private lateinit var itemSelector: Selector private val model: SharedViewModel = ViewModelProviders.of(activity).get (ShareViewModel::class .java ) override fun onViewCreated (view: View , savedInstanceState: Bundle ?) { super .onViewCreated(view, savedInstanceState) itemSelector.setOnClickListener { item -> } } } class TwoFragment : Fragment () { private val model: SharedViewModel = ViewModelProviders.of(activity).get (ShareViewModel::class .java ) override fun onViewCreated (view: View , savedInstanceState: Bundle ?) { super .onViewCreated(view, savedInstanceState) model.selected.observe(viewLifecycleOwner, Observer<Item> { item -> }) } }
当两个Fragment获取ViewModel
时,都会从宿主Activity的ViewModelStore
获取数据,又因为key一致,所以可以得到同一个ViewModel对象。
只要保证作用域(ViewModelStore)相同,就可以获取相同的ViewModel
实例
ViewModel拓展 ViewModel与协程 Android 提供了viewModelScope
这一拓展属性,可以在ViewModel
销毁时退出,可以利用viewModelScope
启动ViewModel
中的协程,而不用担心任务泄漏。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class MyViewModel () : ViewModel() { fun initialize () { viewModelScope.launch { processBitmap() } } suspend fun processBitmap () = withContext(Dispatchers.Default) { } }
1 2 3 4 5 6 7 8 9 10 11 12 public val ViewModel.viewModelScope: CoroutineScope get () { val scope: CoroutineScope? = this .getTag(JOB_KEY) if (scope != null ) { return scope } return setTagIfAbsent( JOB_KEY, CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) ) }
Activity销毁重建数据恢复方式
onSaveInstanceState() / onRestoreInstanceState() 当Activity开始停止时,系统会调用onSaveInstanceState()
存储当前Activity的状态,执行于onStop()
之后。
重建先前被销毁的Activity时,调用到onRestoreInstanceState()
获取原先存储的Activity状态,在onCreate()
也会获得同样的数据。数据对象是Bundle
。
Fragment.setRetainInstance(true) 当配置发生改变时,Fragment 会随着宿主 Activity 销毁与重建,当我们调用 Fragment 中的 setRetainInstance(true) 方法时,系统允许 Fragment 绕开销毁-重建的过程。使用该方法,将会发送信号给系统,让 Activity 重建时,保留 Fragment 的实例。需要注意的是:
使用该方法后,不会调用 Fragment 的 onDestory()
方法,但仍然会调用 onDetach()
方法
使用该方法后,不会调用 Fragment 的 onCreate(Bundle)
方法。因为 Fragment 没有被重建。
使用该方法后,Fragment 的 onAttach(Activity)
与 onActivityCreated(Bundle)
方法仍然会被调用。
简述setRetainInstance(true)
流程:
调用setRetainInstance(true)
时,会将该Fragment存入到FragmentManagerViewModel
中的mRetainedFragment
中,等价于Fragment
已经位于FragmentActivity
的ViewModelStore
中。也就是存储于NonconfigurationInstance
中。
后续可以从mLastConfiguationInstances
获取存储的Fragment
,再调用到FragmentManager.restoreSaveState()
就可以还原Fragment。Fragment实例也没有发生改变。
onRetainNonConfigurationInstance()/getLastNonConfigurationInstance() 在 Activity 中提供了 onRetainNonConfigurationInstance
方法,用于处理配置发生改变时数据的保存。随后在重新创建的 Activity 中调用 getLastNonConfigurationInstance
获取上次保存的数据。我们不能直接重写上述方法,如果想在 Activity 中自定义想要恢复的数据,需要我们调用上述两个方法的内部方法:
onRetainCustomNonConfigurationInstance()
getLastCustomNonConfigurationInstance()
注意:onRetainNonConfigurationInstance
方法系统调用时机介于 onStop - onDestory 之间,getLastNonConfigurationInstance
方法可在 onCreate 与 onStart 方法中调用。
存储位置
数据类型
系统内存不足回收 数据是否存在
配置更改 数据是否存在
数据读写速度
onSaveInstanceState() / onRestoreInstanceState()
序列化到磁盘
支持基础数据类型
和可序列化对象
是
是
慢 有IO操作
setRetainInstance(true)
内存
支持复杂对象
受可用内存限制
否 实例已被销毁
是
快 直接内存读写
onRetainNonConfigurationInstance()/ getLastNonConfigurationInstance()
内存
支持复杂对象
受可用内存限制
否 实例已被销毁
是
快 直接内存读写
参考链接 ViewModel知识点
AndroidDeveloper-ViewModel