ViewModel介绍(官网介绍)

ViewModel 类是一种业务逻辑或屏幕级状态容器。它用于将状态公开给界面,以及封装相关的业务逻辑。 它的主要优点是,它可以缓存状态,并可在配置更改后持久保留相应状态。这意味着在 activity 之间导航时或进行配置更改后(例如旋转屏幕时),界面将无需重新提取数据。
注意:AndroidX以后才能使用ViewModel

ViewModel的优势

个人觉得ViewModel的出现给我们带来了以下几点便利:

1、代码分离

  • ViewModel使得UI和业务逻辑分离。这意味着UI代码(如XML布局)专注于显示信息,而ViewModel则处理数据获取、验证和转换。这种分离使得代码更易于阅读和维护。

2、数据绑定:

  • ViewModel通常与LiveData一起使用,这意味着当ViewModel中的数据发生变化时,UI会自动更新。这减少了手动更新UI的需要,降低了出错的可能性。

3、易于测试

  • 由于ViewModel包含业务逻辑,因此可以独立于UI进行测试。这有助于确保你的应用程序在更改UI或添加新功能时仍然正常工作。

4、生命周期管理

  • ViewModel + LiveData可以管理数据的生命周期,确保在适当的时候加载和保存数据。这有助于减少资源消耗并提高应用程序的性能。

5、数据共享

多个Fragment可以共享同一个ViewModel,这使得在多个Fragment之间共享数据变得更加简单和高效

ViewModel是如何实现?

看源码前,我们先大概了解这几个类是干什么的?

  • ViewModel:
    这是ViewModel的基类,它提供了基本的生命周期感知功能。ViewModel内部使用了一个Handler来处理UI线程与后台线程之间的通信,确保数据更新在UI线程进行。

  • ViewModelProvider:
    这个类负责创建和提供ViewModel的实例。它根据请求的ViewModel类型来创建或查找已经存在的实例,并将其与LifecycleOwner(通常是Activity或Fragment)关联起来。

  • ViewModelStore:
    ViewModelStore是一个内部类,用于存储和管理ViewModel的实例。它确保每个LifecycleOwner都有一个唯一的ViewModel实例。

我们将从ViewModel的生命周期和ViewModel是如何实现数据持久化这两个角度分析。

1、ViewModel生命周期

在这里插入图片描述
从上述图中,我们可以看到当我们acivity正常destroy我们的ViewModel才会正常销毁。

1、ViewModel是如何创建的?

我们一般通过ViewModelProvider获取ViewModel

//传入类名获取ViewModel
mViewModel = ViewModelProvider(this).get(MainViewModel::class.java) 
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
	//获取canonicalName,这里面其中包含类名和包的路径
    String canonicalName = modelClass.getCanonicalName();
    if (canonicalName == null) {
        throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
    }
    return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}

@NonNull
@MainThread
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 {
        //noinspection StatementWithEmptyBody
        if (viewModel != null) {
            // TODO: log a warning.
        }
    }
    if (mFactory instanceof KeyedFactory) {
        viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
    } else {
    	//没有获取到的时候通过mFactory创建ViewModel
        viewModel = mFactory.create(modelClass);
    }
    //将创建的ViewModel存入mViewwModelStore
    mViewModelStore.put(key, viewModel);
    return (T) viewModel;
}

1、创建viewModel实例
在这里插入图片描述
2、将ViewModel传入mViewModelStore
在这里插入图片描述
从上面代码可以看到,ViewModelStore会将viewModel和key放到一个HashMap去存放。

viewModelStore是哪里来的呢?
先看看下面代码
在这里插入图片描述
viewModelStore是一个关于activity的静态变量,可以看到通常每个Activity或Fragment都有自己的ViewModelStore,这是为了确保每个组件都有自己的ViewModel实例,从而保持其数据状态的独立性。

然而,如果有特殊需求,你可以通过一些方法让两个Activity共享同一个ViewModelStore,如以下代码。

class SharedViewModelStore {  
    private val viewModelStore = ViewModelStore()  
      
    fun getViewModelStore(): ViewModelStore {  
        return viewModelStore  
    }  
}

class ActivityA : AppCompatActivity() {  
      
    private val sharedViewModelStore = SharedViewModelStore.getViewModelStore()  
    private val myViewModel: MyViewModel by viewModels {  
        MyViewModelFactory(application, sharedViewModelStore)  
    }  
      
    // ...  
}  
  
class ActivityB : AppCompatActivity() {  
      
    private val sharedViewModelStore = SharedViewModelStore.getViewModelStore()  
    private val myViewModel: MyViewModel by viewModels {  
        MyViewModelFactory(application, sharedViewModelStore)  
    }  
      
    // ...  
}

