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的生态。

猜你喜欢

转载自blog.csdn.net/yangzhaomuma/article/details/106180242