美文网首页
ViewModel介绍与源码分析

ViewModel介绍与源码分析

作者: 不收敛的柯西 | 来源:发表于2020-07-15 14:14 被阅读0次

概览

ViewModel 旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel 让数据可在发生屏幕旋转等配置更改后继续留存。

  • 数据缓存: 应用的某个 Activity 中可能包含用户列表。因配置更改而重新创建 Activity 后,新 Activity 必须重新提取用户列表。对于简单的数据, Activity 可以使用 onSaveInstanceState() 方法从 onCreate() 中的捆绑包恢复其数据,但此方法仅适合可以序列化再反序列化的少量数据,而不适合数量可能较大的数据,如用户列表或位图。
  • 异步调用管理: Activity 经常需要进行异步调用,这些调用可能需要一些时间才能返回结果。Activity 需要管理这些调用,并确保系统在其销毁后清理这些调用以避免潜在的内存泄露。此项管理需要大量的维护工作,并且在因配置更改而重新创建对象的情况下,会造成资源的浪费,因为对象可能需要重新发出之前的调用。
  • 代码责任分离: Activity 主要用于显示界面数据、对用户操作做出响应或处理操作系统通信(如权限请求)。如果要求 Activity 也负责从数据库或网络加载数据,那么会使类越发膨胀。为 Activity 分配过多的责任可能会导致单个类尝试自己处理应用的所有工作,而不是将工作委托给其他类。以这种方式为 Activity 分配过多的责任也会大大增加测试的难度。

ActivityFragment中分离出视图数据所有权的做法更易行且更高效。

项目依赖

打开 项目build.gradle 文件并添加 google() 代码库,如下所示:

  allprojects {
        repositories {
            google()
        }
    }

然后, 在 appbuild.gradle添加 ViewModel 组件代码库,如下所示:

def lifecycle_version = "2.2.0"
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"

ViewModel 的生命周期

ViewModel 对象存在的时间范围是获取 ViewModel 时传递给 ViewModelProviderLifecycleViewModel 将一直留在内存中,直到限定其存在时间范围的 Lifecycle 永久消失。对于 ActivityFragmentViewModel生命周期即:在被初始化后直至对应页面的 onDestroy 被回调后。

下图 说明了 Activity 经历屏幕旋转而后结束的过程中所处的各种生命周期状态。同样适用于 Fragment 的生命周期。

viewmodel-lifecycle.png

在 Fragment 之间共享数据

两个 Fragment 可以使用其 Activity 范围共享 ViewModel 来处理此类通信,如以下示例代码所示:

class SharedViewModel : ViewModel() {
    val selected = MutableLiveData<Item>()

    fun select(item: Item) {
        selected.value = item
    }
}

class MasterFragment : Fragment() {

    private lateinit var itemSelector: Selector

    // Use the 'by activityViewModels()' Kotlin property delegate
    // from the fragment-ktx artifact
    private val model: SharedViewModel by activityViewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        itemSelector.setOnClickListener { item ->
            // Update the UI
        }
    }
}

class DetailFragment : Fragment() {

    // Use the 'by activityViewModels()' Kotlin property delegate
    // from the fragment-ktx artifact
    private val model: SharedViewModel by activityViewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        model.selected.observe(viewLifecycleOwner, Observer<Item> { item ->
            // Update the UI
        })
    }
}

从示例中可以看出:

  • Activity 不需要执行任何操作,也不需要对此通信有任何了解。
  • 除了 SharedViewModel 约定之外,Fragment 不需要相互了解。如果其中一个 Fragment 消失,另一个 Fragment 将继续照常工作。
  • 每个 Fragment 都有自己的生命周期,而不受另一个 Fragment 的生命周期的影响。如果一个 Fragment 替换另一个 Fragment,界面将继续工作而没有任何问题。

延伸

1、AndroidViewModel

由于 ViewModel 生命周期可能长与Activity 生命周期,所以为了避免内存泄漏,Google 禁止在 ViewModel 中持有 ContextActivityView 的引用。如果一定需要使用 Context, 可以继承 AndroidViewModel 类,内部维护了一个 ApplicationContext

public class AndroidViewModel extends ViewModel {
    @SuppressLint("StaticFieldLeak")
    private Application mApplication;

    public AndroidViewModel(@NonNull Application application) {
        mApplication = application;
    }

    /**
     * Return the application.
     */
    @SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"})
    @NonNull
    public <T extends Application> T getApplication() {
        return (T) mApplication;
    }
}

2、保存状态(SavedStateHandle)

开篇已经说过,ViewModel 对象可以处理配置更改,因此您无需担心旋转时或其他情况下的状态。但是,如果您需要处理系统发起的进程终止,则可以使用 onSaveInstanceState() 作为备用方式。

添加依赖:

implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"

需要使用可扩展 AbstractSavedStateVMFactory 的Factory 来创建ViewModel,或者在ActivityFragment中使用默认Factory创建它们。

