JetPack架构---ViewModel作用、使用与原理

一、ViewModel的作用

1、ViewModel 用于管理与界面(Activity、Fragment)相关的数据。

2、ViewModel让数据可在发生屏幕旋转等配置更改后仍能继续存在。

3、ViewModel 让Activity与Fragment共享数据更方便。

ViewModel作为JetPack中一个重要部件,本身并不复杂。ViewModel总结起来就一个功能:保存数据,并且在ViewModel中的数据,不会因为配置变化(横竖屏转换)而丢失,只有在Activity真正被销毁的时候,才会真正销毁数据。

二、ViewModel的使用

简单使用示例如下:

0、依赖添加。在build中添加如下依赖

def lifecycle_version = "2.2.0"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"

1、定义。定义一个类MyViewModel继承ViewModel(抽象类)

public class MyViewModel extends ViewModel {
    public int count;
}

2、简单使用。2.2.0版本后,获取ViewModelProvider的方式发生了变化,新版本取值方式如下:

MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
viewModel.count = 1;

三、原理说明

ViewModel为什么能在横竖屏变化后,仍能保存数据?ViewModel的生命周期为什么与Activity不同?

这里从三条线来说明:

一个是ViewModel如何存入到Activity中。

一个是ViewModel如何取出。

一个是Activity配置发生变化,ViewModel数据如何被保存下来的。

1、ViewModel的存入、取出(两条线并行)

以上面使用的例子为例,当定义好MyViewModel后,在界面上取值的流程如下:


//(1)get逻辑,取值入口
MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);

//(2)ViewModelProvider中的get方法
@MainThread
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);
}

//(3)真实将viewModel存入到mViewModelStore中。
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        //从mViewModelStore中取值,首次为null
        ViewModel viewModel = mViewModelStore.get(key);
        //isInstance逻辑:当入参是null时,返回为false
        //此处判断null与该类是否属于ViewModel
        //当不为null,且该类属于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.
            }
        }
        //当viewModel为null的时候,新建viewModel对象
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        //将viewModel对象,存入到mViewModelStore中,供后续使用
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

通过以上三个步骤,完成获取当前viewModel值的首次新建、存入与取出的逻辑。

2、Activity配置发生变化,如何保存ViewModel数据

具体流程是这样的:

(1)从ActivityThread中RelaunchActivity开始:

android.app.ActivityThread#handleRelaunchActivity

--> handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents, pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity");

--> handleDestroyActivity(r.token, false, configChanges, true, reason);

--> performDestroyActivity(token, finishing, configChanges, getNonConfigInstance, reason);

--> activity.retainNonConfigurationInstances()   

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);
                    }
                }
}

retainNonConfigurationInstances该方法位于activity中

--> android.app.Activity#retainNonConfigurationInstances



//(1)配置变化时,被调用
NonConfigurationInstances retainNonConfigurationInstances() {
        //重点是这句,获取activity
        Object activity = onRetainNonConfigurationInstance();
        HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
        FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

       
        mFragments.doLoaderStart();
        mFragments.doLoaderStop(true);
        ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();

        if (activity == null && children == null && fragments == null && loaders == null
                && mVoiceInteractor == null) {
            return null;
        }
   
        NonConfigurationInstances nci = new NonConfigurationInstances();
        //获取的activity存入nci中。
        nci.activity = activity;
        nci.children = children;
        nci.fragments = fragments;
        nci.loaders = loaders;
        if (mVoiceInteractor != null) {
            mVoiceInteractor.retainInstance();
            nci.voiceInteractor = mVoiceInteractor;
        }
        return nci;
    }


//(2)onRetainNonConfigurationInstance在ComponentActivity中的具体实现

public final Object onRetainNonConfigurationInstance() {
        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
            //此时获取的的这个nc,也就是上一次保存的NonConfigurationInstances中的activity(只是 
            //名称是activity,本质上是一个object)
            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;
    }


//(3)上一次保存的NonConfigurationInstances中的activity
public Object getLastNonConfigurationInstance() {
        return mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.activity : null;
    }

将目光转回ActivityThread中,继续后续流程

--> android.app.ActivityThread#handleLaunchActivity

--> android.app.ActivityThread#performLaunchActivity

--> performLaunchActivity中调用activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window, r.configCallback);

--> android.app.Activity#onCreate(android.os.Bundle) 在onCreate中重新赋值

至此,配置变化为何可以保存viewModel的流程已经完毕。

 

四、懂的更多

1、ViewModel中如何传入额外参数?

参考androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory。如需要额外传入参数,重写Factory。

再以本文前的MyViewModel为例,如果需要传入参数:

//(1)MyViewModel定义
public class MyViewModel extends ViewModel {
    private int count;
    public MyViewModel(int count){
        this.count =  count;
    }
    
    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }
    
}

//(2)定义Factory
public class MyViewModelFactory implements ViewModelProvider.Factory {
    private int count;
    public MyViewModelFactory(int count){
        this.count = count;
    }

    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        return (T)new MyViewModel(count);
    }
}

//(3)传入参数与取值
MyViewModel myViewModel = new ViewModelProvider(this,new MyViewModelFactory(10)).get(MyViewModel.class);

 

2、ViewModel数据是在什么时候清理的?

生命周期走到destroy,且不是因为配置变化的时候,如下:

//ComponentActivity的构造函数中:androidx.activity.ComponentActivity#ComponentActivity()
getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    //生命周期走到destroy
                    if (!isChangingConfigurations()) {
                        //并且不是因为配置变化,则清理数据
                        getViewModelStore().clear();
                    }
                }
            }
        });

  ViewModel在JetPack中,常与其他组件如LiveData等一起使用,共同构造了JetPack的生态。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android Jetpack是Google提供的一套用于加速Android应用开发的工具包,其中包括了许多架构组件,其中之一就是ViewModelViewModel是一种设计模式,用于保存和管理与UI相关的数据。在传统的Android开发中,当屏幕旋转或者因为其他原因导致Activity或Fragment重建时,之前保存的临时数据就会丢失。而ViewModel的出现解决了这个问题。 ViewModel的主要作用是将数据与UI组件分离。它的工作方式是创建一个ViewModel类,并在其中保存需要与UI组件交互的数据。这样,当屏幕旋转或重建时,ViewModel实例不会销毁,数据也会得到保留。然后,在Activity或Fragment中,通过获取ViewModel实例,可以轻松地访问这些数据。 使用ViewModel的好处有很多。首先,它可以避免内存泄漏,因为ViewModel的生命周期与Activity或Fragment无关。其次,它可以节省资源,因为当Activity或Fragment销毁时,ViewModel实例可以被系统缓存起来,下次再创建时可以直接返回该实例。另外,由于ViewModel保存了与UI相关的数据,可以减少因为屏幕旋转导致的数据重复加载的问题。 在使用ViewModel时,你可以选择使用Android Jetpack中的其他架构组件来进一步提高开发效率,比如通过LiveData实现数据的观察和通知,或者通过DataBinding来实现UI与数据的自动绑定。 总之,ViewModel是Android Jetpack中非常重要的一个架构组件,它的出现实现了数据与UI的解耦,提高了开发效率,并且解决了数据丢失的问题。希望通过这篇文档的详解,你对ViewModel有了更深入的理解。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值