欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ViewModel

程序员文章站 2022-03-13 17:19:47
...

在看这个之前相信你已经了解了LifecycleLiveData

1. ViewModel是什么?
ViewModel:即View(视图)Model(数据)。它是负责准备和管理和UI组件(Fragment/Activity)相关的数据类,以及UI组件通信。

2. ViewModel生命周期

ViewModel
上图是用Activity作为例子,左侧表示Activity的生命周期状态,右侧绿色部分表示ViewModel的生命周期范围。当屏幕旋转的时候,Activity会被recreate,Activity会经过几个生命周期方法,但是这个时候ViewModel还是之前的对象,并没有被重新创建,只有当Activity的finish()方法被调用时,ViewModel.onCleared()方法会被调用,对象才会被销毁。这张图很好的描述了是当Activity被recreate时,ViewModel的生命周期。
另外,有个注意的地方:在ViewModel中不要持有Activity的引用。为什么要注意这一点呢?从上面的图我们看到,当Activity被recreate时,ViewModel对象并没有被销毁,如果Model持有Activity的引用时就可能会导致内存泄漏。那如果你要使用到Context对象怎么办呢,那就使用ViewModel的子类AndroidViewModel吧。

3. 组件间通信

public class UserViewModel extends ViewModel {
    private MutableLiveData<Integer> mProgress;

    public MutableLiveData<Integer> getProgress() {
        if(mProgress == null){
            mProgress = new MutableLiveData<>();
        }
        return mProgress;
    }
}
public class FragmentOne extends Fragment {
    private UserViewModel mUserViewModel ;

    public static FragmentOne getInstance(){
        return new FragmentOne();
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //注意这里是getActivity(),不是this
        ViewModelProvider viewModelProvider = new ViewModelProvider(getActivity());// create(UserViewModel.class);
        mUserViewModel = viewModelProvider.get(UserViewModel.class);
    }

    @OnClick(R.id.btn_set_name)
    void onViewClicked(View v){
        switch (v.getId()){
            case R.id.btn_set_value:
                mUserViewModel .getProgress().setValue(20);
                break;
        }
    }
}
public class FragmentTwo extends Fragment {
    private UserViewModel mUserViewModel ;

    public static FragmentTwo getInstance(){
        return new FragmentTwo ();
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //注意这里是getActivity(),不是this
        ViewModelProvider viewModelProvider = new ViewModelProvider(getActivity());// create(UserViewModel.class);
        mUserViewModel = viewModelProvider.get(UserViewModel.class);
        mUserViewModel.getProgress().observe(this, new Observer<Integer>() {
            @Override
            public void onChanged(Integer integer) {
                //mProgressBar1.setProgress(integer);
                Log.i(TAG, "mUserViewModal onChanged: " + integer);
            }
        });
    }
}

代码逻辑很简单,首先定义一个UserViewModel 类,继承自ViewModel。然后定义两个Fragment:FragmentOne和FragmentTwo。在FragmentOne中改变UserViewModel 中LiveData保存的数据,然后在FragmentTwo中会收到数据改变的通知。这样一个过程就完成了FragmentOne和FragmentTwo之间的一次简单的通信。至于其中的原理,相信看过LiveData这篇文章话,这都不是问题了。在上面的例子中有个小陷阱:在初始化mUserViewModel 时,ViewModelProvider()构造方法传入的是Activity对象,如果你改成Fragment对象,那FragmentTwo里就只能傻等了,永远不会收到数据改变的通知。因为如果传给ViewModelProvider()构造方法对象不同时,最终得到的就不是同一个ViewModel对象,这一点也可以通过打印两个Fragment中的mUserViewModel验证。

类图

ViewModel

  • ViewModelProviders是ViewModel工具类,该类提供了通过Fragment和Activity得到ViewModel的方法,而具体实现又是有ViewModelProvider实现的。ViewModelProvider是实现ViewModel创建、获取的工具类。在ViewModelProvider中定义了一个创建ViewModel的接口类——Factory。ViewModelProvider中有个ViewModelStore对象,用于存储ViewModel对象。
  • ViewModelStore是存储ViewModel的类,具体实现是通过HashMap来保存ViewModle对象。
  • ViewModel是个抽象类,里面只定义了一个onCleared()方法,该方法在ViewModel不在被使用时调用。
  • ViewModel有一个子类AndroidViewModel,这个类是便于要在ViewModel中使用Context对象,因为我们前面提到是不能在ViewModel中持有Activity的引用。
  • ViewModelStores是ViewModelStore的工厂方法类,它会关联HolderFragment,HolderFragment有个嵌套类——HolderFragmentManager。

时序图
在使用ViewModel的例子中,也许你会察觉得到一个ViewModel对象需要的步骤有点多啊,怎么不直接new一个出来呢?在你看到ViewModel的类图关系后,你应该就能明白了,因为是会缓存ViewModel对象的。下面以在Fragment中得到ViewModel对象为例看下整个过程的时序图。
ViewModel
时序图看起来比较复杂,但是它只描述了两个过程:

  • 得到ViewModel对象
  • HolderFragment被销毁时,ViewModel收到onCleared()通知

源码基本分析
下面分析一下为什么同一个Activity可以获取相同的UserModal实例。

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

发现构造函数第一个参数ViewModelStoreOwner ,即存储owner ViewModel的容器。所以如果是同一个Activity,那么ViewModelStoreOwner 是相同的。

那么怎么获取到的ViewModel的呢?继续往下看ViewModelProvider.get

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

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

发现就是以UserViewModel的class拼接了一个key,然后在ViewModelStore中查询的,因为我们构造ViewModelProvider的时候是传入的相同的Activity,所以ViewModelStore是相同的,故在获取的时候将得到相同的UserViewModel.

此时我们在看看ViewModelStore的实现

public class ViewModelStore {

    private final HashMap<String, ViewModel> mMap = new HashMap<>();

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

    final ViewModel get(String key) {
        return mMap.get(key);
    }

    Set<String> keys() {
        return new HashSet<>(mMap.keySet());
    }

    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

ViewModelStore是缓存ViewModel的类,put()、get()方法用于存取ViewModel对象,另外提供了clear()方法用于清空缓存的ViewModel对象,在该方法中会调用ViewModel.onCleared()方法通知ViewModel对象不再被使用

参考博客:
https://blog.csdn.net/zhuzp_blog/article/details/78910535

相关标签: android ViewModel