解密ViewModel是如何实现页面状态保存功能?

前言

互联网越来越卷,要求越来越高,我们不能仅仅停留在api的使用层面,要深入源码,阅读源码,分析源码,理解源码,最后模仿源码,才能快速提高 " 搬砖 "水平!

一,简介

ViewModel作为谷歌Jetpack重要组件之一,它不仅可以保存当前页面的状态,还能在页面配置更改后持久化保留相应状态。

二,使用步骤

1. 普通ViewModel

1.定义MainViewModel类继承ViewModel()

kotlin 复制代码
class MainViewModel:ViewModel() {
   val mainLiveData = MutableLiveData<String>() 
}

2.在activity中通过ViewModelProvider获取MainViewModel实例对象。

kotlin 复制代码
class MainActivity : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val viewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).
        get(MainViewModel::class.java)
        viewModel.mainLiveData.observe(this){
            
        }
    }

通过定义一个MainViewModel类继承自ViewModel抽象类,然后再Activity中初始化MainViewModel。

2.提供Content的AndroidViewModel

  1. 如果需要在ManViewModel中使用context,MainViewModel需要继承AndroidViewModel。
arduino 复制代码
class MainViewModel(application: Application):AndroidViewModel(application) {
   val mainLiveData = MutableLiveData<String>()

}

2 .使用ViewModelProvider获取MainViewModel实例对象

kotlin 复制代码
 val viewModel = ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory(application)).get(MainViewModel::class.java)
        viewModel.mainLiveData.observe(this){

        }

三,源码分析

1. Activity绑定

  1. Activity的父类ComponentActivity 实现了ViewModelStoreOwner接口,
scala 复制代码
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        ContextAware,
        LifecycleOwner,
        ViewModelStoreOwner,
}
  1. ViewModelStoreOwner 中定义了getViewModelStore 方法用来提供ViewModelStore
csharp 复制代码
public interface ViewModelStoreOwner {
     //返回一个ViewModelStore
    @NonNull
    ViewModelStore getViewModelStore();
}
  1. ViewModelStore 中定义HashMap 用来保存ViewModel实例。
typescript 复制代码
public class ViewModelStore {
    //定义HashMap集合存储ViewModel
    private final HashMap<String, ViewModel> mMap = new HashMap<>();
    //存储ViewModel
    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }
   //根据key值获取对应的ViewModel
    final ViewModel get(String key) {
        return mMap.get(key);
    }

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

    /**
     *  回收所有ViewModel并清空HashMap.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}
  1. 由于ComponentActivity 实现了ViewModelStoreOwner 接口,在ComponentActivity 中调用getViewModelStore 可以获取ViewModel 的存储类ViewModelStoreViewModelStore 中使用HashMap 存储ViewModel,如果存储的ViewModel的key值相同,则删除oldViewModel旧的ViewModel,替换成最新的ViewModel。

  2. ComponentActivity 中使用Lifecycle 对当前activity生命周期进行监听,并在on Destroy 生命周期中对保存在ViewModelStore 中的ViewModel进行清空释放。

less 复制代码
public ComponentActivity() {
        Lifecycle lifecycle = getLifecycle();
        ......................
        .........................
        //使用lifecycle对activity生命周期进行监听
        getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                 //如果生命周期为onDestory
                if (event == Lifecycle.Event.ON_DESTROY) {
                    // 清除context
                    mContextAwareHelper.clearAvailableContext();
                    //如果应用配置没有发生改变
                    if (!isChangingConfigurations()) {
                        //清除ViewModelStore中HashMap存储的ViewModel
                        getViewModelStore().clear();
                    }
                }
            }
        });
     getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                //初始化ViewModelStore
                ensureViewModelStore();
                getLifecycle().removeObserver(this);
            }
        });

ComponentActivity 退出销毁时,会调用getViewModelStore().clear(), 清除ViewModelStoreHashMap 存储的ViewModel,释放资源。

  1. 调用ensureViewModelStore 进行ViewModelStore进行初始化。
scss 复制代码
void ensureViewModelStore() {
        //如果存储类为null
        if (mViewModelStore == null) {
            //获取上一个保存的NonConfigurationInstances实例对象
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // 从 NonConfigurationInstances 恢复 ViewModelStore
                mViewModelStore = nc.viewModelStore;
            }
            //否则创建新的ViewModelStore
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
    }

  static final class NonConfigurationInstances {
        Object custom;
        //成员变量用来保存ViewModelStore
        ViewModelStore viewModelStore;
    }

NonConfigurationIns tances 是一个静态类,它有两个成员变量ObjectViewModelStore ;如果ViewModelstore 如果不存在,则将NonConfigurationIns tances 的成员变量保存的ViewModelstore 赋值给ViewModelStore

typescript 复制代码
    //获取上一次保存的数据
    @Nullable
    public Object getLastNonConfigurationInstance() {
        return mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.activity : null;
    }

mLastNonConfigurationInstances 是上一次保存的数据实例,当Activity被创建的时候,调用attach 方法,mLastNonConfigurationInstances = lastNonConfigurationInstances; 当应用配置发生改变时,如屏幕旋转,会导致Activity重建,这个时候会获取上一次保存的mLastNonConfigurationInstances ,并取出其中存储的viewmodestore ,最后得到ViewModel,直接从ViewModel中得到数据恢复界面列表。

2. ViewModel保存

  1. 使用ViewModelProvider 创建ViewModel
less 复制代码
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
     //创建ViewModelProvider对象
    return new ViewModelProvider(activity);
    }
  1. 由于当前activity实现ViewModelStoreOwner 接口,所以可以通过getViewModelStore 方法获取ViewModelStore对象。
less 复制代码
 public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
      //由于当前activity实现ViewModelStoreOwner接口,
      //所以可以通过getViewModelStore方法获取ViewModelStore对象。
     
     this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
    }
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    }
相关推荐
Mast Sail几秒前
windows下authas调试tomcat
java·windows·tomcat·authas
长征coder13 分钟前
Maven Deploy的依赖与引用方的依赖不同
java·maven
努力学习的明21 分钟前
Spring MVC 对 JavaWeb 的优化:从核心组件到注解
java·spring·mvc·web
小雅痞22 分钟前
[Java][Leetcode middle] 238. 除自身以外数组的乘积
java·leetcode
小李不想说话34 分钟前
HTTPS 加密原理
java·网络·网络协议·学习·安全·http·https
斯密码赛我是美女37 分钟前
ssti刷刷刷
java·服务器·前端
进击的小白菜1 小时前
Java回溯算法解决非递减子序列问题(LeetCode 491)的深度解析
java·算法·leetcode
众乐乐_20081 小时前
Java 后端给前端传Long值,精度丢失的问题与解决
java·前端·状态模式
北辰浮光1 小时前
[springboot]SSM日期数据转换易见问题
java·spring boot·后端
木梓辛铭2 小时前
Spring Cache的详细使用
java·后端·spring