private val mViewModel: ViewModelSavedState by lazy {
       ViewModelProvider(this).get(ViewModelSavedState::class.java)
}

ViewModel 添加一个接收 SavedStateHandle 的构造函数

class SavedStateViewModel(private val state: SavedStateHandle) : ViewModel() {

  private val NAME_KEY = "name"

    fun getName(): LiveData<String?>? {
        return savedStateHandle.getLiveData(NAME_KEY)
    }

    fun saveNewName(newName: String?) {
        savedStateHandle.set(NAME_KEY, newName)
    }

}

测试SavedStateHandle方法:

  1. 保存状态数据到SavedStateHandle
  2. 点击手机Home键,返回桌面
  3. 使用命令adb shell ps -A |grep com.view.model.explore查看当前App是否存活
  4. 使用命令adb shell am kill com.view.model.explore结束App进程
  5. 重新进入应用,查看是否可以通过SavedStateHandle获取到对应数据

ViewModel源码分析

ViewModel的源码非常简单,从生命周期我们可以看出主要包括初始化和清除俩个关键点

1、ViewModelProvider初始化

ViewModel实例化代码:

ViewModelProvider(this).get(ViewModel::class.java)

首先看一下 ViewModel的构造方法:

    public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
    }

    public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
        //获取Activity、Fragment中的mViewModelStore
        this(owner.getViewModelStore(), factory);
    }

    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    }

熟悉 ViewModel 的同学应该清楚,可以通过自定义 Factory 的方式来实例化不同构造函数的 ViewModel

主要看下使用默认Factory来实例化 ViewModel的流程。若当前 ownerHasDefaultViewModelProviderFactory,则使用默认的Factory来处理初始化,否则每次初始化都会生成一个新的ViewModel实例。NewInstanceFactory的逻辑非常简单,就不看了。

ActivityFragment基类均有实现HasDefaultViewModelProviderFactory接口,两个实现方案类似,下面看一下Activity的实现代码:

public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
        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 (mDefaultFactory == null) {
            mDefaultFactory = new SavedStateViewModelFactory(
                    getApplication(),
                    this,
                    getIntent() != null ? getIntent().getExtras() : null);
        }
        return mDefaultFactory;
    }

很简单的逻辑。创建了一个可以保存 SavedStateFactory,后续将由它来创建 ViewModel。对于Factory中的具体逻辑,将在后续获取ViewModel实例处分析。

2、获取ViewModel实例

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

    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        //根据key值,获取mViewModelStore中以HashMap形式缓存的ViewModel
        ViewModel viewModel = mViewModelStore.get(key);

        //mViewModelStore已有modelClass生成的ViewModel实例,返回该实例
        if (modelClass.isInstance(viewModel)) {
            //回调OnRequeryFactory接口
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;
        }

        if (mFactory instanceof KeyedFactory) {
            //根据key生成ViewModel
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            //normal生成ViewModel
            viewModel = (mFactory).create(modelClass);
        }
        //将生成的ViewModel缓存到mViewModelStore
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

简单的说,首先会根据keymViewModelStore的缓存中找是否已有实例生成,若有则返回该实例,若无缓存则创建新的实例并缓存到mViewModelStore中。

看一下mViewModelStore中缓存实例的put方法,这里做了一层保护,若缓存的key中已存在实例,则回调onCleared方法,结束生命周期。

    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }

最后,看一下默认SavedStateViewModelFactory创建ViewModel的主要实现:

public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
    boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
    Constructor<T> constructor;
    //查找构造方法中是否有SavedStateHandle参数
    if (isAndroidViewModel) {
        constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
    } else {
        constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
    }
    // doesn't need SavedStateHandle
    if (constructor == null) {
        return mFactory.create(modelClass);
    }

    SavedStateHandleController controller = SavedStateHandleController.create(
            mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
    if (isAndroidViewModel) {
        viewmodel = constructor.newInstance(mApplication, controller.getHandle());
    } else {
        viewmodel = constructor.newInstance(controller.getHandle());
    }
    viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller);
    return viewmodel;
}

由此可知默认Factory支持继承自ViewModelAndroidViewModel的子类在构造方法中添加SavedStateHandle参数

3、ViewModel销毁

销毁的方法Activity与Fragment也类似。即当前生命周期回调 onDestroy且不是configuration改变时,会清空mViewModelStore中的ViewModel缓存。

    public void onDestroy() {
        mCalled = true;
        FragmentActivity activity = getActivity();
        boolean isChangingConfigurations = activity != null && activity.isChangingConfigurations();
        if (mViewModelStore != null && !isChangingConfigurations) {
            mViewModelStore.clear();
        }
    }

接着看一下ViewModelStoreclear方法,至此结束。

    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }

参考

ViewModel 概览(Android developers文档)
ViewModel 的已保存状态模块
Incorporate Lifecycle-Aware Components(CodeLabs)

相关文章

网友评论

      本文标题:ViewModel介绍与源码分析

      本文链接:https://www.haomeiwen.com/subject/muhsxktx.html