ViewModel原理分析
-
使用:维护数据的稳定性
-
使用场景:优化用户体验感
-
Activity重建(横竖屏切换,声明周期的问题)
- 解决:将数据放到ViewModel中
-
-
使用原理:除非Activity执行onDestroy销毁ViewModel才脱离Activity
使用ViewModel:解决横竖屏切换后数据丢失问题:
-
问题描述:当页面进行横竖屏切换时,生命周期改变,导致数据丢失
-
实例:点击计数按钮可观察到数字不断增加,一旦发生横竖屏切换后,数据就归零
-
运行截图:
-
竖屏状态下进行点击
-
将手机横放:切换成横屏
-
可以观察到数据被立即置空了
-
-
代码示例:
-
工程结构:
MainActivity代码
package com.derry.viewmodel import android.os.Bundle import android.util.Log import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.ViewModelProvider // 绑定机制 import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { // 300个字段 var number : Int = 0; override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // myViewModel = MyViewModel() 不能直接实例化,因为如果能这样写,系统不可控了 //当横竖屏切换时,没有这一句,数据会消失,但是不会丢失,只是隐形了 text_number.text = number.toString() // 点击事件 lambda btn_plus.setOnClickListener { text_number.text= (++number).toString() } } }
-
-
解决思路:将数据交由ViewModel进行托管即可
-
MainActivity代码:
package com.derry.viewmodel import android.os.Bundle import android.util.Log import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.ViewModelProvider // 绑定机制 import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { private lateinit var myViewModel: MyViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Log.d("DDD", "onCreate: ") // myViewModel = MyViewModel() 不能直接实例化,因为如果能这样写,系统不可控了 // 旧版本的写法,更新特别快(扩展性不强) // ViewModelProviders.of() // this == ViewModelStoreOwner, myViewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()) .get(MyViewModel::class.java) // findv text_number text_number.text = "${myViewModel.number}"//当横竖屏切换时,没有这一句,数据会消失 // 点击事件 lambda btn_plus.setOnClickListener { text_number.text = "${++myViewModel.number}" } }
-
如何使用组件库对Activity简化
-
activity过去的责任:拆分以后变成管理者,只做绑定的维护;体现出单一职责
- 横竖屏切换
- 逻辑代码
- UI的刷新
- UI初始化
- activity依赖布局控件
-
拆分:
-
ViewModel:
- 逻辑
- 横竖屏切换
-
DataBinding:是可以绑定很多东西
- UI的刷新,DataBinding+LiveData
- UI初始化
- 依赖布局文件
-
-
异常:
-
提示信息:
AndroidStudio报错:Unable to load class 'javax.xml.bind.JAXBException'. This is an unexpected error
-
解决办法:调整项目配置版本
-
-
长按的话还是需要findViewById
-
概述:
-
MVC在后端是比较好的,但是在Android端比较难
-
ViewModel 源码分析:
-
源码示意:
myViewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()) .get(MyViewModel::class.java)-
第一个参数:this,就代表了ViewModeStoreOwner代表了这个借口
-
接口示意图:
- 这个接口会返回ViewModelStore:一个存储类
-
ViewModelStore示例:
- 这是一个存储类;存储了ViewModel
- 通过遍历哈希值实现清空(clear等操作)
-
-
第二个参数:ViewModelProvider.NewInstanceFactory()
-
使用了依赖倒转原则(Factory factory):面向高层,面向抽象
- 很多XXXFactory接口(NewFactory):有无数的子类,但他的父类就是Factory借口,后面我们只需要处理Factory
- Factory:实例化ViewModel
-
为了实现这个:就是去保存ViewModel
-
如何拿到这个ViewModel
-
调用了getViewModelStore
-
进入getViewModelStore()
-
nc 去拿到ViewModelStore对象
横竖屏切换执行流程(Activity重建与AMS通信,这个是重点)
-
概述:横竖屏切换会引起Activity重建
-
Activity重建:由ActivityManagerService(AMS通信机制)通过IBinder进行处理
-
核心:调用Activity.java中的retainNonConfigurationInstances()这个函数;
-
这个是空函数
为什么是空函数: 想让子类完成重写
- Activity的子类是ComponentActivity,在ComponentActivity中也有一个叫retainNonConfigurationInstances()的空函数;
- 那么开始套娃:想调用ComponentActivity的子类MainActivity中的这个函数
- 但是MainActivity中是没有这个函数的--->在MainActivity中重写这个函数
-
在MainActivity中重写这个函数:retainNonConfigurationInstances()并观察结果
override fun onRetainCustomNonConfigurationInstance(): Any? { Log.d("MainActivity", "发生横竖屏切换了 ") return "DDDDDDD" }
-
运行结果:发生很竖屏切换时,会打印出相应的日志信息
-
-
这个函数的内在实现机理:保证MViewModelStore在Activity重建前后是同一份
-
代码展示:
-
执行流程:将上一次的数据拷贝到这一次
-
当发生Activity重建(横竖屏切换等),就会调用这个函数
-
mViewModelStore的保存:
- 本次ViewModelStore为空:将上一次的ViewModelStore 拿过来
- 不为空,保存此时ViewModelStore(放在nc里面);拿给下一次用
-
-
ViewModelStore中装的是什么?
-
ViewModelStore装HashMap,HashMap装ViewModel,ViewModel装想保存的数据
-
代码截图展示:
-
-
-
此函数存在的意义:
- 在ViewModel出现之前,可以去重写这个函数实现临时变量(LoginActivity)的保存;
- ViewModel代替了这个函数的功能,所以为什么这个函数是过时的
-
nc如何进行保存:
- AMS在创建Activity中会把nc拿给新创建的Activity;
- nc 对象这个才是最重要的
-
Activity通信:跨进程
反射代码
-
代码:
myViewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()) .get(MyViewModel::class.java)//反射实现实例化(决定用哪个Factory进行实例化)
实现细节:
-
ViewModel生命周期:这个是重点
- 只有在Activity销毁是才会移除所有的ViewModel(依次移除),此时ViewModel的生命周期才结束
-
重建时机:这里有个拦截
-
代码截图:
-
-
热启动:
- 进程在,这个就在
- 旋转的时候是不会清空ViewModel的