2、ViewModel是什么时候销毁的?

大家都知道ViewModel在Activity在onDestroy的时候会销毁,那么有个问题,在屏幕旋转的时候也会走onDestroy方法,为什么旋转的时候就不会销毁ViewModel呢?

    public ComponentActivity() {
		...
        getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                	//这个就是关键所在,判断是否正常销毁,还是改变配置. 如果是正常销毁则会把ViewModelStore的viewModel给销毁掉
                    if (!isChangingConfigurations()) {
                        getViewModelStore().clear();
                    }
                }
            }
        });
		...
    }

2、ViewModel如何实现数据持久化?

当屏幕发生旋转的时候,会走到这个onRetainNonConfigurationInstance函数。

    @NonNull
    @Override
    public ViewModelStore getViewModelStore() {
        if (getApplication() == null) {
            throw new IllegalStateException("Your activity is not yet attached to the "
                    + "Application instance. You can't request ViewModel before onCreate call.");
        }
        if (mViewModelStore == null) {
        	//获取非配置实例
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }
	...
	//调用新实例,允许您提取来自上一个实例的任何有用的动态状态。
	@Nullable
    public Object getLastNonConfigurationInstance() {
        return mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.activity : null;
    }

上述代码看出,当屏幕旋转后,会从NonConfigurationInstances 获取viewModelStore,当获取不到的时候再新建一个新的ViewModelStore.
那么这个mLastNonConfigurationInstances 是什么时候开始赋值的呢?

    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
            IBinder shareableActivityToken) {
        attachBaseContext(context);
        ...
        //在activity的attach的时候开始赋值
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        if (voiceInteractor != null) {
            if (lastNonConfigurationInstances != null) {
                mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
            } else {
                mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
                        Looper.myLooper());
            }
        }
		...
    }

mLastNonConfigurationInstances 的获取我们看到了,但是我们实际上想要的是viewModelStore。我们的ViewModelStore是在哪里开始获取呢?
让我们看看下面代码,代码中自有答案。

    @Override
    @Nullable
    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
            //没有人调用getViewModelStore(),所以看看我们上一个NonConfigurationInstance中是否有现有的ViewModelStore
            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;
    }

这里就是ViewModelStore赋值的时候。

那么我们再问 这个是onRetainNonConfigurationInstance是什么时候开始调用呢?
onRetainNonConfigurationInstance在Activity的onDestroy的时候会被调用。

public final class ActivityThread{
    @Override
    public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
            boolean getNonConfigInstance, String reason) {
        //调用performDestroyActivity()方法    
        ActivityClientRecord r = performDestroyActivity(token, finishing,
                configChanges, getNonConfigInstance, reason);
        ...        
    } 
}

总结:

存储 ViewModel

  • 创建 ViewModelStore:当Activity或Fragment被创建时,它们的ViewModelStore实例也会被创建。
  • 获取 ViewModel:使用ViewModelProvider和ViewModelStore来获取ViewModel实例。如果ViewModel不存在,则会被创建并存储在ViewModelStore中。
  • 使用 ViewModel:在Activity或Fragment中使用ViewModel来管理数据。

配置更改时的处理

  • 销毁 Activity:当发生配置更改时(如屏幕旋转),当前的Activity实例会被销毁。
  • 保存 ViewModelStore:在Activity销毁之前,它的ViewModelStore会被保存。这个保存过程涉及到多个层次:
    ComponentActivity将ViewModelStore存入其NonConfigurationInstances。
    Activity将ComponentActivity的NonConfigurationInstances(包含ViewModelStore)存入其自己的NonConfigurationInstances。
    Activity的NonConfigurationInstances被存入ActivityClientRecord。
    ActivityClientRecord被存入ActivityThread的mActivities ArrayMap中。
  • 恢复 ViewModel重建 Activity:配置更改后,一个新的Activity实例会被创建。
  • 恢复 ViewModelStore:当新的Activity实例被创建时,它会通过调用attach()方法并传入与旧Activity相同的ActivityClientRecord来恢复其ViewModelStore。
  • 获取已存在的ViewModel:新的Activity实例使用ViewModelProvider和恢复的ViewModelStore来获取已存在的ViewModel实例。由于ViewModelStore中已经存在该ViewModel实例,因此它会被返回而不是创建新的实例。
  • 恢复 ViewModel 的状态:由于ViewModel实例没有被销毁,因此它管理的数据状态也会被恢复。
  • 销毁时的处理:当Activity或Fragment最终被销毁时(例如用户按下返回键或系统资源紧张),它们的ViewModelStore也会被销毁。然而,由于ViewModel的生命周期与ViewModelStore分离,ViewModel实例本身不会被销毁,除非没有Activity或Fragment引用它。
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