ViewModel的创建本篇文章不关注ViewModel是如何使用的,主要讨论的是ViewModel的原理以及它是如何创建的
我们在创建ViewModel的时候,有多种方式,比如,我们可以直接调用它的构造方法
val model = MyViewModel()
我们也可以使用下面的方法来创建
val model = ViewModelProviders.of(this).get(MyViewModel::class.java)
注意,使用ViewModelProviders需要导入下面的依赖
implementation "android.arch.lifecycle:extensions:1.1.1"
说明:不推荐直接调用ViewModel的构造参数去创建一个ViewModel,因为ViewModel的主要目的是去保存数据,比如在我们的Activity翻转的时候,Activity的onDestroy方法会被调用,然后Activity的构造方法会被调用,然后Activity的onCreate方法会被调用,ViewModel的目的就是确保,销毁前的Activity与重新创建的Activity可以获取到同一个ViewModel,从而达到数据保存的目的,我们通过ViewModelProviders.of(..).get(..)的方式,就可以确保获取到的ViewModel是同一个ViewModel,但是,如果我们是直接调用ViewModel的构造参数去获取ViewModel,那么销毁前的Activity与重新创建的Activity获取到的不是同一个ViewModel。
最近,发现了有一种新的创建ViewModel的方式,就是调用ViewModelProvider的构造方法,创建一个ViewModelProvider,注意不是**ViewModelProviders**
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) { mFactory = factory; mViewModelStore = store; }
然后调用ViewModelProvider的get方法,传入要构造的ViewModel的Class对象,去获取一个ViewModel
@NonNull @MainThread publicT get(@NonNull Class 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); }
那么,我们的疑问是,ViewModelProvider构造参数中的ViewModelStore、Factory是什么呢?它与我们使用ViewModelProviders.of(..).get(..)的方式创建ViewModel有什么联系吗?我们后面会一一进行分析。
ViewModelProviders.of.get我们先分析ViewModelProviders.of(..).get(..)是如何创建ViewModel的,这有利于我们后面的理解。
创建ViewModelProvider我们使用下面的语句来创建ViewModel
val model = ViewModelProviders.of(this).get(MyViewModel::class.java)
首先查看ViewModelProviders.of方法,这个方法用于创建ViewModelProvider
@NonNull @MainThread public static ViewModelProvider of(@NonNull FragmentActivity activity) { return of(activity, null); }
调用了自己的重载方法,第二个参数传入了null
@NonNull @MainThread public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) { // 检查是否能获取到Application,如果可以则获取,不可以则抛出异常 Application application = checkApplication(activity); // 若factory为null,那么就获取AndroidViewModelFactory,并赋值为factory if (factory == null) { factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application); } // 获取activity的ViewModelStore,然后创建ViewModelProvider,构造参数为 // ViewModelStore和factory return new ViewModelProvider(activity.getViewModelStore(), factory); }
AndroidViewModelFactory是创建ViewModel的一个工厂,ViewModelStore用于存储ViewModel,getViewModelStore方法定义在ViewModelStoreOwner接口中,ComponentActivity实现了该接口
public interface ViewModelStoreOwner { @NonNull ViewModelStore getViewModelStore(); }
另外,Fragment类也实现了ViewModelStoreOwner接口,在Fragment中也可以调用getViewModelStore方法获取对应的ViewModelStore。
上面提到AndroidViewModelFactory,它的定义如下
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory { // AndroidViewModelFactory采取单例的方式进行定义 private static AndroidViewModelFactory sInstance; @NonNull public static AndroidViewModelFactory getInstance(@NonNull Application application) { if (sInstance == null) { sInstance = new AndroidViewModelFactory(application); } return sInstance; } // AndroidViewModelFactory持有Application的引用 private Application mApplication; public AndroidViewModelFactory(@NonNull Application application) { mApplication = application; } @NonNull @Override // 创建ViewModel的方法 publicT create(@NonNull Class modelClass) { if (AndroidViewModel.class.isAssignableFrom(modelClass)) { try { return modelClass.getConstructor(Application.class) .newInstance(mApplication); } catch ... } return super.create(modelClass); } }
继承关系:AndroidViewModelFactory -> NewInstanceFactory -> Factory,它们都定义在ViewModelProvider里面,Factory接口定义了create方法,用于创建ViewModel。
我们上面调用了ViewModelProvider的构造方法,它里面其实就是将ViewModelStore和Factory保存起来了
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) { mFactory = factory; mViewModelStore = store; }
总结一下,ViewModelStore就是Activity中的ViewModelStore,它用于存储ViewModel,由于我们没有主动指定Factory,所以这里Factory就是系统默认的AndroidViewModelFactory。
创建ViewModel调用ViewModelProvider的get方法,就可以创建ViewModel
@NonNull @MainThread publicT get(@NonNull Class modelClass) { // 获取ViewModel的 包名.类名 String canonicalName = modelClass.getCanonicalName(); if (canonicalName == null) { throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels"); } return get(DEFAULT_KEY + ":" + canonicalName, modelClass); }
调用了自己的重载方法get,传入两个参数,第一个参数是key,用于标识ViewModel,这里使用ViewModel的「包名+类名」来标识ViewModel,第二个参数是ViewModel对应的Class对象
@NonNull @MainThread publicT get(@NonNull String key, @NonNull Class modelClass) { // 在ViewModelStore中查找是否有key对应的ViewModel ViewModel viewModel = mViewModelStore.get(key); // 该方法类似于instanceof的语句,如果viewModel对应的类型,是modelClass对应的类型,或者 // 是modelClass的子类类型,那么该方法返回true,否则返回false。如果viewModel为null,该方 // 法也返回false 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 { // 利用AndroidViewModelFactory去创建一个ViewModel viewModel = (mFactory).create(modelClass); } // 将ViewModel存入ViewModelStore中 mViewModelStore.put(key, viewModel); //noinspection unchecked // 返回ViewModel return (T) viewModel; }
这里我们需要关注三点:
- AndroidViewModelFactory如何创建ViewModel的
- ViewModelStore是如何缓存ViewModel的
- ViewModelStore是如何获取的
上面调用了AndroidViewModelFactory的create方法创建了一个ViewModel,它的create方法如下
@NonNull @Override publicT create(@NonNull Class modelClass) { // 如果AndroidViewModel是modelClass对应的类型的父类,该方法返回true,进入If // 语句,否则该方法返回false if (AndroidViewModel.class.isAssignableFrom(modelClass)) { //noinspection TryWithIdenticalCatches try { return modelClass.getConstructor(Application.class).newInstance(mApplication); } catch ... } // 调用父类的create方法 return super.create(modelClass); }
其实AndroidViewModel就是内部包含了一个Application对象的ViewModel,一般我们使用ViewModel的时候也没有去继承AndroidViewModel,所以这里我们就认为它不会进入If语句,而是直接调用了父类的create方法。
AndroidViewModelFactory的父类是NewInstanceFactory,它的create方法如下
public static class NewInstanceFactory implements Factory { @SuppressWarnings("ClassNewInstance") @NonNull @Override publicT create(@NonNull Class 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); } } }
我们可以看到这里调用了modelClass的newInstance方法,也就是modeClass对应的ViewModel需要有一个空参数的构造方法,否则就会构造失败,抛出异常。
所以,如果我们使用ViewModelProviders.of.get的方式构造ViewModel,那么只可以构造出无参数的ViewModel,如果我们的ViewModel的构造方法是有参数的,通过ViewModelProviders.of.get的方式将会构造失败,这时候我们就需要自定义Factory来构造ViewModel。
ViewModelStore的缓存我们直接看下ViewModelStore的源码
public class ViewModelStore { // 存储ViewModel的Map private final HashMapViewModelStore的获取mMap = new HashMap<>(); // 存入ViewModel final void put(String key, ViewModel viewModel) { ViewModel oldViewModel = mMap.put(key, viewModel); // 如果有旧的ViewModel与key对应,就调用旧的ViewModel的onCleared方法 if (oldViewModel != null) { oldViewModel.onCleared(); } } // 根据key获取ViewModel,若key没有对应的ViewModel,该方法会返回null final ViewModel get(String key) { return mMap.get(key); } // 返回key的集合 Set keys() { return new HashSet<>(mMap.keySet()); } // 调用所有ViewModel的clear方法,通知它们可以销毁了,并且清理干净mMap public final void clear() { for (ViewModel vm : mMap.values()) { vm.clear(); } mMap.clear(); } }
ViewModelStore是在创建ViewModelProvider的时候获取的
@NonNull @MainThread public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) { Application application = checkApplication(activity); if (factory == null) { factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application); } return new ViewModelProvider(activity.getViewModelStore(), factory); }
这里调用了activity的getViewModelStore方法获取ViewModelStore,该方法如下
@NonNull @Override 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."); } // 判断mViewModelStore是否为null if (mViewModelStore == null) { NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance(); if (nc != null) { // Restore the ViewModelStore from NonConfigurationInstances // 查看是否可以从nc恢复ViewModelStore mViewModelStore = nc.viewModelStore; } // 若没有恢复成功,就新创建一个ViewModelStore if (mViewModelStore == null) { mViewModelStore = new ViewModelStore(); } } // 返回ViewModelStore return mViewModelStore; }
这里我们分为三种场景分别讨论ViewModelStore的获取
- Activity是新创建的:这时候mViewModelStore和nc均为null,那么就新构造一个ViewModelStore并返回。
- 在场景1的基础上,Activity已经创建了一个mViewModelStore,但是Activity由于配置更改,例如横竖屏的切换,导致Activity销毁又重建:这时候mViewModelStore为null,但是nc不为null,并且从nc当中获取到的viewModelStore也不为null,这时候重建的Activity和销毁前的Activity拿到的ViewModelStore是同一个,然后就可以通过同一个ViewModelStore,去获取之前的ViewModel,以达到配置更改但是数据得到保存的目的。
- 在场景1的基础上,Activity已经创建了一个mViewModelStore,这时候我主动去销毁该Activity,然后又重新启动该Activity:该场景和场景1一样,即mViewModelStore和nc均为null,那么就新构造一个ViewModelStore并返回。
到了这里,我们就明白了
- 通过ViewModelProviders.of.get的方式是如何做到,在横竖屏切换时,销毁前的Activity与重新创建的Activity可以获取到同一个ViewModel。
- ViewModelProviders.of.get默认只可以构造没有参数的ViewModel,如果ViewModel带有参数,我们就需要自定义构造ViewModel的Factory。
例如现在我们的ViewModel构造方法是带有参数的
class MyViewModel(val arg: Int) : ViewModel() { ... }
那么我们就需要自定义一个Factory,在它的create方法中,调用MyViewModel的带有参数的构造方法,创建MyViewModel并返回
class VMFactory(val arg: Int) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override funcreate(modelClass: Class ): T { return MyViewModel(arg) as T } }
接着在Activity当中,我们只需要主动地去构造ViewModelProvider,传入我们自定义的Factory,然后调用ViewModelProvider的get方法,即可获取MyViewModel的实例
class ViewModelActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_view_model) val model = ViewModelProvider(viewModelStore, VMFactory(10)).get(MyViewModel::class.java) ... } }
这里构造ViewModelProvider是使用了和ViewModelProviders.of.get一样的构造方法
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) { mFactory = factory; mViewModelStore = store; }
但是传入一个ViewModelStore总归是有点别扭,其实ViewModelProvider还有另外一个构造方法
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) { this(owner.getViewModelStore(), factory); }
这个方法中,我们只需要传入一个ViewModelStoreOwner,然后它就会获取owner的ViewModelStore,然后自动调用上面的含ViewModelStore,Factory参数的构造方法。
ComponentActivity和Fragment都实现了ViewModelStoreOwner接口,可以调用getViewModelStore方法,获取ViewModelStore。
参考- Android开发 ViewModel_2_了解多种自定义实例方式 - 博客园.
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)