JetPack——ViewModel简析
简介
ViewModel以生命周期的方式存储和管理界面相关的数据。让数据在发生屏幕旋转等配置更改后得以继续留存。同时,可以将数据操作从UI控制器(Activity)里分离出来,这样就只需要Activity控制UI逻辑而无需处理数据业务逻辑。在需要进行一些异步操作的时候,免去了在Activity里大量的维护工作,并避免了在Activity销毁时潜在的内存泄漏问题。
总结一下主要优点:
可以更容易的将数据操作逻辑与Activity分离。
ViewModel的实现和基本使用
JetPack为UI控制器提供了 ViewModel 辅助程序类,该类负责为界面准备数据。在配置更改期间会自动保留 ViewModel 对象,以便它们存储的数据立即可供下一个 activity 或 fragment 实例使用。
代码如下:
public class MyViewModel extends ViewModel {
private MutableLiveData<List<String>> users;
public LiveData<List<String>> getUsers() {
if (users == null) {
users = new MutableLiveData<List<String>>();
loadUsers();
}
return users;
}
private void loadUsers() {
}
}
使用:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyViewModel model = new ViewModelProvider(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
});
}
}
ViewModel的创建
注意在上面的代码中,并没有采用new MyViewModel()
的方式去创建ViewModel。而是使用:
MyViewModel model = new ViewModelProvider(this).get(MyViewModel.class);
对应的在kotlin中使用:
private val myViewModel: MyViewModel by viewModels()
;
先说结论:使用ViewModelProvider
去创建ViewModel,确保了重新创建了相同Activity时,它接收的MyViewModel实例与第一个Activity创建的ViewModel实例相同。可以简单的理解为他是一个单例。
接下来看源码是如何实现的:
首先看ViewModelProvider
的构造方法和实现:
private final Factory mFactory;
private final ViewModelStore mViewModelStore;
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;
}
构造方法很简单,创建一个ViewModelStore
和Factory
准备工作
ViewModelStore解析
查看ViewModelStore的代码如下:
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();
}
}
代码很简单,就是一个HashMap存储ViewModel的键值对(HashMap源码要点解析)。
ViewModelStore
通过接口ViewModelStoreOwner
的getViewModelStore()
方法获取,以ComponentActivity
为例,具体实现如下:
//ComponentActivity.java:
static final class NonConfigurationInstances {
Object custom;
ViewModelStore 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.");
}
ensureViewModelStore();
return mViewModelStore;
}
@SuppressWarnings("WeakerAccess") /* synthetic access */
void ensureViewModelStore() {
if (mViewModelStore == null) {
NonConfigurationInstances nc =(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
}
其中getLastNonConfigurationInstance
方法实际上是返回一个activity,也可以理解为是一个ComponentActivity
的实例,在这里不做深入讨论,其在Activity
中的具体实现为:
@Nullable
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null? mLastNonConfigurationInstances.activity : null;
}
不难发现,每个Activity都会持有一个mViewModelStore
,ensureViewModelStore()
方法确保了所有ViewModel获取到的ViewModelStore
都为同一个。也就说:Activity通过持有mViewModelStore
,使用HaspMap管理ViewModel。也不难发现,activity和ViewModel是一对多的关系。
Factory解析
Factory
顾名思义,就是一个工厂模式。它是一个定义在ViewModelProvider
中的接口,代码很简单:
public interface Factory {
@NonNull
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
就是用来创建ViewModel
的。
以AppCompatActivity
为例:它通过继承ComponentActivity
,实现了ViewModelStoreOwner
接口,而在ViewModelProvider
初始化时,结合上面的代码,Factory的具体实现为:NewInstanceFactory.getInstance();
具体代码如下:
public static class NewInstanceFactory implements Factory {
private static NewInstanceFactory sInstance;
@NonNull
static NewInstanceFactory getInstance() {
if (sInstance == null) {
sInstance = new NewInstanceFactory();
}
return sInstance;
}
@SuppressWarnings("ClassNewInstance")
@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
的工厂类,使用create()
方法通过反射创建ViewModel
实例。
真正创建
完成了准备工作, 接下来就看ViewModel
具体的创建过程:
@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");
}
//用类名作为Key值
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//首先查看Map中是否有当前ViewModel的实例
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;
}
可以看到,首先使用类名作为key值,判断Activity所持有的ViewModelStore是否包含当前ViewModel的实例,有就直接拿来使用。如果没有,就通过上文中的Factory.create
创建一个实例,并添加到mViewModelStore中。
总结一下:
Activity通过HashMap持有所有的实例化后的ViewModel。
ViewModelProvider通过单例工厂模式和ViewModelStore,也就是HashMap实现对ViewModel的创建和获取,以此保证ViewModel在当前Activity中保持单例。
ViewModel的生命周期
ViewModel 对象存在的时间范围是获取 ViewModel 时传递给 ViewModelProvider 的 Lifecycle。ViewModel 将一直留在内存中,直到限定其存在时间范围的 Lifecycle 永久消失:对于 activity,是在 activity 完成时;而对于 fragment,是在 fragment 分离时。
其生命周期如下图所示:
通常在Activity的onCreate()方法里创建/获取ViewModel。若在旋转设备屏幕时,再次调用onCreate(),结合上文中ViewModel
的创建,其实并不会创建新的ViewModel实例,而是从HashMap中取出本就存在的实例。
因此:ViewModel 存在的时间范围是从您首次请求 ViewModel 直到 activity 完成并销毁。
ViewModel的销毁
销毁源码如下:
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
@MainThread
final void clear() {
if (mBagOfTags != null) {
synchronized (mBagOfTags) {
for (Object value : mBagOfTags.values()) {
// see comment for the similar call in setTagIfAbsent
closeWithRuntimeException(value);
}
}
}
onCleared();
}
其中:clear()
方法在ViewModelStore
中调用,其相关代码在上文中已展示:
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
而ViewModelStore.clear()
则在Activity中被调用:
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();
}
}
}
});
通过Lifecycle
管理的Activity的生命周期,在Activity销毁时,也就是onDestory
时调用。
带参数的ViewModel实现
既然ViewModel不可以通过new关键字来初始化,那么如果需要再初始化时向ViewModel传参该怎么办呢。例如:
public class MyViewModel extends ViewModel {
private String tag;
public MyViewModel(String tag) {
this.tag = tag;
}
private MutableLiveData<List<String>> users;
public LiveData<List<String>> getUsers() {
if (users == null) {
users = new MutableLiveData<List<String>>();
loadUsers();
}
return users;
}
private void loadUsers() {
}
}
根据上文中的代码解析,不难发现,我们需要重写Factory
的create
方法,结合ViewModelProvider
的构造函数,可以创建一个工厂类:
class MyViewModelFactory implements ViewModelProvider.Factory {
private String tag;
public MyViewModelFactory(String tag) {
this.tag = tag;
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
return (T) new MyViewModel(tag);
}
}
具体实现:
MyViewModelFactory myViewModelFactory = new MyViewModelFactory("TAG");
MyViewModel model = new ViewModelProvider(this,myViewModelFactory).get(MyViewModel.class);
当然了,也可以使用匿名内部类的方式实现:
MyViewModel model = new ViewModelProvider(this, new ViewModelProvider.Factory(){
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
return (T) new MyViewModel("tag");
}
}).get(MyViewModel.class);
在kolin中的扩展
koltin中使用扩展函数简化了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)
@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)
}
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
}
其本质上还是用ViewModelProvider
创建,只不过加了一些Kotlin的语法糖,简化了操作。
不仅如此,Kotlin还是支持使用注解,这样就可以省去创建Factory
的麻烦。刚兴趣的可以自己去探索(Hilt 和 Jetpack 集成)
总结
Activity持有ViewModel,一个Activity可以有多个类型的ViewModel;
同一个类型的ViewModel在Activity中有且只有一个实例;
通过HashMap和工厂模式保证了单一类型ViewModel的单例;
ViewModel生命周期贯穿整个Activity,且不会重复创建。