ViewModel是干什么的?
在页面(活动/碎片)中,在很简单的情况在我们会将UI交互,数据的获得与处理等相关的逻辑,全都放在一个页面中,但是如果我们要处理的业务很复杂,这样做会显得代码冗杂且不利于解读,这样不符合"单一责任"原则.
所以页面就应该只是负责接收用户的交互以及数据的展示,其他逻辑应该放在另外一个东西上面,为此,Android为我们提供了ViewModel类,专门用来存放应用程序页面所需要的数据.它将页面中所需要的数据从页面中剥离出来.
另外,如果我们的应用程序支持横竖屏切换,当用户旋转手机屏幕时,我们还需要考虑数据的存储与恢复。如果数据不进行存储,那么通常我们还需要重新去获取一次。
而ViewModel能为我们解决这个问题,它独立于配置变化。也就是说,屏幕旋转导致的Activity重建,并不会影响到ViewModel的生命周期
LiveData是干什么的?
-
首先LiveData是一种可观察监听的数据存储类,当生命周期发生,数据有更新时,observer可感知监听到.
-
其次LiveData其实也是一个观察者模式的数据实体类,它可以跟它注册的观察者回调数据是否已经更新.
-
LiveData还能知晓它绑定的Activity或者Fragment的生命周期,它只会给前台活动的activity回调(这个很厉害).这样你可以放心的在它的回调方法里直接将数据添加到View,而不用担心会不会报错.(你也可以不用费心费力判断Fragment是否还存活)
LiveData和ViewModel实现跨页面通信
官方建议LiveData和ViewModel一起使用,因为 ViewModel 支持共享作用域,并且官方文档都推荐了通过 共享 ViewModel 来实现跨页面通信的需求:
在这里我们使用LiveData和ViewModel实现一个小demo,ViewModel里面创建一个Timer计时器,每隔一秒,通过LiveData来更新主界面的UI,这种方法在只用之后我们可以清楚的看到,如果手机从横屏切换到竖屏,或者竖屏切换到横屏,它ViewModel里面Timer计时器,都不会重新开始计时.
1. 首先创建一个TimerViewModel类继承ViewModel
LiveData是一个数据的包装。具体的包装对象可以是任何数据,包括集合。它是一个抽象类,首先先创建一个类实现LiveData
public class TimerViewModel extends ViewModel
{
private Timer timer;
private int currentSecond;
/**
* LiveData是抽象类,MutableLiveData是具体实现类
*/
private MutableLiveData<String> content;
public MutableLiveData<String> getContent(){
if(content == null){
content = new MutableLiveData<>();
}
return content;
}
/**
* 开始计时
* */
public void startTiming()
{
if (timer == null)
{
currentSecond = 0;
timer = new Timer();
TimerTask timerTask = new TimerTask()
{
@Override
public void run()
{
currentSecond++;
content.postValue(String.valueOf(currentSecond));
}
};
timer.schedule(timerTask, 2000, 1000);//延迟2秒执行
}
}
/**
* 由于屏幕旋转导致的Activity重建,该方法不会被调用
*
* 只有ViewModel已经没有任何Activity与之有关联,系统则会调用该方法,你可以在此清理资源
* */
@Override
protected void onCleared()
{
super.onCleared();
timer.cancel();
}
}
ViewModel是一个抽象类,其中只有一个方法onCleared(),当ViewModel不再被需要的时候,也就是与之相关的Activity都被销毁时,该方法会被系统调用,我们可以在这个方法里面执行一些资源释放的操作,以免内存泄漏。
既然ViewModel的销毁是由系统来判断和执行的,那么系统是如何判断的呢?是根据Context引用。因此,我们在使用ViewModel的时候,千万不能从外面传入Activity,Fragment或者View之类的含有Context引用的东西,否则系统会认为该ViewModel还在使用中,从而无法被系统销毁回收,导致内存泄漏的发生。
2. 在Activity或是fragment进行监听观察
public class MainActivity extends AppCompatActivity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iniComponent();
}
private void iniComponent()
{
TextView tvTime = findViewById(R.id.tvTime);
//构建了ViewModel对象
TimerViewModel timerViewModel = new ViewModelProvider(this).get(TimerViewModel.class);
//监听了content属性变化,只要触发了setValue/postValue方法就会走
timerViewModel.getContent().observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
tvTime.setText("TIME: " + s);
}
});
timerViewModel.startTiming();
}
}
首先创建一个 MutableLiveData(LiveData是抽象类)对象 ,通过 observe 方法可以订阅修改数据的通知,通过 postValue()或者 setValue() 方法发送事件更新数据,已经订阅的 Observer 能够得到数据更改的通知,就会回调 onChanged() 方法。
// 在UI线程中调用该方法通知数据变更
liveData.setValue(object);
// 在子线程中调用该方法通知数据变更,该方法中切换到UI线程后调用setValue方法
liveData.postValue(object);
总结
我们前面提到过,使用ViewModel的时候,不能将任何含有Context引用的对象传入ViewModel,因为这可能会导致内存泄露。但如果你希望在ViewModel中使用Context怎么办呢?我们可以使用AndroidViewModel类,它继承自ViewModel,并且接收Application作为Context,既然是Application作为Context,也就意味着,我们能够明确它的生命周期和Application是一样的,这就不算是一个内存泄露了。文章来源:https://www.toymoban.com/news/detail-461209.html
使用ViewModel,不仅将界面和数据从代码上进行了分离,而且不再需要关心屏幕旋转带来的数据的丢失和获取问题。也许你会说onSaveInstanceState() 方法同样可以解决屏幕旋转带来的数据丢失问题,但它只能保存少量的能支持序列化的数据,而ViewModel没有这个限制,它能支持页面中所有的数据。但要注意的是,ViewModel不支持数据的持久化,当界面彻底销毁,ViewModel及其数据也就不存在了。文章来源地址https://www.toymoban.com/news/detail-461209.html
到了这里,关于ViewModel和LiveData的基本使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!