在页面(Activity/Fragment)功能较为简单的情况下,我们通常会将UI交互、与数据获取等相关的业务逻辑全部写在页面中。但是在页面功能复杂的情况下,代码量会变的非常多,也违反了"单一功能原则"。 页面只应该负责处理用户与UI控件的交互,并将数据展示到屏幕上,而数据获取相关的业务逻辑应该单独处理和存放。为了解决这个问题,Android为我们提供了ViewModel类,专门用于存放页面所需的数据。
ViewModel可以这么理解: 它是介于View(视图)和Model(数据模型)之间的一个东西。它起到了桥梁的作用,使视图和数据既能分离,也能保持通信。
ViewModel存储UI相关数据,供Activity或Fragment等UI控制器使用。当设备配置信息发生改变时,ViewModel会自动保存数据,以便新生成的UI能够恢复数据。例如,如果你想在你的App中显示一个用户信息列表,那么就可以把数据保存到ViewModel中,替代原来的onSaveInstanceState()方案,示例代码如下所示。
public class MyViewModel extends ViewModel {
private MutableLiveData<List<User>> users;
public LiveData<List<User>> getUsers() {
if (users == null) {
users = new MutableLiveData<List<User>>();
loadUsers();
}
return users;
}
private void loadUsers() {
// Do an asynchronous operation to fetch users.
}
}
可以用如下方式在新的Activity中访问用户列表数据。
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
// Create a ViewModel the first time the system calls an activity's onCreate() method.
// Re-created activities receive the same MyViewModel instance created by the first activity.
MyViewModel model = new ViewModelProvider(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
// update UI
});
}
}
如果这个Activity是重新创建的,那么它会获取到第一个Activity所创建的MyViewModel实例。当持有这个对象的Activity结束生命周期时,Android框架会调用ViewModel的onCleared()方法,以便它能释放资源。
Viewmodel的作用域是从ViewModelProvider获取ViewModel开始,到Activity的onDestory或Fragment的onDetatch结束。例如,Activity因为屏幕旋转导致生命周期变更,ViewModel的生命周期如下图所示。
ViewModelProvider通过ViewModelStore存取ViewModel,ViewModelStore将ViewModel存储到HashMap中管理,默认的key为ViewModel实例的类名。如下所示:
public class ViewModelProvider {
......
/**
* Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or
* an activity), associated with this {@code ViewModelProvider}.
*
* The created ViewModel is associated with the given scope and will be retained
* as long as the scope is alive (e.g. if it is an activity, until it is
* finished or process is killed).
*
* @param key The key to use to identify the ViewModel.
* @param modelClass The class of the ViewModel to create an instance of it if it is not
* present.
* @param The type parameter for the ViewModel.
* @return A ViewModel that is an instance of the given type {@code T}.
*/
@SuppressWarnings("unchecked")
@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 {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
} else {
viewModel = mFactory.create(modelClass);
}
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
......
}
ViewModelProvider中的ViewModelStore对象是从ViewModelStoreOwner中获取的,ViewModelStoreOwner是一个interface,它的实例一般为Activity或Fragment。如下所示:
public class ViewModelProvider {
......
/**
* Creates {@code ViewModelProvider}. This will create {@code ViewModels}
* and retain them in a store of the given {@code ViewModelStoreOwner}.
*
* This method will use the
* {@link HasDefaultViewModelProviderFactory#getDefaultViewModelProviderFactory() default factory}
* if the owner implements {@link HasDefaultViewModelProviderFactory}. Otherwise, a
* {@link NewInstanceFactory} will be used.
*/
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
.....
}
ComponentActivity实现了getViewModelStore方法,如果有缓存的ViewModelStore则返回缓存,如果没有则新建一个ViewModelStore,传递给ViewModelProvider。如下所示:
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
ContextAware,
LifecycleOwner,
ViewModelStoreOwner,
HasDefaultViewModelProviderFactory,
SavedStateRegistryOwner,
OnBackPressedDispatcherOwner,
ActivityResultRegistryOwner,
ActivityResultCaller {
......
/**
* Returns the {@link ViewModelStore} associated with this activity
*
* Overriding this method is no longer supported and this method will be made
* final
in a future version of ComponentActivity.
*
* @return a {@code ViewModelStore}
* @throws IllegalStateException if called before the Activity is attached to the Application
* instance i.e., before onCreate()
*/
@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) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
}
......
}
Activity结束生命周期时会将ViewModelStore缓存到NonConfigurationInstances中,而NonConfigurationInstances是由ActivityThread通过ActivityClientRecord类维护的,启动新的Activity时会把NonConfigurationInstances传递给这个Activity,这样就可以理解ViewModel为什么可以在Activity之间传递数据了。
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
ContextAware,
LifecycleOwner,
ViewModelStoreOwner,
HasDefaultViewModelProviderFactory,
SavedStateRegistryOwner,
OnBackPressedDispatcherOwner,
ActivityResultRegistryOwner,
ActivityResultCaller {
......
/**
* Retain all appropriate non-config state. You can NOT
* override this yourself! Use a {@link androidx.lifecycle.ViewModel} if you want to
* retain your own non config state.
*/
@Override
@Nullable
@SuppressWarnings("deprecation")
public final Object onRetainNonConfigurationInstance() {
// Maintain backward compatibility.
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
// No one called getViewModelStore(), so see if there was an existing
// ViewModelStore from our last NonConfigurationInstance
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;
}
}
当ViewModelStoreOwner是Fragment时,实现原理更加复杂,但是最终还是用的Activity的存储逻辑,本质上是一样的。
ViewModel可以帮助我们更好地将UI与业务逻辑从代码层面分离开来,依赖ViewModel的生命周期特性,我们不需要在关心屏幕旋转导致数据丢失的问题,只关注渲染数据即可;需要注意的是,如果想在ViewModel中使用Context,建议使用ViewModel的子类AndroidViewModel。