三个问题,重点原理分析:
- ViewModel是如何创建出来的?
- 为什么不同的Fragment使用相同的Activity对象来获取ViewModel,可以轻易的实现ViewModel共享?
- ViewModel为什么在Activity销毁重建时不会被销毁回收?
答案:
这里我们又学到了Activity的两个跟生命周期相关的函数调用:onRetainNonConfigurationInstance和getLastNonConfigurationInstance。
- Activity实现了ViewModelStoreOwner接口,创建了ViewModelStore对象。
- 当Activity意外销毁时,onRetainNonConfigurationInstance函数被回调,在此函数中对ViewModelStore对象进行了保存。
- 当Activity重建时,onCreate方法中会先获取getLastNonConfigurationInstance,如果其中的ViewModelStore对象不为空,就直接引用,不再重新创建ViewModelStore对象了。
横竖平:能够保存数据的原理:ViewModelStore,里面用的hashmap
修改系统语言会发生什么?
进程会被杀死
ViewModel横竖屏切换的时候,数据还保留着。不用onSaveInstance方法了
ViewModel有下面的两个优点:
- Activity进行重建的时候,ViewModel的数据不会被回收调用。这时候我们就可以不用通过onSaveInstanceState()方法来进行数据的存储了。而且用onSaveInstanceState()方法为了使Activity能够尽快的重建还只能存储少量的数据进行恢复。
- Activity中通常会有有那种在其创建的时候获取数据,然后在其销毁的时候释放数据的方法。如果这些放在Activity中的话,在Activity进行重建的时候,会很浪费资源。但是如果方法在ViewModel中的话,Activity的重建将不会导致数据的重复获取。
Fragment间共享数据
在上面ViewMoel的生命周期也提到了 ViewModel只会在Activity存活时,只会创建一次,因此
在同一个Activity中可以在多个Fragment中共享ViewModel中数据。
ViewModel主要是用来存放数据的,
Android中的ViewModel是一个可以用来存储UI相关的数据的类。ViewModel的生命周期会比创建它的Activity、Fragment的生命周期长
Demo实战:
上图中,是以平常的方式实现的计数器,当我们旋转屏幕而没有其他处理的时候,计数器的数据丢失了。这是因为屏幕旋转时,我们的
activity
被销毁重建了,而存放在activity
的数据自然也是没有了。
MyViewModel viewModel;
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewModel= ViewModelProviders.of(this).get(MyViewModel.class);
textView=findViewById(R.id.tv_show);
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
viewModel.num++;
textView.setText(String.valueOf(viewModel.num));
}
});
textView.setText(String.valueOf(viewModel.num));
}
}
if(number==null){
number=new MutableLiveData<>();
number.setValue(0);
}
使用:
ViewModel不是new出来的
<span style="color:#000000"><span style="color:#cccccc"><code class="language-kotlin"> <span style="color:#cc99cd">val</span> model <span style="color:#67cdcc">=</span> ViewModelProviders.<span style="color:#f08d49">of</span>(activity<span style="color:#67cdcc">!!</span>).<span style="color:#f08d49">get</span>(SharedViewModel<span style="color:#67cdcc">::</span><span style="color:#cc99cd">class</span>.java)</code></span></span>
<span style="color:#000000"><span style="color:#cccccc">
<span style="color:#404040"><code class="language-java"><span style="color:#f8c555">MutableLiveData</span></code></span></span></span>
Databing绑定ViewModel
activityMainBinding=DataBindingUtil.setContentView(this,R.layout.activity_main);
viewModel=ViewModelProviders.of(this,new SavedStateViewModelFactory(this)).get(MyAndroidViewModel.class);
activityMainBinding.setData(viewModel);//databing绑定ViewModel
activityMainBinding.setLifecycleOwner(this);//基类实现了这个监听
AndroidViewModel:防止内存泄漏。
使用ViewModel的时候,需要注意的是ViewModel不能够持有View、Lifecycle、Acitivity引用,而且不能够包含任何包含前面内容的类。因为这样很有可能会造成内存泄漏。
这张图也解释了为什么
ViewModel
中不能持有Activity
、Fragment
、view
的引用。因为Activity
在重建后是一个新的对象,如果ViewModel
中持有旧对象的引用,这个旧对象可能就等不到释放,造成泄漏。
*** 需要注意的是
如果确实需要,应该使用
ViewModel
类中不应该持有Activity
、Fragment
、view
的引用,具体原因后面会讲解释***如果确实需要,应该使用
applicationcontext
,或者使用含有上下文的AndroidViewModel
。
产生的原因:数据永久保存,shareperfece用的话要context.所以产生了AndroidViewModel
那如果需要使用Context对象改怎么办。这时候我们可以给ViewModel一个Application。Application是一个Context,而且一个应用也只会有Application。
我们自己添加Application?其实没必要Google还有一个AndroidViewModel。这是一个包含Application的ViewModel。
下面是一个AndroidViewModel的源码:
下面是一个AndroidViewModel的源码:
<span style="color:#000000"><span style="color:#cccccc"><span style="color:#404040"><code class="language-kotlin"><span style="color:#cc99cd">public</span> <span style="color:#cc99cd">class</span> AndroidViewModel extends ViewModel {
<span style="color:#cc99cd">@SuppressLint</span>(<span style="color:#7ec699">"StaticFieldLeak"</span>)
<span style="color:#cc99cd">private</span> Application mApplication;
<span style="color:#cc99cd">public</span> <span style="color:#f08d49">AndroidViewModel</span>(<span style="color:#cc99cd">@NonNull</span> Application application) {
mApplication <span style="color:#67cdcc">=</span> application;
}
<span style="color:#999999">/**
* Return the application.
*/</span>
<span style="color:#cc99cd">@SuppressWarnings</span>(<span style="color:#7ec699">"TypeParameterUnusedInFormals"</span>)
<span style="color:#cc99cd">@NonNull</span>
<span style="color:#cc99cd">public</span> <span style="color:#67cdcc"><</span>T extends Application<span style="color:#67cdcc">></span> T <span style="color:#f08d49">getApplication</span>() {
<span style="color:#999999">//noinspection unchecked</span>
<span style="color:#cc99cd">return</span> (T) mApplication;
}
}</code>
</span></span></span>
ViewModelSavedState(进程在后台被杀死数据也能存活)
需要添加这个依赖:
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0-rc02'
实现:通过private SavedStateHandle handle;
package com.example.viewmodelsavestate;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.SavedStateHandle;
public class MyViewModel extends ViewModel {
private SavedStateHandle handle;
public MyViewModel(SavedStateHandle handle){
if(!handle.contains("NUMBER")){
handle.set("NUMBER",0);
}
this.handle = handle;
}
public LiveData<Integer>getnumber(){
return handle.getLiveData("NUMBER");
}
public void add(){
handle.set("NUMBER",(int)handle.get("NUMBER")+1);
}
}
public class MainActivity extends AppCompatActivity {
ActivityMainBinding binding;
MyViewModel myViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
myViewModel = new ViewModelProvider(this,new SavedStateViewModelFactory(getApplication(),this)).get(MyViewModel.class);
binding.setData(myViewModel);
binding.setLifecycleOwner(this)
实战练习:
public class MyAndroidViewModel extends AndroidViewModel {
SavedStateHandle saveStateHanle
public MyAndroidViewModel(@NonNull Application application, MutableLiveData<Integer> number, SavedStateHandle saveStateHanle) {
super(application);
this.saveStateHanle=saveStateHanle;
}
public static final String KEY="dddsfdsafsfds";
/**
* 不能为私有
* @return
*/
public MutableLiveData<Integer> getNumber() {
if(!saveStateHanle.contains(KEY)){
saveStateHanle.set(KEY,0);
}
return saveStateHanle.getLiveData(KEY);
}
public void add() {
getNumber().setValue(getNumber().getValue()+1);
}