ViewModel

Jetpack之ViewModel那些事

ViewModel简介

在页面(Activity/Fragment)功能较为简单的情况下,我们通常会将UI交互、与数据获取等相关的业务逻辑全部写在页面中。但是在页面功能复杂的情况下,代码量会变的非常多,也违反了"单一功能原则"。 页面只应该负责处理用户与UI控件的交互,并将数据展示到屏幕上,而数据获取相关的业务逻辑应该单独处理和存放。为了解决这个问题,Android为我们提供了ViewModel类,专门用于存放页面所需的数据。
ViewModel可以这么理解: 它是介于View(视图)和Model(数据模型)之间的一个东西。它起到了桥梁的作用,使视图和数据既能分离,也能保持通信。

ViewModel使用示例

ViewModel存储UI相关数据,供Activity或Fragment等UI控制器使用。当设备配置信息发生改变时,ViewModel会自动保存数据,以便新生成的UI能够恢复数据。例如,如果你想在你的App中显示一个用户信息列表,那么就可以把数据保存到ViewModel中,替代原来的onSaveInstanceState()方案,示例代码如下所示。

public class MyViewModel extends ViewModel {
    private MutableLiveData<List<User>> users;
    public LiveData<List<User>> getUsers() {
        if (users == null) {
            users = new MutableLiveData<List<User>>();
            loadUsers();
        }
        return users;
    }

    private void loadUsers() {
        // Do an asynchronous operation to fetch users.
    }
}

可以用如下方式在新的Activity中访问用户列表数据。

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same MyViewModel instance created by the first activity.

        MyViewModel model = new ViewModelProvider(this).get(MyViewModel.class);
        model.getUsers().observe(this, users -> {
            // update UI
        });
    }
}

如果这个Activity是重新创建的,那么它会获取到第一个Activity所创建的MyViewModel实例。当持有这个对象的Activity结束生命周期时,Android框架会调用ViewModel的onCleared()方法,以便它能释放资源。

ViewModel生命周期

Viewmodel的作用域是从ViewModelProvider获取ViewModel开始,到Activity的onDestory或Fragment的onDetatch结束。例如,Activity因为屏幕旋转导致生命周期变更,ViewModel的生命周期如下图所示。

ViewModel_第1张图片

  • 补充说明:
  1. ViewModelProvider调用get方法时,ViewModel才与Activity的生命周期绑定。
  2. 第一个Activity调用onDestroy时,ViewModel也会调用一次onCleared

ViewModel实现原理

ViewModelProvider通过ViewModelStore存取ViewModel,ViewModelStore将ViewModel存储到HashMap中管理,默认的key为ViewModel实例的类名。如下所示:

public class ViewModelProvider {
            ......
    /**
     * Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or
     * an activity), associated with this {@code ViewModelProvider}.
     * 

* The created ViewModel is associated with the given scope and will be retained * as long as the scope is alive (e.g. if it is an activity, until it is * finished or process is killed). * * @param key The key to use to identify the ViewModel. * @param modelClass The class of the ViewModel to create an instance of it if it is not * present. * @param The type parameter for the ViewModel. * @return A ViewModel that is an instance of the given type {@code T}. */ @SuppressWarnings("unchecked") @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 { viewModel = mFactory.create(modelClass); } mViewModelStore.put(key, viewModel); return (T) viewModel; } ...... }

ViewModelProvider中的ViewModelStore对象是从ViewModelStoreOwner中获取的,ViewModelStoreOwner是一个interface,它的实例一般为Activity或Fragment。如下所示:

public class ViewModelProvider {
            ......
    /**
     * Creates {@code ViewModelProvider}. This will create {@code ViewModels}
     * and retain them in a store of the given {@code ViewModelStoreOwner}.
     * 

* This method will use the * {@link HasDefaultViewModelProviderFactory#getDefaultViewModelProviderFactory() default factory} * if the owner implements {@link HasDefaultViewModelProviderFactory}. Otherwise, a * {@link NewInstanceFactory} will be used. */ public ViewModelProvider(@NonNull ViewModelStoreOwner owner) { this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory() : NewInstanceFactory.getInstance()); } ..... }

ComponentActivity实现了getViewModelStore方法,如果有缓存的ViewModelStore则返回缓存,如果没有则新建一个ViewModelStore,传递给ViewModelProvider。如下所示:


public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        ContextAware,
        LifecycleOwner,
        ViewModelStoreOwner,
        HasDefaultViewModelProviderFactory,
        SavedStateRegistryOwner,
        OnBackPressedDispatcherOwner,
        ActivityResultRegistryOwner,
        ActivityResultCaller {
                    ......
            /**
            * Returns the {@link ViewModelStore} associated with this activity
            * 

* Overriding this method is no longer supported and this method will be made * final in a future version of ComponentActivity. * * @return a {@code ViewModelStore} * @throws IllegalStateException if called before the Activity is attached to the Application * instance i.e., before onCreate() */ @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."); } ensureViewModelStore(); return mViewModelStore; } @SuppressWarnings("WeakerAccess") /* synthetic access */ void ensureViewModelStore() { if (mViewModelStore == null) { NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance(); if (nc != null) { // Restore the ViewModelStore from NonConfigurationInstances mViewModelStore = nc.viewModelStore; } if (mViewModelStore == null) { mViewModelStore = new ViewModelStore(); } } } ...... }

Activity结束生命周期时会将ViewModelStore缓存到NonConfigurationInstances中,而NonConfigurationInstances是由ActivityThread通过ActivityClientRecord类维护的,启动新的Activity时会把NonConfigurationInstances传递给这个Activity,这样就可以理解ViewModel为什么可以在Activity之间传递数据了。

public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        ContextAware,
        LifecycleOwner,
        ViewModelStoreOwner,
        HasDefaultViewModelProviderFactory,
        SavedStateRegistryOwner,
        OnBackPressedDispatcherOwner,
        ActivityResultRegistryOwner,
        ActivityResultCaller {
                    ......
            /**
            * Retain all appropriate non-config state.  You can NOT
            * override this yourself!  Use a {@link androidx.lifecycle.ViewModel} if you want to
            * retain your own non config state.
            */
            @Override
            @Nullable
            @SuppressWarnings("deprecation")
            public final Object onRetainNonConfigurationInstance() {
                // Maintain backward compatibility.
                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
                    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;
            }
        }

当ViewModelStoreOwner是Fragment时,实现原理更加复杂,但是最终还是用的Activity的存储逻辑,本质上是一样的。

总结

ViewModel可以帮助我们更好地将UI与业务逻辑从代码层面分离开来,依赖ViewModel的生命周期特性,我们不需要在关心屏幕旋转导致数据丢失的问题,只关注渲染数据即可;需要注意的是,如果想在ViewModel中使用Context,建议使用ViewModel的子类AndroidViewModel。

你可能感兴趣的:(#,Jetpack,android,java,开发语言)