ViewModel的作用及怎样使用
ViewModel使用在MVVM框架中,结合LiveData实现View层和控制层解耦。ViewModel类旨在以注重生命周期的方式存储和管理界面相关的数据,ViewModel作为一个类保存着LiveData类型的数据,这样做的好处在于当Activity因为配置的改变(如屏幕旋转)而重新创建时,界面中所持有的数据能够不被销毁。 对于简单的数据,Activity可以从onCreate()中利用Bundle对象恢复数据,但此方法仅适合可以序列化再反序列化的少量数据, 而不适合数量可能较大的数据,如列表或位图。
先回顾一下onSaveInstanceState方法怎样使用的:
Android中的activity通过onSaveInstanceState()方法保存activity的状态,在onCreate或者onRestoreInstanceState方法恢复状态。
public class SaveStateActivityTest extends Activity {
public static final String TAG = "SaveStateActivity";
@Override
public void onCreate(Bundle savedInstanceState) {
if (savedInstanceState!=null) {
Log.v(TAG, savedInstanceState.getString("data"));
}
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Log.v(TAG, "onCreate");
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
Log.v(TAG, savedInstanceState.getString("data"));
super.onRestoreInstanceState(savedInstanceState);
Log.v(TAG, "onRestoreInstanceState");
}
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putString("data", "mzq");
super.onSaveInstanceState(outState);
Log.v(TAG, "onSaveInstanceState");
}
}
ViewModel是怎样使用的
架构组件为界面控制器提供了
ViewModel
辅助程序类,该类负责为界面准备数据。在配置更改期间会自动保留ViewModel
对象,以便它们存储的数据立即可供下一个 activity 或 fragment 实例使用。例如,如果您需要在应用中显示用户列表,请确保将获取和保留该用户列表的责任分配给ViewModel
,而不是 activity 或 fragment。
class MyViewModel : ViewModel() {
private val users: MutableLiveData<List<User>> by lazy {
MutableLiveData<List<User>>().also {
loadUsers()
}
}
fun getUsers(): LiveData<List<User>> {
return users
}
private fun loadUsers() {
// Do an asynchronous operation to fetch users.
}
}
然后,您可以从 Activity 访问该列表,如下所示:
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// 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.
// Use the 'by viewModels()' Kotlin property delegate
// from the activity-ktx artifact
val model: MyViewModel by viewModels()
model.getUsers().observe(this, Observer<List<User>>{ users ->
// update UI
})
}
}
进入主题
我们常用的创建ViewModel的方法:
SearchViewModel viewModel = new ViewModelProvider(this).get(MzqViewModel.class);
我们看一下ViewModelProvider类的构造方法:
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory)owner).getDefaultViewModelProviderFactory()
: NewInstanceFactory.getInstance());
}
我们看一下ViewModelStoreOwner是何方神圣:
它是Android.lifecycle包下的一个接口:
package androidx.lifecycle;
import androidx.annotation.NonNull;
/**
* A scope that owns {@link ViewModelStore}.
* <p>
* A responsibility of an implementation of this interface is to retain owned ViewModelStore
* during the configuration changes and call {@link ViewModelStore#clear()}, when this scope is
* going to be destroyed.
*
* @see ViewTreeViewModelStoreOwner
*/
@SuppressWarnings("WeakerAccess")
public interface ViewModelStoreOwner {
/**
* Returns owned {@link ViewModelStore}
*
* @return a {@code ViewModelStore}
*/
@NonNull
ViewModelStore getViewModelStore();
}
这个接口只有一个方法,getViewModelStore()方法,该方法返回ViewModelStore对象,从命名上可得知该对象上用于存储ViewModel的,一个Activity可能关联多个ViewModel,因此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());
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
果真如我们的猜想,它内部是一个HashMap的键值对集合,值即为我们的ViewModel对象。
Activity实现了ViewModelStoreOwner,并且重写了getVeiwModelStore()的方法。我们来看一下具体实现。
我们验证一下Activity实现了ViewModelStoreOwner:
public class AppCompatActivity extends FragmentActivity ....
public class FragmentActivity extends ComponentActivity ...
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
ContextAware,
LifecycleOwner,
ViewModelStoreOwner,
HasDefaultViewModelProviderFactory,
SavedStateRegistryOwner,
OnBackPressedDispatcherOwner,
ActivityResultRegistryOwner,
ActivityResultCaller
可以看到ComponentActivity类实现了ViewModelStoreOwner接口。
我们继续查看一下ComponentActivity是怎样实现getViewModelStore方法的:
@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; //是ViewModelStore类型的对象
}
@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();
}
}
}
这个方法当本地变量mViewModelStore为空时,会通过getLastNonConfigurationInstance()方法尝试获得ViewModeStore,如果没有的活就重新创建。而ViewModel不被销毁的玄机就在这里。
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
Activity类中:
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
NonConfigurationInstaces是个很简单的类,包含了ViweModelStore,而getLastNonConfigurationInstace会尝试获取上次配置改变被保存的NonConfigurationInstaces对象
那么在哪里对NonConfigurationInstances中的ViewModel进行赋值存储呢?
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;
}
viewModeStore不为null或者lastNonConfigurationInstacne不为null时,我们就把viewModelStore保存在NonConfigurationInstance对象中。当重新创建时,我们就能通过getLastNonConfigurationInstance()方法得到我们保存到的ViewModelStore了,而ViewModelStore里面就有我们的ViewMode。
总结
当Activity要销毁前回调onRetainNonConfigurationInstance()方法保存viewModelStore在lastNonConfigurationInstacne.activity(NonConfigurationInstance)中,然后在getViewModelStore方法中又会首先去获取lastNonConfigurationInstace.activity(NonConfigurationInstance)对象。