- ViewModel是什么 具有宿主生命周期感知能力的数据存储组件 ViewModel保存的数据在页面因配置变更导致页面销毁重建后依然存在
- ViewModel怎么使用
- ViewModel为什么能在配置变更后保存数据
- ViewModel结合SaveState后做到页面非正常关闭保存数据
- ViewModel在Activity之间共享数据
- ViewModel在Fragment之间共享数据
ViewModel
ViewModel具有宿主生命周期感知能力的数据存储组件 ViewModel保存的数据在页面因配置变更导致页面销毁重建后依然存在
ViewModel怎么使用
class ViewModelActivity : AppCompatActivity() {
val viewModel : TestViewModel by lazy {
ViewModelProvider(this)[TestViewModel::class.java]
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.view_model_activity)
viewModel.testLiveData.observe(this){
Log.d("ViewModel数据", it)
}
viewModel.initData()
}
}
class TestViewModel : ViewModel(){
val testLiveData = MutableLiveData<String>()
fun initData() : LiveData<String>{
//检查LiveData中是否存在数据,存在就复用,不存在重新生成
if(testLiveData.value == null){
//使用随机数,ViewModel保存数据,页面因配置销毁后,展示的数据应该相同
//TODO 业务请求,数据库请求
testLiveData.value = Random().nextFloat().toString()
}
return testLiveData
}
}
ViewModel原理
我们从ViewModelProvider中获取ViewModel实例主要使用到以下的几个方法初始化函数最终都会调用到最后一个构造函数。我们使用的函数都接受一个ViewModelStoreOwner的参数,这是因为我们使用的Activity继承类ViewModelStoreOwner这个接口
当我们使用get获取ViewModel的时候,首先会将传入进来的Class取出全类名并且拼接上前缀,再进入mViewModelStore中寻找ViewModel对应的实例,如果有取出,没有则新建。
这里可以看到ViewModel实例是保存在mViewModelStore中,而mViewModelStore的提供者是ViewModelStoreOwner,所以ViewModel最终的保存操作是在ViewModelStoreOwner中,也就是在Activity中。
public class ViewModelProvider{
public ViewModelProvider(@NonNull ViewModelStoreOwner owner){}
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull androidx.lifecycle.ViewModelProvider.Factory factory) { }
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull androidx.lifecycle.ViewModelProvider.Factory factory) { }
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 androidx.lifecycle.ViewModelProvider.OnRequeryFactory) {
((androidx.lifecycle.ViewModelProvider.OnRequeryFactory) mFactory).onRequery(viewModel);
}
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
if (mFactory instanceof androidx.lifecycle.ViewModelProvider.KeyedFactory) {
viewModel = ((androidx.lifecycle.ViewModelProvider.KeyedFactory) mFactory).create(key, modelClass);
} else {
viewModel = mFactory.create(modelClass);
}
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
}
Actvity中是在哪里保存mViewModelStore的呢,由于方法太多,我只提供方法名称。
AppCompatActivity继承自ComponentActivity,ComponentActivity实现了ViewModelStoreOwner
在onRetainNonConfigurationInstance方法中提供了保存的实现,该方法由Activity中retainNonConfigurationInstances方法调用。Activity中的方法又在ActivityThread中被handleDestroyActivity方法调用
Fragment为何能共享Activity中的ViewModel数据
/**
* D/Fragment-ViewModel数据: 0.19282305
D/ViewModel数据: 0.19282305
旋转后
D/Fragment-ViewModel数据: 0.19282305
D/ViewModel数据: 0.19282305
*/
class ViewModelActivity : AppCompatActivity() {
val viewModel: TestViewModel by lazy {
ViewModelProvider(this)[TestViewModel::class.java]
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.view_model_activity)
viewModel.testLiveData.observe(this) {
Log.d("ViewModel数据", it)
}
viewModel.initData()
val fragment = supportFragmentManager.findFragmentById(R.id.testFragment)
if(fragment == null){
supportFragmentManager.beginTransaction()
.add(R.id.testFragment, TestFragment.getInstance()).commitNowAllowingStateLoss()
}
}
}
class TestViewModel : ViewModel() {
val testLiveData = MutableLiveData<String>()
fun initData(): LiveData<String> {
//检查LiveData中是否存在数据,存在就复用,不存在重新生成
if (testLiveData.value == null) {
//使用随机数,ViewModel保存数据,页面因配置销毁后,展示的数据应该相同
//TODO 业务请求,数据库请求
testLiveData.value = Random().nextFloat().toString()
}
return testLiveData
}
}
class TestFragment : Fragment() {
companion object {
fun getInstance(): Fragment {
return TestFragment()
}
}
val fragmentViewModel: TestViewModel by lazy {
ViewModelProvider(requireActivity())[TestViewModel::class.java]
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
fragmentViewModel.testLiveData.observe(viewLifecycleOwner, {
Log.d("Fragment-ViewModel数据", it)
})
return inflater.inflate(R.layout.view_model_activity, container, false)
}
}
从上面的代码中可以看到,如果我们的Activity中持有Fragment,且Fragment中获取ViewModel的方式为
ViewModelProvider(requireActivity())[TestViewModel::class.java]
则Fragment中的ViewModel实例和Activity中是同一个实例。
上面介绍过ViewModel的保存者是mViewModelStore,而mViewModelStore的提供者是ViewModelStoreOwner,而在Fragment中我们使用的是Fragment中保存的Activity,所以ViewModelStoreOwner是同一个对象,所以ViewModel是同一个对象。