一、概述
为什么需要viewmodel或者说viewmodel的优势是什么?
如果Activity或者Fragment销毁或者重建,存储在其中的数据会丢失,对于简单的数据比如Activity可以使用onSaveInstanceState()
方法来从onCreate()
中恢复数据,但这个方法只适合可以序列化再反序列化的少量数据,而不适合较大的数据。
另外一个问题是,界面经常需要异步操作,比如网络请求等,当界面销毁时,往往需要手动维护异步取消的动作,这无疑显得特别繁琐。并且把所有代码都写在界面中,会变得特别臃肿。
于是就需要将视图数据与界面分离,让层次清晰且高效。且viewmodel可以维护自己的生命周期,不需要手动操作,这无疑大大降低开发难度。
二、简单实现viewmodel
简单的viewmodel实现
class UserInfoviewmodel : viewmodel() {
//延迟创建LiveData对象
val userInfoLiveData: mutablelivedata<UserInfo> by lazy {
//创建LiveData
mutablelivedata<UserInfo>()
}
/**
* 请求用户信息
*/
fun requestUserInfo() {
//伪代码:请求到了用户信息
var userInfo = UserInfo("zhangsan", 18)
//将请求到的值userInfo设置给LiveData,更新数据
userInfoLiveData.value = userInfo
}
}
在Activity中调用
class MainActivity : AppCompatActivity() {
//创建viewmodel对象
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)
//调用viewmodel的方法请求数据
userInfoviewmodel.requestUserInfo()
//通知修改数据
userInfoviewmodel.userInfoLiveData.observe(this, Observer<UserInfo> {
//拿到了请求到的UserInfo对象,通知修改数据
textview.text = it?.name
})
}
}
需要注意的点:
- 如果Activity重新创建,它接收的viewmodel实例与第一个Activity创建的实例相同。
- 当Activity完成(不能简单理解为destroy)时,框架会调用
viewmodel
的onCleared()
方法,以便它可以清理资源。 - viewmodel的生命周期比视图的生命周期长,所以viewmodel绝不能持有视图、Lifecycle或Activity的上下文等引用,否则就会造成内存泄漏。
- viewmodel绝不能观察对生命周期感知型对象(如LiveData对象)的更改。
- 如果viewmodel需要持有上下文,只能是
Application
的上下文,扩展Androidviewmodel
类并通过构造方法传入,如下代码:
/**
* 继承Androidviewmodel,创建时需要通过构造方法传入Application对象
*/
class CustomApplicationviewmodel(application: Application) : Androidviewmodel(application) {
override fun onCleared() {
super.onCleared()
}
}
//因为这个viewmodel持有Application的引用,所以它的生命周期会很长
//于是可以在Application类中创建全局的viewmodel
class App : Application() {
//创建全局viewmodel对象
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) {
//viewmodelStore底层是HashMap集合
//通过key从集合中取出viewmodel对象
viewmodel viewmodel = mviewmodelStore.get(key);
//判断这个viewmodel类的实例对象是否是需要viewmodel类的实例对象
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);
}
//并且将创建的viewmodel实例对象存在HashMap中
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() {
//通过ktx扩展创建viewmodel对象
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)
//调用viewmodel的方法请求数据
userInfoviewmodel.requestUserInfo()
//通知修改数据
userInfoviewmodel.userInfoLiveData.observe(this, Observer<UserInfo> {
//拿到了请求到的UserInfo对象,通知修改数据
textview.text = it?.name
})
}
}
在Fragment中创建viewmodel的方式
class CustomFragment:Fragment() {
//通过ktx扩展创建viewmodel对象
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
)中,调用的时候也是从这个集合中取,那么何时从这个集合中删除?
//ComponentActivity部分源码
//ComponentActivity的构造方法,创建Activity时调用
public ComponentActivity() {
...
getLifecycle().addobserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
//当收到Activity的ON_DESTROY事件时
if (event == Lifecycle.Event.ON_DESTROY) {
// Clear out the available context
mContextAwareHelper.clearavailableContext();
// And clear the viewmodelStore
//isChangingConfigurations()永远返回false
if (!isChangingConfigurations()) {
//清除viewmodelStore
getviewmodelStore().clear();
}
}
}
});
getLifecycle().addobserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
//担保viewmodelStore,如果不是正常的onDestroy这里会恢复viewmodelStore
ensureviewmodelStore();
getLifecycle().removeObserver(this);
}
});
...
}
可以看到当触发onDestroy事件时,viewmodel的集合确实会被清除掉,但是下面又有担保的方式恢复,看下担保恢复的方法:
@SuppressWarnings("WeakerAccess") /* synthetic access */
void ensureviewmodelStore() {
if (mviewmodelStore == null) {
//取出之前配置的实例
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
//恢复viewmodelStore
// Restore the viewmodelStore from NonConfigurationInstances
mviewmodelStore = nc.viewmodelStore;
}
//如果viewmodel还是null,创建新的viewmodelStore给这个新的Activity用
if (mviewmodelStore == null) {
mviewmodelStore = new viewmodelStore();
}
}
}
可以看到配置的实例中如果还有就取出来重新用,如果没有就创建一个新的viewmodelStore给新的Activity用。继续跟源码,跟到Activity类就是系统方法了,并没有找到答案。
@Nullable
public Object getLastNonConfigurationInstance() {
//如果不为null返回一个Activity实例
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
//给上面的Activity实例赋值的地方
...
NonConfigurationInstances retainNonConfigurationInstances() {
Object activity = onRetainNonConfigurationInstance();
...
/**
* Called by the system,as part of destroying an
* activity due to a configuration change,...
*/
public Object onRetainNonConfigurationInstance() {
return null; //系统调用的方法,只能看到null
}
2、Fragment中viewmodel何时退出
与上面的原理类似,看viewmodelStore何时移除
//FragmentStateManager
//销毁时的方法
void destroy() {
...
//是否Fragment应该被移除
boolean beingRemoved = mFragment.mRemoving && !mFragment.isInBackStack();
// Clear any prevIoUs saved state
if (beingRemoved && !mFragment.mBeingSaved) {
mFragmentStore.setSavedState(mFragment.mWho, null);
}
//Fragment是否应该被销毁
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) {
// Clear and remove the Fragment's child non config state
FragmentManagerviewmodel childNonConfig = mChildNonConfigs.get(who);
if (childNonConfig != null) {
childNonConfig.onCleared();
mChildNonConfigs.remove(who);
}
// Clear and remove the Fragment's viewmodelStore
viewmodelStore viewmodelStore = mviewmodelStores.get(who);
if (viewmodelStore != null) {
//移除viewmodelStore
viewmodelStore.clear();
mviewmodelStores.remove(who);
}
}
五、在Fragment之间共享数据
Activity 中的两个或更多 Fragment 需要相互通信是一种很常见的现象。多个 fragment 可以在其 activity 范围内共享 viewmodel
存储的数据。 先看下官方的示例代码,这里做了精简:
class Sharedviewmodel : viewmodel() {
//创建LiveData
val selected = mutablelivedata<Item>()
//为LiveData赋值
fun select(item: Item) {
selected.value = item
}
}
class ListFragment : Fragment() {
//创建(获取)viewmodel对象
private val model: Sharedviewmodel by activityviewmodels()
...
}
class DetailFragment : Fragment() {
//创建(获取)viewmodel对象
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) {
...
// Get the FragmentManagerviewmodel
if (parent != null) {
mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
} else if (host instanceof viewmodelStoreOwner) {
//获取viewmodelStore赋值给FragmentManager自己的viewmodelStore
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();
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。