LiveData是什么以及有什么特点
LiveData 之所以能够感知到组件的生命周期 是因为 在LiveData里面给组件中注册了一个生命周期的观察者
![](https://img.haomeiwen.com/i6052465/2848db9e635027ad.png)
LiveData的使用方式
- 数据持有使用
- 跨组件通信
实时数据LiveData
LiveData是一个可被观察的数据容器类。它将数据包装起来,使数据成为被观察者,当该数据发生变化时,观察者就能获得通知,我们不需要自己去实现观察者模式
LiveData和ViewModel的关系
ViewModel用于存放页面所需的各种数据,我们还可以在其中存放一些与数据相关的业务逻辑,因此ViewModel中的数据可能会随着业务的变化而变化。
对于页面来说,页面并不需要关心ViewModel中的业务逻辑,它只需要关心展示什么数据,并且希望在数据发生变化时,得到通知并更新页面。
- LiveData的作用:在ViewModel中的数据发生变化时通知页面,所以LiveData通常放在ViewModel中使用,用于包装ViewModel中需要被外界观察的数据。
LiveData的基本使用方法
我们在上一个案例的计时器基础上使用LiveData对接口进行改写。
LiveData是一个抽象类,不能直接使用,我们通常使用它的直接子类MutableLiveData
public class TimerWithLiveDataViewModel extends ViewModel {
//计时器中的秒是希望被外界观察的 所以我们使用LiveData进行包装
private MutableLiveData<Integer> currentSecond;
public LiveData<Integer> getCurrentSecond(){
if (currentSecond == null) {
currentSecond = new MutableLiveData<>();
}
return currentSecond;
}
}
- 利用LiveData完成页面与ViewModel的通信
private void initComponent() {
TextView timeTv = (TextView) findViewById(R.id.tv_second);
//创建viewModel
TimerWithLiveDataViewModel timerWithLiveDataViewModel = new ViewModelProvider(this).get(TimerWithLiveDataViewModel.class);
//得到ViewModel中的LiveData
LiveData<Integer> currentSecond = timerWithLiveDataViewModel.getCurrentSecond();
//通过Observe观察LiveData的变化
currentSecond.observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer second) {
Log.d(TAG, "onChanged: "+second);
//变化后更新UI
timeTv.setText("TIME:" + second);
}
});
LiveData的postValue()和setValue()
- postValue()和setValue()都是希望修改LiveData所包装的数据使用
- postValue()用在非UI线程中、setValue()用在UI线程中
LiveData的原理
我们通过LiveData.observe()方法来看一下LiveData的原理,在observe方法中接收两个参数,一个是LifecycleOwner对象,一般是Activity,在方法的最后一行将Observer与Activity的生命周期关联在一起,所以LiveData能够感知页面的生命周期。它可以检测页面当前的状态是否为激活状态或者页面是否被销毁。只有页面处于激活状态时,页面才能收到来自LiveData的通知,若页面被销毁那么LiveData会自动清除与页面的 关联,从而避免可能引发的内存泄漏
LiveData.observerForever()方法
LiveData还提供了一个名为observerForever()的方法,使用起来与observe()没有太大差别。它们的区别主要在于,当LiveData所包装的数据发生变化时,无论页面处于什么状态,observerForever()都能收到通知。使用这个方法要注意记得调用removeObserver()来停止对LiveData的观察,否则LiveData一直处于激活状态,导致Activity不会被系统自动回收,这就造成了内存泄漏
ViewModel+LiveData实现Fragment间的通信
我们可以基于ViewModel和Fragment组件的特性,可以利用LiveData实现同一个Activity中不同的Fragment间的通信
- 创建viewModel使用LiveData包装Progress
public class ShareDataViewModel extends ViewModel {
private MutableLiveData<Integer> progress;
public LiveData<Integer> getProgress() {
if (progress == null) {
progress = new MutableLiveData<>();
progress.setValue(0);
}
return progress;
}
@Override
protected void onCleared() {
super.onCleared();
progress = null;
}
}
- fragment布局文件 两个fragment布局相同
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_above="@+id/seekBar"
android:text="Fragment_One"/>
<SeekBar
android:id="@+id/seekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="100"
android:layout_centerInParent="true"
android:layout_marginTop="20dp"/>
</RelativeLayout>
- Fragment代码 两个Fragment代码类似
public class FragmentOne extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater,ViewGroup container, Bundle savedInstanceState) {
View parentView = inflater.inflate(R.layout.fragment_one, container, false);
SeekBar seekBar = (SeekBar) parentView.findViewById(R.id.seekBar);
//获取viewModel
ShareDataViewModel shareDataViewModel = new ViewModelProvider(getActivity()).get(ShareDataViewModel.class);
//获取viewModel中包装的seekbar
MutableLiveData<Integer> progressLiveData = (MutableLiveData<Integer>) shareDataViewModel.getProgress();
//通过观察LiveData的变化改变UI
progressLiveData.observe(getViewLifecycleOwner(), new Observer<Integer>() {
@Override
public void onChanged(Integer progress) {
seekBar.setProgress(progress);
}
});
//监听seekbar变化
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
//用户操作seekbar更新liveData数据
progressLiveData.setValue(progress);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
return parentView;
}
}
- activity布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".fragmentcommunication.FragmentComminiActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragmentone"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/teal_200"
/>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragmenttwo"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
/>
</LinearLayout>
- Activity代码
public class FragmentComminiActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment_commini);
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction transaction = fm.beginTransaction();
transaction.add(R.id.fragmentone, new FragmentOne());
transaction.add(R.id.fragmenttwo, new FragmentTwo());
transaction.commit();
}
}
运行后,可以看到两个Fragment互相通信通过viewModel+liveData,并且旋转屏幕使得SeekBar进度保持一致
![](https://img.haomeiwen.com/i6052465/71541b572a3d7a85.png)
整理与总结
总的来说就是,ViewModel将页面与数据进行解耦,ViewModel中存放页面中的数据,当ViewModel中的数据发生变化时我们希望通知页面进行页面更新。在没有LiveData之前,我们通过接口回调的方式来完成这个需求,有了LiveData之后,我们可以更简单、优雅的处理数据:
使用LiveData对ViewModel中的数据进行包装,并在页面中监听LiveData的数据变化。当数据变化时收到通知进而更新UI
网友评论