使用ViewModel定义的数据可以在发生屏幕旋转的时候仍然保留数据:当手机发生横竖屏旋转的时候,Activity会被重建,同时 存放在Activity中的数据 也会丢失,viewModel的生命周期和Activity不同,他可以保证手机在发生屏幕旋转的时候不会被重新创建,只有当Activity退出的时候才会跟着Activity一起销毁。
专门用于存放和界面相关的数据,只要界面可以看到的数据,都应该存放在ViewModel中,而不是Activity中
ViewModel通常持有LiveData和相关的处理逻辑,ViewModel管理的数据有一个特点,就是不会随着页面配置改变而销毁,但在页面销毁时则会正常跟着销毁。
以下通过实现一个计数器具体实现ViewModel的使用
(1)添加依赖
dependencies {
...
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
...
}
(2)在ViewModel中存放与界面相关的数据
class MainViewModel :ViewModel() {
var count = 0
}
(3)创建ViewModel的实例,并使用
class MainActivity : AppCompatActivity() {
private lateinit var button :Button
private lateinit var counter:TextView
private lateinit var viewModel:MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button = findViewById(R.id.add)
counter = findViewById(R.id.counter)
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
//viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
//方法已经被弃用了,现在使用第一种方式获取ViewModel对象
button.setOnClickListener {
viewModel.count++
counter.text = viewModel.count.toString()
}
counter.text = viewModel.count.toString()
//可以使得在旋转屏幕之后,textView展示ViewModel中的数据值,而不是每次都显示xml文件中的默认值
}
}
在上面举例的基础上,继续实现向ViewModel中传递参数
(1)自定义创建ViewModel的子类
class MainViewModel(time :Int) :ViewModel() {
var count = time
}
(2)创建自定义的ViewModelProvider.Factory
class MainViewModelFactory(val times :Int) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return MainViewModel(times) as T
}
}
(3)获取ViewModel实例
class MainActivity : AppCompatActivity() {
private lateinit var button :Button
private lateinit var counter:TextView
private lateinit var viewModel:MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button = findViewById(R.id.add)
counter = findViewById(R.id.counter)
//仅为了展示传入参数的使用,传入100并没有意义,可以通过直接改变count的初始值实现相同的效果
viewModel = ViewModelProvider(this,MainViewModelFactory(100)).get(MainViewModel::class.java)
button.setOnClickListener {
viewModel.count++
counter.text = viewModel.count.toString()
}
counter.text = viewModel.count.toString()
}
}
ViewModel是MVVM框架中的VM,使得数据处理和界面分开,举例说明:
class BrandModel :ViewModel() {
private var brands : MutableLiveData<List<Brand>>? = null
fun getBrands() :LiveData<List<Brand>>? {
if(brands == null) {
brands = MutableLiveData<List<Brand>>()
loadBrands()
}
return brands
}
private fun loadBrands() {
//进行网络请求或者一些其他的逻辑处理操作数据
}
}
在上面定义了一个ViewModel,管理brands这组数据,并且封装了加载brands的处理逻辑。而View只需要监听brands,在回调中根据brands处理界面就好,这样就做到了界面和数据的分离。
/*
创建{@code ViewModelProvider}。创建ViewModels并将它们保留在给定{@code ViewModelStoreOwner}的存储中。
此方法将使用{@link HasDefaultViewModelProviderFactory#getDefaultViewModelProviderFactory()默认工厂}
如果所有者实现{@link HasDefaultViewModelProviderFactory}。否则将使用{@link NewInstanceFactory}。
*/
//ComponentActivity和Fragment都实现了HasDefaultViewModelProviderFactory接口
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;
}
通过构造方法给成员变量mFactory和mViewModelStore赋值
get方法总体:get()方法首先尝试通过mViewModelStore的get()方法获取ViewModel的实例,如果没获取到则使用mFactory的create()创建实例,创建出来后则存入到mViewModelStore中。在这里mFactory就是ViewModel的构造工厂,mViewModelStore则是ViewModel的缓存管理者。
ViewModelProvider作为ViewModel的提供者,使用缓存mViewModelStore和工厂mFactory实现,第一次提供ViewModel时会通过工厂创建出来,后续则都是从缓存中拿。
private static final String DEFAULT_KEY =
"androidx.lifecycle.ViewModelProvider.DefaultKey";
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) {
//在mViewModelStore的map中获取
ViewModel viewModel = mViewModelStore.get(key);
//如果viewModel可以强转为modelClass类型
if (modelClass.isInstance(viewModel)) {
//如果mFactory是OnRequeryFactory类型,调用onRequery()方法,该方法当前为空实现
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
//如果mFactory是KeyedFactory类型,则调用create方法创建新的ViewModel实例,将创建好的实例放入mViewModelStore中
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
} else {
viewModel = mFactory.create(modelClass);
}
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
---
static class OnRequeryFactory {
void onRequery(@NonNull ViewModel viewModel) {
}
}
---
//实现KeyedFactory的类有:SavedStateViewModelFactory和AbstractSavedStateViewModelFactory
abstract static class KeyedFactory extends OnRequeryFactory implements Factory {
public abstract <T extends ViewModel> T create(@NonNull String key,
@NonNull Class<T> modelClass);
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
throw new UnsupportedOperationException("create(String, Class) must be called on "
+ "implementaions of KeyedFactory");
}
}
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
//如果是新创建的ViewModel存放的时候会将原来的ViewModel中的值进行销毁,key是根据类的全限定名来定义的
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();
}
}
ViewModelStoreOwner接口getViewModelStore()的实现就是提供一个ViewModelStore实例,而ComponentActivity使用Lifecycle能力在页面销毁时调用ViewModelStore实例的clear方法,清空其中的ViewModel。
public class 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();
//当改变不是因为页面配置引起的
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
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) {
//获得activity上次销毁时保留的信息
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
}
获得activity上次销毁时保留的信息使用的是getLastNonConfigurationInstance()保留数据使用的是onRetainNonConfigurationInstance()
public final Object onRetainNonConfigurationInstance() {
// Maintain backward compatibility.
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
/*
如果是null,说明以前没有调用过 getViewModelStore()方法
,也就是没有调用过ViewModelProvider(requireActivity()).get(DemoViewModel::class.java)
的方法来获取ViewModel。
所以我们看一下最后一个的NonConfigurationInstance里面是否存在viewModelStore
*/
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
//如果保留的信息中含有viewModelStore则使用
viewModelStore = nc.viewModelStore;
}
}
//custom一直是null,如果viewModelStore为null则返回null
if (viewModelStore == null && custom == null) {
return null;
}
// 如果viewModelStore不是null,也就是说最后一个NonConfigurationInstance里面有值
//直接new出来NonConfigurationInstances并赋值,返回出去
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
保存的数据都是NonConfigurationInstances类型的数据,并且是ComponentActivity的静态内部类,
用来存储viewModelStore的,无论配置是否改变,这个类的实例不会改,里面的ViewModelStore不会消失
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
当执行完onRetainNonConfigurationInstance()之后会将生成的NonConfigurationInstances对象存储在LocalActivityManager中,从而将页面中每一个Activity中的数据需要进行保存的数据全部保存。之后在Activity的Attach的时候,会把NonConfigurationInstances赋值给成员变量
public HashMap<String,Object> dispatchRetainNonConfigurationInstance() {
HashMap<String,Object> instanceMap = null;
//mActivityArray会对每一个调用StartActivity的Activity进行保存
final int N = mActivityArray.size();
for (int i=0; i<N; i++) {
LocalActivityRecord r = mActivityArray.get(i);
if ((r != null) && (r.activity != null)) {
Object instance = r.activity.onRetainNonConfigurationInstance();
if (instance != null) {
if (instanceMap == null) {
instanceMap = new HashMap<String,Object>();
}
instanceMap.put(r.id, instance);
}
}
}
return instanceMap;
}
总结:先通过NonConfigurationInstances来拿ViewModelStore,NonConfigurationInstances是一个静态内部类,不会因为配置改变(比如屏幕旋转),而重新创建
如果NonConfigurationInstances没有拿到,证明这就是个新的ViewModelStore,所以直接走创建ViewModelStore流程
前面说了ComponentActivity和Fragment都实现了HasDefaultViewModelProviderFactory接口
现在来看一下getDefaultViewModelProviderFactory()方法在ComponentActivity是如何实现的
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;
}
通过构造方法创建一个SavedStateViewModelFactory对象,传入了Application、当前ComponentActivity实例和Intent中的数据bundle。
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();
}
构造方法接受的参数中,页面实例是SavedStateRegistryOwner接口类型的,通过该接口获取到SavedStateRegistry和Lifecycle。另外成员变量mFactory是AndroidViewModelFactory的单例对象。
create()方法
public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
Constructor<T> constructor;
if (isAndroidViewModel && mApplication != null) {
//AndroidViewModel的构造器
constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
} else {
//支持SavedState的ViewModel的构造器
constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
}
// 普通的ViewModel的创建过程
if (constructor == null) {
return mFactory.create(modelClass);
}
SavedStateHandleController controller = SavedStateHandleController.create(
mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
try {
T viewmodel;
if (isAndroidViewModel && mApplication != null) {
viewmodel = constructor.newInstance(mApplication, controller.getHandle());
} else {
viewmodel = constructor.newInstance(controller.getHandle());
}
viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller);
return viewmodel;
} catch (IllegalAccessException e) {
throw new RuntimeException("Failed to access " + modelClass, e);
} catch (InstantiationException e) {
throw new RuntimeException("A " + modelClass + " cannot be instantiated.", e);
} catch (InvocationTargetException e) {
throw new RuntimeException("An exception happened in constructor of "
+ modelClass, e.getCause());
}
}
create()方法支持创建三种类型的ViewModel:AndroidViewModel、支持SavedState的ViewModel、普通ViewModel,这里由于篇幅原因,只分析一下普通ViewModel的创建。普通ViewModel通过mFactory的create()方法创建出来。
根据一开始的构造方法可知:如果application != null,则创建的是AndroidViewModelFactory实例,如果为null则创建的是NewInstanceFactory实例
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
try {
return modelClass.getConstructor(Application.class).newInstance(mApplication);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
return super.create(modelClass);
}
}
如果不是AndroidViewModel类型的话,就调用父类的create方法,
public static class NewInstanceFactory implements Factory {
.....
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection TryWithIdenticalCatches
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);
}
}
}
NewInstanceFactory的create()则是直接通过反射创建出ViewModel实例。
SaveStateViewModelFactory作为ComponentActivity和Fragment提供的对象,在NewInstanceFactory的基础上增加了对AndroidViewModel和支持SavedStated的ViewModel的创建,但对于普通的ViewModel创建,最后还是降级使用NewInstanceFactory完成。
通过ViewModelProvider的构造方法传入ViewModelStoreOwner对象,Activity和Fragment都实现了ViewModelStoreOwner接口,根据传入的参数得到ViewModelStore和Factory,然后通过get方法得到ViewModel对象,
get方法首先会在ViewModelStore中寻找ViewModel对象如果没有就会通过Factory进行创建并且存放在ViewModelStore中。
通过Activity或者Fragment的getViewModelStore()方法来进行获取,首先会通过getLastNonConfigurationInstance()获取activity的保留信息,当屏幕旋转的时候会执行onRetainNonConfigurationInstance()方法,之后会将生成的NonConfigurationInstances对象存储在LocalActivityManager中,从而将页面中每一个Activity中的数据需要进行保存的数据全部保存。之后在Activity的Attach的时候,会把NonConfigurationInstances赋值给成员变量。所以当通过getLastNonConfigurationInstance()获取activity的保留信息后如果有viewModelStore就进行复用,如果没有就新建ViewModelStore
activity和Fragment都实现了HasDefaultViewModelProviderFactory接口,在ViewModelProvider的构造方法中,可以得知,通过调用getDefaultViewModelProviderFactory()方法返回的SaveStateViewModelFactory对象,通过create方法去创建ViewModel,否则就用NewInstanceFactory去创建ViewModel,比如需要我们在ViewModel中传递参数的时候,我们可以写自己的Factory继承NewInstanceFactory,走我们自己方法Create(),通过create方法去创建ViewModel,如果application != null,则通过Application的构造器创建ViewModel实例,如果为null的话,通过反射的方式创建ViewModel实例。
(1)一个比较好的编程规范:给每个对应的Activty或者Fragment都创建一个对应的ViewModel,所有与界面相关的数据都应该存放在ViewModel中
(2)为什么必须要使用ViewModelProviders来获取ViewModel的实例而不是每次在OnCreate()中重新创建ViewModel创建对象?
如果在onCreate()中,则每次改变页面配置的时候还是会重新进行创建ViewModel实例,无法保存其中的数据,不能达到理想的效果。
(3)如果在Activity中创建静态变量,也可以使得屏幕旋转时的数据不会被重新创建效果和ViewModel的作用相同,为什么使用ViewModel而不是使用静态变量?
对于框架来说,没有实现像ViewModel一样将Activity的功能进行划分。
(4)比较onSaveInstanceState() 与 onRetainNonConfigurationInstance()
onRetainNonConfigurationInstance()这个方法可以像onSaveInstanceState()的方法一样保留变化前的Activity State,最大的不同在于这个方法可以返回一个包含有状态信息的Object,其中甚至可以包含Activity Instance本身。
(5)ViewModel 对象存在的时间比Activity的生命周期要长,禁止在ViewModel的持有Activity、Fragment、View等对象,这样会引起内存泄漏。如果需要在ViewModel中引用Context的话,google为我们提供了AndroidViewModel 类来供我们使用。
(6)ViewModel的好处:
(7)ViewModel 和 onSaveInstanceState 区别:
ViewModel
onSaveInstanceState