解密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;
    }
相关推荐
长安初雪7 分钟前
Java客户端SpringDataRedis(RedisTemplate使用)
java·redis
aloha_78933 分钟前
B站宋红康JAVA基础视频教程(chapter14数据结构与集合源码)
java·数据结构·spring boot·算法·spring cloud·mybatis
尘浮生41 分钟前
Java项目实战II基于Java+Spring Boot+MySQL的洗衣店订单管理系统(开发文档+源码+数据库)
java·开发语言·数据库·spring boot·mysql·maven·intellij-idea
猿饵块1 小时前
cmake--get_filename_component
java·前端·c++
编程小白煎堆1 小时前
C语言:枚举类型
java·开发语言
王哈哈嘻嘻噜噜1 小时前
c语言中“函数指针”
java·c语言·数据结构
qq_339191141 小时前
spring boot admin集成,springboot2.x集成监控
java·前端·spring boot
苹果酱05672 小时前
通过springcloud gateway优雅的进行springcloud oauth2认证和权限控制
java·开发语言·spring boot·后端·中间件
Sunny_yiyi2 小时前
Gateway--服务网关
java·开发语言·gateway
Mike!2 小时前
C++进阶 set和map讲解
java·开发语言·数据结构·c++·set·map·cpp