在Activity/Fragment中,通常会将UI交互、与数据获取等相关的业务逻辑全部写在页面中,但是在页面功能复杂的情况下,代码量会变的非常多。页面只应该负责处理用户与UI控件的交互,并将数据展示到屏幕上,而数据获取相关的业务逻辑应该单独处理和存放。谷歌为了解决这种问题,推出了ViewModel组件。
ViewModel 是以感知生命周期的形式来存储和管理视图相关的数据。
在app的build.gradle中添加依赖
dependencies {
implementation "androidx.lifecycle:lifecycle-viewmodel:2.2.0"
}
在这里实现一个简单计数功能,首先在ViewModel 定义一个计数变量num
public class ExViewModel extends ViewModel {
private int num;
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
}
所有与界面相关的数据应该放在ViewModel中。接着,在Activity中获取ViewModel对象。
private int i=0;
private ExViewModel model;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ex_jetpack);
// 获取ExViewModel 对象
model=new ViewModelProvider(this).get(ExViewModel.class);
refreshData();
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
i++;
model.setNum(i);
refreshData();
}
});
}
public void refreshData(){
tv_num.setText(model.getNum()+"");
}
在旋转设备屏幕时,Activity会被销毁重新创建,但ViewMode不会被销毁。效果如下图
当我们旋转屏幕导致Activity重建时,计数值并没有改变。这意味着横/竖屏状态下的Activity所对应的ViewModel是同一个,并没有被销毁,所持有的数据也一直都存在。
可以看到,Activity的生命周期不断变化,经历了被销毁重新创建,而ViewModel的生命周期没有发生变化。
小结:
当系统“未经你许可”时销毁了你的activity,则onSaveInstanceState会被系统调用。
因配置更改界面销毁重建情况下,则ViewModel恢复数据。
onSaveInstanceState是在 序列化到磁盘中。
ViewModel是存在内存中,读写速度快。
只能存可序列化和反序列化的对象,且大小有限制(一般Bundle限制大小1M)。
可以存复杂数据,大小限制就是App的可用内存。
在页面中通过ViewModelProvider类来实例化ViewModel
ExViewModel model=new ViewModelProvider(this).get(ExViewModel.class);
接下来,查看ViewModelProvider源码:
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
在代码中,调用了只需传ViewModelStoreOwner的构造方法,最后会走到两个参数的构造方法。ViewModelStoreOwner是ViewModel存储器拥有者;ViewModelStore是ViewModel存储器;Factory是创建ViewModel实例的工厂。
接下来,查看ViewModelStoreOwner
// ViewModelStoreOwner
public interface ViewModelStoreOwner {
/**
* Returns owned {@link ViewModelStore}
*
* @return a {@code ViewModelStore}
*/
@NonNull
ViewModelStore getViewModelStore();
}
ViewModelStoreOwner是个接口,其实现类是Activity/Fragment,也就是说Activity/Fragment都是ViewModel存储器的拥有者。
// ComponentActivity.java
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
ContextAware,
LifecycleOwner,
ViewModelStoreOwner,
HasDefaultViewModelProviderFactory,
SavedStateRegistryOwner,
OnBackPressedDispatcherOwner,
ActivityResultRegistryOwner,
ActivityResultCaller {
....
@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;
}
}
从ComponentActivity 源码来看,实现了 ViewModelStoreOwner接口。
接下来,先看下ViewModelStore 源码,如何存储ViewModel以及ViewModel实例如何获取的
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());
}
/**
* 调用ViewModel的clear()方法,然后清除ViewModel;
如果ViewModelStore的拥有者(Activity/Fragment)销毁后不会重建,那么就需要调用此方法
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
从ViewModelStore的源码可以看成,ViewModel实际上是以HashMap
接下来,ViewModelProvider类get() 方法做了什么
@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");
}
// ViewModelStore中的Map的用于存 ViewModel的 Key
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
@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);
}
//如果从ViewModelStore获取到,直接返回
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
//使用工厂模式创建 ViewModel 实例
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
} else {
viewModel = mFactory.create(modelClass);
}
//将创建的 ViewModel 实例放进 mViewModelStore 缓存中
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
先尝试从ViewModelStore获取ViewModel实例,如果没有获取到,就使用Factory创建,创建出的ViewModel最后都会存放到mViewModelStore中。
接下来,回到顶部 ComponentActivity如何实现ViewModelStoreOwner接口方法getViewModelStore()。
// ComponentActivity
@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;
}
void ensureViewModelStore() {
//如果存储器是空,就先尝试 从lastNonConfigurationInstance从获取
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// 从 NonConfigurationInstances 恢复 ViewModelStore
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
//如果lastNonConfigurationInstance不存在,就new一个
mViewModelStore = new ViewModelStore();
}
}
}
先从NonConfigurationInstances中获取VIewModelStore实例,如果不存在,则会创建一个新的ViewModelSotre。从源码中可以看出,ViewModelStore会缓存到NonConfigurationInstances中。
接着,查看getLastNonConfigurationInstance()方法,NonConfigurationInstances如何会持有ViewModelStore。
// Activity
@Nullable
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
public Object onRetainNonConfigurationInstance() {
return null;
}
// ComponentActivity
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;
}
//new了一个NonConfigurationInstances
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
// 将viewModelStore交给 NonConfigurationInstances 对象保存
nci.viewModelStore = viewModelStore;
return nci;
}
通过源码注释可以知道mLastNonConfigurationInstances是由onRetainNonConfigurationInstance方法返回的。在onRetainNonConfigurationInstance()方法中viewModelStore赋值 NonConfigurationInstances 对象。
onRetainNonConfigurationInstance() 方法在什么时候被调用的?
// ActivityThread
void performDestroyActivity(ActivityClientRecord r, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
......
if (getNonConfigInstance) {
try {
// 调用retainNonConfigurationInstances() 方法
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);
}
}
}
......
}
在performDestroyActivity方法中会被调用,可以看到onRetainNonConfigurationInstance方法返回的Object会赋值给ActivityClientRecord的lastNonConfigurationInstances,这样子就保存了下来。
所以现在就可以明白,onRetainNonConfigurationInstance()方法就是Activity因配置改变而正要销毁时,且新Activity会立即创建,那么系统就会调用该方法。也就说,配置改变时 系统把viewModelStore存在了NonConfigurationInstances中,之后再重建的时候就可以通过getLastNonConfigurationInstance()方法来获取之前缓存的ViewModelStore实例。
在getLastNonConfigurationInstance()方法中,有个NonConfigurationInstances类型 变量mLastNonConfigurationInstances,NonConfigurationInstances
// Activity
NonConfigurationInstances mLastNonConfigurationInstances;
@Nullable
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
FragmentManagerNonConfig fragments;
ArrayMap<String, LoaderManager> loaders;
VoiceInteractor voiceInteractor;
}
// ComponentActivity
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
注意 : Activity 中静态内部类NonConfigurationInstances 和ComponentActivity中静态内部类NonConfigurationInstances是完全不同的类。
在Activity中,getLastNonConfigurationInstance()方法返回值是NonConfigurationInstances类中属性activity值, 该属性值也就是ComponentActivity中NonConfigurationInstances对象。这一点可以从上文中 ComponentActivity类中onRetainNonConfigurationInstance() 方法里面看出来。
下面,探究一下mLastNonConfigurationInstances 在哪里进行赋值的。
//Activity
final void attach(Context context, ActivityThread aThread, ...
NonConfigurationInstances lastNonConfigurationInstances,... ) {
...
//在attach方法中会进行赋值
mLastNonConfigurationInstances = lastNonConfigurationInstances;
...
}
mLastNonConfigurationInstances是在Activity的attach方法中赋值。attach方法是为Activity关联上下文环境(是Activity启动的核心流程),在ActivityThread的performLaunchActivity方法中调用,这里的lastNonConfigurationInstances是存在ActivityClientRecord中的一个组件信息。
// ActivityThread
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
······
//由ActivityClientRecord中获得
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, r.shareableActivityToken);
......
}
在performLaunchActivity方法中,可以看到lastNonConfigurationInstances是保存在ActivityClientRecord中的;又因为界面在销毁的时候调用performDestroyActivity方法,内部又会调用Activity的retainNonConfigurationInstances方法将lastNonConfigurationInstances缓存到ActivityClientRecord中,也就是存到应用本身的进程中了。
而ActivityClientRecord是存在ActivityThread的mActivities中:
//ActivityThrtead.java
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
ActivityThread 中的 ActivityClientRecord 是不受activity重建的影响,所以ActivityClientRecord中lastNonConfigurationInstances也不受影响,那么ComponentActivity中的NonConfigurationInstances的viewModelStore不受影响,因此viewModel也就不受影响了。
在ViewModelStore中会调用ViewModel的clear方法
// ViewModelStore
private final HashMap<String, ViewModel> mMap = new HashMap<>();
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
遍历缓存中viewmodel依次调用,那ViewModelStore的clear方法在何时调用
// ComponentActivity
public ComponentActivity() {
......
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
//activity生命周期处于destory状态
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
}
调用ViewModelStore中的clear()方法,前提条件是activity生命周期处于destory状态,并且配置没有发生改变。
如果能帮助您,请点赞、关注