解密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;
    }
相关推荐
invicinble15 分钟前
关于微服务基本框架搭建--fegin最小使用demo
java·spring boot·微服务
梦想的旅途216 分钟前
企业微信API 实现企业微信消息的自动发送?
java·服务器·前端
大不点wow24 分钟前
为什么 Spring 更推荐构造器注入,而不是字段 `@Autowired`
java·spring·intellij-idea
用户298698530141 小时前
Java 创建 CSV 文件的三种实用方法:从零构建、数组写入与 Excel 转换
java·后端·excel
Cry丶1 小时前
一次 FTP 上传引发 504 Gateway Timeout 的排查复盘
java·nginx·ftp·openresty·被动模式·504 gateway·生产问题排查
码农进化录1 小时前
Java 程序员的 AI 进化论 | AI 辅助 CI/CD,让 AI 帮你做代码审查和部署决策
java·spring boot·openai
zyplayer-doc1 小时前
企业文档的进化:网盘和在线文档的下一个升级方向—知识库系统
java·大数据·数据库·人工智能·ocr
吃饱了得干活1 小时前
从零设计一个高并发点赞系统:缓存+MQ异步持久化实战
java·架构
奇牙coding1 小时前
gpt-5.6-luna 调用报 400 bad_request 怎么办?同样的 messages 在 gpt-5.5 没问题——两处校验差异和修复写法
java·windows·gpt·ai
凤山老林1 小时前
从“无法创建本地线程“异常说起:深度解析线程栈与JVM线程优化指南
java·开发语言·jvm