一、问题
- 为什么要用ViewModel,有什么好处
- ViewModel如何在配置变化时保留数据
- 在界面旋转时,ViewModel中的请求还在执行吗
- ViewModel如何做到在页面销毁时,停止协程中的工作
二、解答
问题1、为什么要用ViewModel,有什么好处
- 数据状态的维护
- 旋转后保证数据还在
- 跟随Activity生命周期的作用域,防止内存泄漏
- 便于单元测试
- 只用于做纯的业务代码
- 不持有Activity或View对象
- 便于共享
同一个Activity下的不同Fragment之间共享
问题2、ViewModel如何在配置变化时保留数据
利用的是Activity重建机制中的NonConfigurationInstances来保存
也就是在配置变化时,Activity会被销毁,但是重建时会从旧的Activity中拿到ViewModelStore
问题3、在界面旋转时,ViewModel中的请求还在执行吗
还会在,并不会调用ViewModel的clear方法
因为系统会判断是否因为页面在旋转导致的,如果是则不销毁ViewModel
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();
}
}
}
});
问题4、ViewModel如何做到在页面销毁时,停止协程中的工作
这是因为ViewModel会去监听Activity/Fragment的生命周期
在onDestory时,且不需要重建的情况下,会去调用ViewModel的clear方法
在这个方法里面会去close掉所有支持Closeable的对象
如 viewModelScope
//viewModelScope
public val ViewModel.viewModelScope: CoroutineScope
get() {
val scope: CoroutineScope? = this.getTag(JOB_KEY)
if (scope != null) {
return scope
}
return setTagIfAbsent(
JOB_KEY,
CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
)
}
internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
override val coroutineContext: CoroutineContext = context
override fun close() {
coroutineContext.cancel()
}
}
// ViewModel的clear方法
@MainThread
final void clear() {
mCleared = true;
// Since clear() is final, this method is still called on mock objects
// and in those cases, mBagOfTags is null. It'll always be empty though
// because setTagIfAbsent and getTag are not final so we can skip
// clearing it
if (mBagOfTags != null) {
synchronized (mBagOfTags) {
for (Object value : mBagOfTags.values()) {
// see comment for the similar call in setTagIfAbsent
closeWithRuntimeException(value);
}
}
}
onCleared();
}
private static void closeWithRuntimeException(Object obj) {
if (obj instanceof Closeable) {
try {
((Closeable) obj).close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
网友评论