1、概述
LiveData
是一种可观察的数据存储器类,LiveData
使用观察者模式,每当数据发生变化时,LiveData
会通知 Observer
对象,我们可以在这些 Observer
对象中更新UI
ViewModel
对象为特定的界面组件(如 Fragment 或 Activity)提供数据,并包含数据处理业务逻辑,会配合LiveData
一起使用
接下来,我们会先介绍如果使用LiveData,并编写一个LiveData Demo,接着再结合ViewModel对LiveData Demo的代码进行重构
2、LiveData使用说明
LiveData<T>
是一个抽象类,它有2个子类分别是:MutableLiveData<T>
和MediatorLiveData<T>
,在编写代码时,是创建的子类。我们先来看MutableLiveData<T>
使用方法,在后面的示例中再介绍如何使用MediatorLiveData<T>
2.1、创建LiveData对象
如果我们要观察的对象类为String
,就通过如下代码创建一个MutableLiveData
对象
MutableLiveData<String> liveData = new MutableLiveData<>();
2.2、观察LiveData对象
通过observe
方法来监听LiveData
的数据变化,每当LiveData
发生变化时,都会回调onChanged
方法,并返回值的内容String s
liveData.observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.i(TAG, "onChanged: " + s);
}
});
2.3、更新LiveData对象
通过LiveData
的setValue
方法,给LiveData
赋值,每次赋完值后,都会回调onChanged
方法
liveData.setValue("add for test");
当给LiveData
赋值为"add for test"
时,onChanged
会输出日志:
2021-03-20 21:59:21.483 26430-26430/com.example.livedatademo I/LiveDataDemo: onChanged: add for test
3、编写LiveData Demo
我们创建一个Demo,主界面只有一个TextView
,TextView
用作展示一个数字,这个数字会从59开始显示,然后每隔1秒数字会减1,直到数字变为0。
接下来,我们看下不使用LiveData
和使用LiveData
来实现这个Demo的区别
3.1、不使用LiveData
代码说明如下:
1、在MainActivity
增加一个countDown
的方法,通过CountDownTimer
创建一个倒计时器
2、new CountDownTimer(1 * 60 * 1000, 1 * 1000)
中2个参数的含义:第一个参数表示这个Timer的总时长
为60秒;第2个参数表示每隔1秒
中会更新一次,并回调onTick
方法
3、每次调用onTick
方法,会调用textView.setText(String.valueOf(l / 1000));
设置一次TextView
的内容对应代码如下:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "LiveDataDemo";
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
countDown();
}
private void countDown() {
new CountDownTimer(1 * 60 * 1000, 1 * 1000) {
@Override
public void onTick(long l) {
Log.i(TAG, "onTick: " + l);
textView.setText(String.valueOf(l / 1000));
}
@Override
public void onFinish() {
}
}.start();
}
}
- 弊端:
业务逻辑(倒计时的功能)
和UI逻辑(TextView更新)
没有分开,耦合到一起了
3.2、使用MutableLiveData
为了方便演示,我们直接在MainActivity中操作LiveData,实际项目中不要这样编写代码,应该把LiveData放到ViewModel中
代码说明如下:
1、在MainActivity
中增加一个long
类型的MutableLiveData
2、每次回调onTick
方法时,调用liveData.setValue(l);
,把最新的值设置给LiveData
3、调用LivewData
的observe
方法,设置一个监听器,每当LiveData
的值变化时,会回调onChanged
方法,在onChanged
中设置TextView
的内容对应代码如下:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "LiveDataDemo";
private final MutableLiveData<Long> liveData = new MutableLiveData<>();
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
// TODO:为了方便演示,我们直接在MainActivity中操作LiveData,实际项目中不要这样编写代码,应该把LiveData放到ViewModel中
liveData.observe(this, new Observer<Long>() {
@Override
public void onChanged(Long aLong) {
textView.setText(String.valueOf(aLong / 1000));
}
});
countDown();
}
public void countDown() {
new CountDownTimer(1 * 60 * 1000, 1 * 1000) {
@Override
public void onTick(long l) {
Log.i(TAG, "onTick: " + l);
// TODO:为了方便演示,我们直接在MainActivity中操作LiveData,实际项目中不要这样编写代码,应该把LiveData放到ViewModel中
liveData.setValue(l);
}
@Override
public void onFinish() {
}
}.start();
}
}
3.3、使用MediatorLiveData
MediatorLiveData
可以合并多个LiveData
源,只要任何原始的LiveData
源对象发生更改,就会触发MediatorLiveData
对象的观察者。
例如,如果界面中有可以从本地数据库或网络更新的 LiveData
对象,则可以向MediatorLiveData
对象添加以下源:
- 与存储在数据库中的数据关联的
LiveData
对象。 - 与从网络访问的数据关联的
LiveData
对象。
Activity
只需观察MediatorLiveData
对象即可从这两个源接收更新。接下来,我们会实现这个例子。
3.3.1、监听2个数据源的变化
-
代码说明如下:
1、创建1个MediatorLiveData<String>
对象final MediatorLiveData<String> liveDataMerger = new MediatorLiveData<>();
2、创建2个
MutableLiveData
:liveData1表示网络数据源,liveData2表示本地数据源private MutableLiveData<String> liveData1 = new MutableLiveData<>(); private MutableLiveData<String> liveData2 = new MutableLiveData<>();
3、调用
MediatorLiveData
的addSource
方法,分别将liveData1
和liveData2
加到MediatorLiveData
要监听的数据源中liveDataMerger.addSource(liveData1, new Observer<String>() { @Override public void onChanged(String s) { Log.i(TAG, "addSource1 onChanged: " + s); liveDataMerger.setValue(s); } }); liveDataMerger.addSource(liveData2, new Observer<String>() { @Override public void onChanged(String s) { Log.i(TAG, "addSource2 onChanged: " + s); liveDataMerger.setValue(s); } });
4、设置
MediatorLiveData
的监听,当2个数据源发生变化时,会回调onChanged
方法,并将变化的内容展示到TextView
上liveDataMerger.observe(this, new Observer<String>() { @Override public void onChanged(String s) { Log.i(TAG, "liveDataMerger onChanged: " + s); textView.setText(s); }
});
3.3.2、 编写模拟2个数据源更新的代码
编写一个mergeTes
的方法,里面创建了2个Timer
,用作模拟网络数据更新和本地数据更新的情况,每隔3
秒会往网络数据liveData1
设置值、每隔10
秒会往本地数据liveData2
设置值。对应代码如下:
public void mergeTest() {
new CountDownTimer(1 * 60 * 1000, 3 * 1000) {
@Override
public void onTick(long l) {
liveData1.setValue("网络有数据更新了" + l/1000);
}
@Override
public void onFinish() {
}
}.start();
new CountDownTimer(1 * 60 * 1000, 10 * 1000) {
@Override
public void onTick(long l) {
liveData2.setValue("本地数据库更新了" + l/1000);
}
@Override
public void onFinish() {
}
}.start();
}
3.3.4、MediatorLiveData运行效果
每隔3秒,TextView
更新为如下内容:
每隔10秒,TextView
更新为如下内容:
4、ViewModel使用
4.1 创建ViewModel类
创建一个MyViewModel类,继承自ViewModel
import androidx.lifecycle.ViewModel;
public class MyViewMode extends ViewModel {
}
4.2 在Activity中使用
public class MainActivity extends AppCompatActivity {
private MyViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewModel = new ViewModelProvider(this).get(MyViewModel.class);
}
}
如果遇到new ViewModelProvider(this)
报错,就在app/build.gradle中加入依赖implementation "androidx.lifecycle:lifecycle-viewmodel:2.3.0"
5、结合ViewModel,重构LiveData Demo
5.1、在ViewModel中增加LiveData
在MyViewModel中增加3个MutableLiveData,分别对应在MainActivity中使用到的。并编写这3个LiveData的get方法
public class MyViewModel extends ViewModel {
private MutableLiveData<Long> liveData = new MutableLiveData<>();
private MutableLiveData<String> liveData1 = new MutableLiveData<>();
private MutableLiveData<String> liveData2 = new MutableLiveData<>();
public MutableLiveData<Long> getLiveData() {
return liveData;
}
public MutableLiveData<String> getLiveData1() {
return liveData1;
}
public MutableLiveData<String> getLiveData2() {
return liveData2;
}
}
5.2、在ViewModel中添加操作数据的逻辑
countDown、mergeTest这2个方法在操作数据,从MainActivity中把这2个方法copy到 MyViewModel中
public class MyViewModel extends ViewModel {
private static final String TAG = "MyViewMode";
... ...
public void countDown() {
new CountDownTimer(1 * 60 * 1000, 1 * 1000) {
@Override
public void onTick(long l) {
Log.i(TAG, "onTick: " + l);
liveData.postValue(l);
}
@Override
public void onFinish() {
}
}.start();
}
public void mergeTest() {
new CountDownTimer(1 * 60 * 1000, 3 * 1000) {
@Override
public void onTick(long l) {
liveData1.postValue("网络有数据更新了" + l / 1000);
}
@Override
public void onFinish() {
}
}.start();
new CountDownTimer(1 * 60 * 1000, 10 * 1000) {
@Override
public void onTick(long l) {
liveData2.postValue("本地数据库更新了" + l / 1000);
}
@Override
public void onFinish() {
}
}.start();
}
}
5.3 修改MainActivity中和liveData有关的代码
1、删除countDown,改用viewModel.countDown();
viewModel = new ViewModelProvider(this).get(MyViewModel.class);
viewModel.countDown();
2、用viewModel.getLiveData().observe替换liveData.observe
viewModel.getLiveData().observe(this, new Observer<Long>() {
@Override
public void onChanged(Long aLong) {
textView.setText(String.valueOf(aLong/1000));
}
});
3、删除liveData相关代码
经过上述修改后,liveData相关的重构已完成,MainActivity代码如下:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "LiveDataDemo";
private TextView textView;
private MutableLiveData<String> liveData1 = new MutableLiveData<>();
private MutableLiveData<String> liveData2 = new MutableLiveData<>();
private MyViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
viewModel = new ViewModelProvider(this).get(MyViewModel.class);
viewModel.countDown();
.... ...
}
5.4、修改MyViewModel
增加liveDataMerger字段,并编写getLiveDataMerger()方法,这个方法里面的内容,是从MainActivity中copy过来的
public class MyViewModel extends ViewModel {
private static final String TAG = "MyViewMode";
.... ...
private MediatorLiveData<String> liveDataMerger;
public MediatorLiveData<String> getLiveDataMerger() {
if (liveDataMerger == null) {
liveDataMerger = new MediatorLiveData<>();
}
liveDataMerger.addSource(liveData1, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.i(TAG, "addSource1 onChanged: " + s);
liveDataMerger.setValue(s);
}
});
liveDataMerger.addSource(liveData2, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.i(TAG, "addSource2 onChanged: " + s);
liveDataMerger.setValue(s);
}
});
return liveDataMerger;
}
... ...
}
5.5、修改MainActivity中和MediatorLiveData有关的代码
1、使用viewModel.getLiveDataMerger().observe替换liveDataMerger.observe
viewModel.getLiveDataMerger().observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.i(TAG, "liveDataMerger onChanged: " + s);
textView.setText(s);
}
});
2、使用viewModel.mergeTest();替换mergeTest()
viewModel.mergeTest();
3、删除掉liveDataMerger相关的代码
5.6 重构后MainActivity的代码
public class MainActivity extends AppCompatActivity {
private static final String TAG = "LiveDataDemo";
private TextView textView;
private MyViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
viewModel = new ViewModelProvider(this).get(MyViewModel.class);
viewModel.getLiveData().observe(this, new Observer<Long>() {
@Override
public void onChanged(Long aLong) {
textView.setText(String.valueOf(aLong / 1000));
}
});
viewModel.getLiveDataMerger().observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.i(TAG, "liveDataMerger onChanged: " + s);
textView.setText(s);
}
});
viewModel.mergeTest();
// viewModel.countDown();
}
}
5.7 重构后MyViewModel的代码
public class MyViewModel extends ViewModel {
private static final String TAG = "MyViewMode";
private MutableLiveData<Long> liveData = new MutableLiveData<>();
private MutableLiveData<String> liveData1 = new MutableLiveData<>();
private MutableLiveData<String> liveData2 = new MutableLiveData<>();
private MediatorLiveData<String> liveDataMerger;
public MediatorLiveData<String> getLiveDataMerger() {
if (liveDataMerger == null) {
liveDataMerger = new MediatorLiveData<>();
}
liveDataMerger.addSource(liveData1, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.i(TAG, "addSource1 onChanged: " + s);
liveDataMerger.setValue(s);
}
});
liveDataMerger.addSource(liveData2, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.i(TAG, "addSource2 onChanged: " + s);
liveDataMerger.setValue(s);
}
});
return liveDataMerger;
}
public MutableLiveData<Long> getLiveData() {
return liveData;
}
public MutableLiveData<String> getLiveData1() {
return liveData1;
}
public MutableLiveData<String> getLiveData2() {
return liveData2;
}
public void countDown() {
new CountDownTimer(1 * 60 * 1000, 1 * 1000) {
@Override
public void onTick(long l) {
Log.i(TAG, "onTick: " + l);
liveData.postValue(l);
}
@Override
public void onFinish() {
}
}.start();
}
public void mergeTest() {
new CountDownTimer(1 * 60 * 1000, 3 * 1000) {
@Override
public void onTick(long l) {
liveData1.postValue("网络有数据更新了" + l / 1000);
}
@Override
public void onFinish() {
}
}.start();
new CountDownTimer(1 * 60 * 1000, 10 * 1000) {
@Override
public void onTick(long l) {
liveData2.postValue("本地数据库更新了" + l / 1000);
}
@Override
public void onFinish() {
}
}.start();
}
}