文章目录
一、LiveData简介
LiveData(实时数据)是一个可被观察的数据容器类,,可以将LiveData理解为一个数据的容器,它将数据包装起来,使数据成为观察者。当数据发生变化时,观察者能够获得通知,LiveData内部已经实现了观察者模式,可以使用LiveData完成ViewModel与页面之间的通信。
二、LiveData与ViewModel关系
ViewModel用于存放页面所需要的各种数据。,我们还可以在其中放一些与数据相关的业务逻辑。例如我们可以在ViewModel中进行数据的获取,加工,解析等。因此,ViewModel中的数据会随着业务的变化而变化。
对于页面来说,页面并不需要关心ViewModel中的业务逻辑,它只关心自己需要展示的数据。并且希望在数据发生变化时通知页面。LiveData的作用就是,在ViewModel中的数据发生变化时通知页面。,因此LiveData通常被放在ViewModel中使用。用于包装ViewModel中那些需要被外界观察的数据。
三、LiveData的使用
3.1、LiveData是一个抽象类,不能直接使用,通常我们使用它的子类MutableLiveData
public class TimerVideModel extends ViewModel {
private MutableLiveData<Integer> currentSecond;
private Timer mTimer;
public void startTiming(){
if(mTimer == null){
currentSecond.setValue(0);
mTimer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
//这里要用postValue方法,而不能用setValue方法,否则会报线程异常错误
currentSecond.postValue(currentSecond.getValue()+1);
}
};
mTimer.schedule(task,1000,1000);
}
}
public LiveData<Integer> getCurrentSecond(){
if(currentSecond == null){
currentSecond = new MutableLiveData<>();
}
return currentSecond;
}
@Override
protected void onCleared() {
super.onCleared();
mTimer.cancel();
}
}
3.2、完成与ViewModel之间的通信
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initComponent();
}
private void initComponent() {
//通过ViewModelProvider 获取到ViewModel
TimerVideModel timerVideModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(TimerVideModel.class);
//得到ViewModel中的LiveData
MutableLiveData<Integer> liveData = (MutableLiveData<Integer>) timerVideModel.getCurrentSecond();
//通过 liveData.observe观察ViewModel中的数据变化
liveData.observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
((TextView) findViewById(R.id.time)).setText("Time:" + integer);
}
});
findViewById(R.id.reset).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//通过LiveData.setValue/postValue()完成对ViewModel中数据的修改
liveData.setValue(0);
}
});
timerVideModel.startTiming();
}
}
在代码中我们通过liveData.observe()方法对LiveData所包装的数据进行观察。反过来,我们希望修改LiveData所包装的数据时,也可以通过LiveData.postValue()/LiveData().setValue()方法完成。LiveData.postValue()是用在非UI线程中,若在UI线程中用LiveData().setValue()方法。
四、LiveData的原理
LiveData通过 liveData.observe()观察ViewModel中的数据变化。
observe()方法接受两个参数,第一个是LifeCycleOwner对象,这里是Activity。第二个参数是Observe对象。 * owner.getLifecycle().addObserver(wrapper);*将Observe与Activity的生命周期关联在一起。因此LiveData能够感知Activity的生命周期。它可以检测到页面当前的状态是否是激活状态,或者页面是否被销毁,只有页面被处于激活状态(LifeCycle.State.ON_START或者LifeCycle.State.ON_RESUME)时,页面才会收到LiveData的通知,若页面被销毁(LifeCycle.State.ON_DESTORY)会自动清除与页面的关联。从而避免可能引发内存泄漏问题。
五、LiveData.ObserveForever()方法
LiveData还提供了一个ObserveForever()的方法,使用和Observe()一样,区别在于,当LiveData所包装的数据发生变化时,无论页面处于什么状态,ObserveForever()都能收到通知,因此,用完之后,一定需要调用removeObserver()方法来停止对LiveData的观察,否则LiveData会一直处于激活状态,Activity则不会被系统自动回收,造成内存泄漏
六、ViewModel+LiveData实现Fragment间通信
6.1、实现效果
在MainActivity中有两个Fragment(FragmentOne和FragmentTwo),两个Fragment中都有一个seeker,拖拽任意的seekbar,另外一个Fragment中的seekbar也改变progress。
6.2、编写MainActivity中的XML
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
tools:context=".MainActivity">
<TextView
android:id="@+id/time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="20dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="0" />
<TextView
android:id="@+id/reset"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="重置"
android:textSize="20dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/time" />
<fragment
android:id="@+id/fragmentOne"
android:name="com.example.livedata.OneFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintHeight_default="percent"
app:layout_constraintHeight_percent="0.5"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/reset" />
<View
android:id="@+id/divideLine"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#000000"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/fragmentOne" />
<fragment
android:id="@+id/fragmentTwo"
android:name="com.example.livedata.TwoFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintHeight_default="percent"
app:layout_constraintHeight_percent="0.5"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/divideLine" />
</androidx.constraintlayout.widget.ConstraintLayout>
6.3、定义ViewModel 和LiveData
public class ShareDataViewModel extends ViewModel {
private MutableLiveData<Integer> progress;
public LiveData<Integer> getProgress(){
if(progress == null){
progress = new MutableLiveData<>();
}
return progress;
}
@Override
protected void onCleared() {
super.onCleared();
progress = null;
}
}
6.4、FragmentOne中逻辑代码
public class OneFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View parentView = inflater.inflate(R.layout.fragment_one, container, false);
final SeekBar seekBar = parentView.findViewById(R.id.seekbar);
final TextView seekProgressTv = parentView.findViewById(R.id.tvSeekValue);
//注意:这里ViewModelProviders.of(getActivity())这里的参数需要是Activity,而不能是Fragment,否则收不到监听
final ShareDataViewModel shareDataViewModel = new ViewModelProvider(getActivity(), new ViewModelProvider.NewInstanceFactory()).get(ShareDataViewModel.class);
final MutableLiveData<Integer> liveData = (MutableLiveData<Integer>) shareDataViewModel.getProgress();
//通过observe方法观察ViewModel中字段数据的变化,并在变化时,得到通知
liveData.observe(this, new Observer<Integer>() {
@Override
public void onChanged(@Nullable Integer progress) {
seekBar.setProgress(progress);
seekProgressTv.setText("Progress:"+ progress);
}
});
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
//用户操作SeekBar时,更新ViewModel中的数据
liveData.setValue(progress);
seekProgressTv.setText("Progress:"+ progress);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
return parentView;
}
}
6.5、FragmentTwo中的代码
public class TwoFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View parentView = inflater.inflate(R.layout.fragment_t_w_o, container, false);
final SeekBar seekBar = parentView.findViewById(R.id.seekbar);
final TextView seekProgressTv = parentView.findViewById(R.id.tvSeekValue);
//注意:这里ViewModelProviders.of(getActivity())这里的参数需要是Activity,而不能是Fragment,否则收不到监听
final ShareDataViewModel shareDataViewModel = new ViewModelProvider(getActivity(), new ViewModelProvider.NewInstanceFactory()).get(ShareDataViewModel.class);
final MutableLiveData<Integer> liveData = (MutableLiveData<Integer>) shareDataViewModel.getProgress();
//通过observe方法观察ViewModel中字段数据的变化,并在变化时,得到通知
liveData.observe(this, new Observer<Integer>() {
@Override
public void onChanged(@Nullable Integer progress) {
seekBar.setProgress(progress);
seekProgressTv.setText("progress:" + progress);
}
});
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
//用户操作SeekBar时,更新ViewModel中的数据
liveData.setValue(progress);
seekProgressTv.setText("progress:" + progress);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
return parentView;
}
}