今天是2022年最后一天,祝大家元旦快乐。
ViewModel
初识
ViewModel
是Jetpack
组件之一,它注重生命周期的方式存储和管理界面的数据,它是让数据在屏幕旋转等配置更改后继续留存。通俗一点就是:手机屏幕发生旋转后,数据依然还在。
ViewModel
没出来之前,怎么存取数据?
在ViewModel
出现之前,一般屏幕发生旋转时候,Activity
生命周期会重新创建,我们会在onSaveInstance()
里面保存数据,然后在onCreate(Bundle saveInstance)
里面取数据,但是这个数据有弊端,必须要实现序列化和反序列化,并且这个数据不能太大。
class ViewModelActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//(2)取数据
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
//(1)存数据
}
}
所以ViewModel
就是为了解决这个问题的。
ViewModel
的使用
(1)添加ViewModel
依赖
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
(2)定义一个类继承ViewModel(AndroidViewModel)
即可
class MyViewModel : ViewModel() {
val updateLiveData by lazy { MutableLiveData<String>() }
}
(3)Activity
创建ViewModel
实例
class ViewModelActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
var myViewModel = ViewModelProvider(this).get(MyViewModel::class.java)
myViewModel.updateLiveData.observe(this) {
//update data
}
}
}
在Activity
中,我们可以使用ViewModelProvider
来得到 ViewMode
的实例。
如果在ViewModel
中,我们需要使用到上下文Context
对象(toast 或者获取系统服务等等),我们可以继承AndroidViewModel
来构建ViewModel
class AndroidModel(app:Application): AndroidViewModel(app) {
}
此时这个AndroidModel
的创建和上面的类似,也是用ViewModelProvider
来获得。
ViewModel
常常结合LiveData
使用,然后在我们的Activity
或者Fragemnt
中去监听LiveData
的改变
然后去在Activity
中做UI的更新逻辑,例如,我们需要根据网络请求来决定是否弹出一个DialogFragment
,我们的网络请求放在ViewModel
中,DialogFragment
必须在Activity
或者Fragment
中弹出来(因为DialogFragment
的弹出不能使用Application
作为Context
),所以此时我们必须使用LiveData
。当网络请求完成之后,我们改变LiveData
的值,并且在Activity
或者Fragment
监听LiveData
的变化,然后作出弹出DailogFragment
的操作
在Fragment
之间共享数据
现在的App
中使用Fragment
是很常见的,之前我们从Activity
中向Fragment
中传递数据,我们使用Bundle
来传递(在创建Fragment
的时候),但是在Activity
中如果需要动态传递(随时传递)数据给Fragment
,我们平常的做法可能是在Activity
中持有Fragment
的应用,然后在Activity
中去调用对于Fragment
的某些方法传递数据,或者利用通知系统(EventBus)
,但是我们如果在BFragment
中促使Activity
中的数据改变,要通知到CFragment
的Ui修改的话,目前的场景只能使用EventBus
。当然也可以使用我们这里的ViewModel
了
class ShareViewModel : ViewModel() {
val selected = MutableLiveData<Item>()
fun select(item: Item) {
selected.value = item
}
}
class ListFragment : Fragment() {
private lateinit var itemSelector: Selector
// Use the 'by activityViewModels()' Kotlin property delegate
// from the fragment-ktx artifact
private val model: ShareViewModel?=null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model = ViewModelProvider(requireActivity()).get(ShareViewModel::class.java)
itemSelector.setOnClickListener { item ->
// Update the UI
}
}
class DetailFragment : Fragment() {
// Use the 'by activityViewModels()' Kotlin property delegate
// from the fragment-ktx artifact
private val model: ShareViewModel?=null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model = ViewModelProvider(requireActivity()).get(ShareViewModel::class.java)
model.selected.observe(viewLifecycleOwner, Observer<Item> { item ->
// Update the UI
})
}
在上面的两个Fragment
在获取ViewModel
的时候 传递的都是requireActivity()
,那么获取到的ViewModel
的实例其实就是同一个SharedViewModel
,所以当Activity
或者任何一个Fragment
中 改变了SharedViewModel中LiveData
的数据,都会及时的通知到。这种方法有以下好处:
Activity
不需要执行任何操作,也不需要对此通信有任何了解。
除了 SharedViewModel
之外,Fragment
不需要相互了解。如果其中一个 Fragment
消失,另一个 Fragment
将继续照常工作。
每个 Fragment
都有自己的生命周期,而不受另一个 Fragment
的生命周期的影响。如果一个Fragment
替换另一个Fragment
,界面将继续工作而没有任何问题。
ViewModel
的源码解析
这里基本只需要知道onCleared()
方法就行了,自定义ViewModel
并重写这个方法,讲释放资源的逻辑放在这个方法中就行
ViewModelProvider
类
获取ViewModel
的实例时,我们是使用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;
}
创建ViewModelProvider
的时候需要传递一个ViewModelStore
和一个Factory
,而我们构建的时候只传递了一个this(Activity)
,其实就是一个ViewModelStoreOwner
,
AppCompatActivity -->FragmentActivity -->ComponentActivity --> ViewModelStoreOwner
有上面这样一个继承实现关系,我们的AppCompatActivity
其实可以说是实现了ViewModelStoreOwner
的,最终返回的是ComponentActivity
中的mViewModelStore
关于Factory
后面再讲
@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();
}
}
}
这里就是获取ViewModelStore
的方法,可以看到在ensureViewModelStore
方法中,我们会首先判断mViewModelStore
是否为null
,然后通过getLastNonConfigurationInstance()
得到一个NonConfigurationInstances
实例,这里其实就是当Activity
旋转的时候ViewModel
中的数据还会存在的奥秘,通过nc
可以获取重建之前的mViewModelStore
,然后从ViewModelStore
里面根据类名获取ViewModel
的实例,所以获取到的ViewModel
在旋转前后其实是同一个实例,在我们的App系统configuration
发生改变的时候 就会回调onRetainNonConfigurationInstance()
这个方法
@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;
}
//Activity中 的方法
@Nullable
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
这里就是将ViewModelStore
进行保存。 这里getLastNonConfigurationInstance
方法 最后其实是返回Activity中的mLastNonConfigurationInstances
变量的activity
对象,我们看看这mLastNonConfigurationInstances
在哪里赋值,我们知道,我们的Activity
的启动其实最终都会走到ActivityThread
类中,当我们启动一个Activity
的时候会执行其中的 performLaunchActivity
方法最终会调用到 Activity
的attach
方法,lastNonConfigurationInstances
是存在ActivityClientRecord
中的一个组件信息
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = mActivities.get(token);
Class<? extends Activity> activityClass = null;
if (localLOGV) Slog.v(TAG, "Performing finish of " + r);
if (r != null) {
activityClass = r.activity.getClass();
r.activity.mConfigChangeFlags |= configChanges;
if (finishing) {
r.activity.mFinished = true;
}
performPauseActivityIfNeeded(r, "destroy");
if (!r.stopped) {
callActivityOnStop(r, false /* saveState */, "destroy");
}
if (getNonConfigInstance) {
try {
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);
}
}
}
try {
r.activity.mCalled = false;
mInstrumentation.callActivityOnDestroy(r.activity);
if (!r.activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + safeToComponentShortString(r.intent) +
" did not call through to super.onDestroy()");
}
if (r.window != null) {
r.window.closeAllPanels();
}
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
"Unable to destroy activity " + safeToComponentShortString(r.intent)
+ ": " + e.toString(), e);
}
}
r.setState(ON_DESTROY);
}
schedulePurgeIdler();
// updatePendingActivityConfiguration() reads from mActivities to update
// ActivityClientRecord which runs in a different thread. Protect modifications to
// mActivities to avoid race.
synchronized (mResourcesManager) {
mActivities.remove(token);
}
StrictMode.decrementExpectedActivityCount(activityClass);
return r;
}
在屏幕旋转造成的的Activity
重建的时候 就会给lastNonConfigurationInstances
这个变量赋值,这样就能够在Activity
重建的时候 获取到之前的ViewModel
了。而我们的ViewModel
中onClear
方法什么时候执行呢,在我们的ComponetActivity
构方法中
public ComponentActivity() {
// ......省略部分代码
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
在onDestory
之后 并且isChangingConfigurations()
为false的时候,才会去执行getViewModelStore().clear()
; 的操作间接调用到ViewModel的onCleared()
方法
get()获取ViewModel
在这里通过get方法创建ViewModel
传入的参数是 所需要创建的ViewModel
的Class
对象
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");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
@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;
}
最终通过下面的get方法获取ViewModel
,当我们从ViewModelStore
根据key值去获取ViewMode
l为null的时候,如果为null 就是用Factory
进行创建。所以后面我们也可以定义自己的Factory
来创建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());
}
/**
* 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
,主要是ViewModel
的存取.
总结
1.现在的Activity
底层已经继承了 ComponentActivity
,并实现了 ViewModelStoreOwner
接口,通过ViewModelProvider
使用默认工厂 创建了 viewModel
,并通过唯一Key值 进行标识,
存储到了 ViewModelStore
中。等下次需要的时候即可通过唯一Key
值进行获取。
2.由于ComponentActivity
实现了ViewModelStoreOwner
接口,实现了 getViewModelStore
方法,当屏幕旋转的时候,会先调用 onRetainNonConfigurationInstance()
方法
将viewModelStore
保存起来,当屏幕旋转之后,会在ensureViewModel()
方法中再调用 getLastNonConfigurationInstance
方法将数据恢复,如果为空的话,会重新创建viewModelStore
,
并存储在全局中,以便以下次发生变化的时候,能够通过onRetainNonConfigurationInstance
保存起来。
3.最后当页面销毁并且没有配置更改的时候,会将viewModelStore
中的数据 进行清除操作。