一、概述
为什么需要ViewModel或者说ViewModel的优势是什么?
如果Activity或者Fragment销毁或者重建,存储在其中的数据会丢失,对于简单的数据比如Activity可以使用onSaveInstanceState() 方法来从onCreate() 中恢复数据,但这个方法只适合可以序列化再反序列化的少量数据,而不适合较大的数据。
另外一个问题是,界面经常需要异步操作,比如网络请求等,当界面销毁时,往往需要手动维护异步取消的动作,这无疑显得特别繁琐。并且把所有代码都写在界面中,会变得特别臃肿。
于是就需要将视图数据与界面分离,让层次清晰且高效。且ViewModel可以维护自己的生命周期,不需要手动操作,这无疑大大降低开发难度。
二、简单实现ViewModel
简单的ViewModel实现
class UserInfoViewModel : ViewModel() {
val userInfoLiveData: MutableLiveData<UserInfo> by lazy {
MutableLiveData<UserInfo>()
}
fun requestUserInfo() {
var userInfo = UserInfo("zhangsan", 18)
userInfoLiveData.value = userInfo
}
}
在Activity中调用
class MainActivity : AppCompatActivity() {
private val userInfoViewModel: UserInfoViewModel by lazy {
UserInfoViewModel()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var textview = findViewById<TextView>(R.id.tv_start)
userInfoViewModel.requestUserInfo()
userInfoViewModel.userInfoLiveData.observe(this, Observer<UserInfo> {
textview.text = it?.name
})
}
}
需要注意的点:
- 如果Activity重新创建,它接收的ViewModel实例与第一个Activity创建的实例相同。
- 当Activity完成(不能简单理解为destroy)时,框架会调用
ViewModel 的onCleared() 方法,以便它可以清理资源。 - ViewModel的生命周期比视图的生命周期长,所以ViewModel绝不能持有视图、Lifecycle或Activity的上下文等引用,否则就会造成内存泄漏。
- ViewModel绝不能观察对生命周期感知型对象(如LiveData对象)的更改。
- 如果ViewModel需要持有上下文,只能是
Application 的上下文,扩展AndroidViewModel 类并通过构造方法传入,如下代码:
class CustomApplicationViewModel(application: Application) : AndroidViewModel(application) {
override fun onCleared() {
super.onCleared()
}
}
class App : Application() {
val mCustomApplicationViewModel: CustomApplicationViewModel by lazy {
CustomApplicationViewModel(this)
}
override fun onCreate() {
super.onCreate()
}
}
var app: App = application as App
app.mCustomApplicationViewModel...
三、ViewModel的初始化方式
1、懒加载的初始化方式
这种方式是最简单的方式,但是封装架构的时候一般不这样用。代码见本文第二章节最上面ViewModel的代码。
2、简单工厂初始化ViewModel
ViewModel的类
class UserInfoViewModel : ViewModel() {
...
}
创建ViewModel对象
var userInfoViewModel = ViewModelProvider.NewInstanceFactory()
.create(UserInfoViewModel::class.java)
或另一种写法
var userInfoViewModel =
ViewModelProvider(this).get(UserInfoViewModel::class.java)
二者的区别create是创建一个新的实例,而get是先从HashMap中找,找不到就创建新的实例。看下源码也就知道为什么重新创建的ViewModel是同一个对象。
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 {
if (viewModel != null) {
}
}
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
} else {
viewModel = mFactory.create(modelClass);
}
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
3、使用ktx扩展(推荐)
添加依赖
implementation 'androidx.activity:activity-ktx:1.4.0'
implementation 'androidx.fragment:fragment-ktx:1.4.1'
ViewModel的类
class UserInfoViewModel : ViewModel() {
...
}
在Activity中创建ViewModel的方式
class MainActivity :AppCompatActivity() {
private val userInfoViewModel by viewModels<UserInfoViewModel>{
ViewModelProvider.NewInstanceFactory()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var textview = findViewById<TextView>(R.id.tv_start)
userInfoViewModel.requestUserInfo()
userInfoViewModel.userInfoLiveData.observe(this, Observer<UserInfo> {
textview.text = it?.name
})
}
}
在Fragment中创建ViewModel的方式
class CustomFragment:Fragment() {
private val userInfoViewModel by activityViewModels<UserInfoViewModel>{
ViewModelProvider.NewInstanceFactory()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
}
四、ViewModel的生命周期
ViewModel 对象存在的时间范围是获取 ViewModel 时传递给 ViewModelProvider 的 Lifecycle 。ViewModel 将一直留在内存中,直到限定其存在时间范围的 Lifecycle 永久消失:对于 activity,是在 activity 完成时;而对于 fragment,是在 fragment 分离时。
如下官方给的生命周期示例图:
上图说明了 Activity 经历屏幕旋转而后结束时所处的各种生命周期状态。该图还在 Activity 生命周期的旁边显示了 ViewModel 的生命周期。此图表说明了 Activity 的各种状态。这些基本状态同样适用于 Fragment 的生命周期。所以不要简单理解Activity的完成状态就是Destroy状态,上图屏幕旋转会经历一次onDestroy 。
通过上图可以看到,ViewModel创建后,它并不关心onDestroy之前的生命周期,它只关系视图控件(Activity,Fragment等)何时退出,我们跟踪一下源码。
1、Activity中ViewModel何时退出
首先通过上面的分析,可以知道ViewModel创建时会放入一个HashMap集合(ViewModelStore.mMap )中,调用的时候也是从这个集合中取,那么何时从这个集合中删除?
public ComponentActivity() {
...
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
mContextAwareHelper.clearAvailableContext();
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
ensureViewModelStore();
getLifecycle().removeObserver(this);
}
});
...
}
可以看到当触发onDestroy事件时,ViewModel的集合确实会被清除掉,但是下面又有担保的方式恢复,看下担保恢复的方法:
@SuppressWarnings("WeakerAccess")
void ensureViewModelStore() {
if (mViewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
}
可以看到配置的实例中如果还有就取出来重新用,如果没有就创建一个新的ViewModelStore给新的Activity用。继续跟源码,跟到Activity类就是系统方法了,并没有找到答案。
@Nullable
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
...
NonConfigurationInstances retainNonConfigurationInstances() {
Object activity = onRetainNonConfigurationInstance();
...
public Object onRetainNonConfigurationInstance() {
return null;
}
2、Fragment中ViewModel何时退出
与上面的原理类似,看ViewModelStore何时移除
void destroy() {
...
boolean beingRemoved = mFragment.mRemoving && !mFragment.isInBackStack();
if (beingRemoved && !mFragment.mBeingSaved) {
mFragmentStore.setSavedState(mFragment.mWho, null);
}
boolean shouldDestroy = beingRemoved
|| mFragmentStore.getNonConfig().shouldDestroy(mFragment);
if (shouldDestroy) {
FragmentHostCallback<?> host = mFragment.mHost;
boolean shouldClear;
if (host instanceof ViewModelStoreOwner) {
shouldClear = mFragmentStore.getNonConfig().isCleared();
} else if (host.getContext() instanceof Activity) {
Activity activity = (Activity) host.getContext();
shouldClear = !activity.isChangingConfigurations();
} else {
shouldClear = true;
}
if ((beingRemoved && !mFragment.mBeingSaved) || shouldClear) {
mFragmentStore.getNonConfig().clearNonConfigState(mFragment);
}
...
}
void clearNonConfigState(@NonNull Fragment f) {
if (FragmentManager.isLoggingEnabled(Log.DEBUG)) {
Log.d(TAG, "Clearing non-config state for " + f);
}
clearNonConfigStateInternal(f.mWho);
}
void clearNonConfigState(@NonNull String who) {
if (FragmentManager.isLoggingEnabled(Log.DEBUG)) {
Log.d(TAG, "Clearing non-config state for saved state of Fragment " + who);
}
clearNonConfigStateInternal(who);
}
private void clearNonConfigStateInternal(@NonNull String who) {
FragmentManagerViewModel childNonConfig = mChildNonConfigs.get(who);
if (childNonConfig != null) {
childNonConfig.onCleared();
mChildNonConfigs.remove(who);
}
ViewModelStore viewModelStore = mViewModelStores.get(who);
if (viewModelStore != null) {
viewModelStore.clear();
mViewModelStores.remove(who);
}
}
五、在Fragment之间共享数据
Activity 中的两个或更多 Fragment 需要相互通信是一种很常见的现象。多个 fragment 可以在其 activity 范围内共享 ViewModel 存储的数据。 先看下官方的示例代码,这里做了精简:
class SharedViewModel : ViewModel() {
val selected = MutableLiveData<Item>()
fun select(item: Item) {
selected.value = item
}
}
class ListFragment : Fragment() {
private val model: SharedViewModel by activityViewModels()
...
}
class DetailFragment : Fragment() {
private val model: SharedViewModel by activityViewModels()
...
}
请注意,这两个 Fragment 都会检索包含它们的 Activity。这样,当这两个 Fragment 各自获取 ViewModelProvider 时,它们会收到相同的 SharedViewModel 实例(其范围限定为该 Activity)。
此方法具有以下优势:
- Activity 不需要执行任何操作,也不需要对此通信有任何了解。
- 除了
SharedViewModel 约定之外,Fragment 不需要相互了解。如果其中一个 Fragment 消失,另一个 Fragment 将继续照常工作。 - 每个 Fragment 都有自己的生命周期,而不受另一个 Fragment 的生命周期的影响。如果一个 Fragment 替换另一个 Fragment,界面将继续工作而没有任何问题。
简单看下源码是如何实现的,Fragment在Attach时会调用FragmentManager的下面的方法:
@SuppressWarnings("deprecation")
@SuppressLint("SyntheticAccessor")
void attachController(@NonNull FragmentHostCallback<?> host,
@NonNull FragmentContainer container, @Nullable final Fragment parent) {
...
if (parent != null) {
mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
} else if (host instanceof ViewModelStoreOwner) {
ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
} else {
mNonConfig = new FragmentManagerViewModel(false);
}
...
getViewModelStore()方法获取的就是Activity的ViewModelStore
@NonNull
@Override
public ViewModelStore getViewModelStore() {
return FragmentActivity.this.getViewModelStore();
}
|