Android Jetpack-ViewModel
发布时间:2023-03-24 11:11:17 所属栏目:教程 来源:
导读:为什么需要viewmodel或者说viewmodel的优势是什么?
一、概述
如果Activity或者Fragment销毁或者重建,存储在其中的数据会丢失,对于简单的数据比如Activity可以使用onSaveInstanceState()方法来从onCreate()中恢
一、概述
如果Activity或者Fragment销毁或者重建,存储在其中的数据会丢失,对于简单的数据比如Activity可以使用onSaveInstanceState()方法来从onCreate()中恢
为什么需要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(); } (编辑:汽车网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
推荐文章
站长推荐