1.前言
LiveData是Google推出的一系列的框架组件的其中一个,它是一个可以被观察的数据持有类,能够感知Activity、Fragment等组件的生命周期。
一个观察者去观察LiveData后,当观察者的生命周期处于STARTED或RESUMED状态时(即onStart()、onResume()、onPause()),LiveData的数据发生变化,则会通知观察者;若观察者处于其他状态,即使LiveData的数据发生变化,也不会发出通知。
正是由于这一特性,因此LiveData可以做到仅在组件处于活跃状态时才进行更新UI的操作。
使用LiveData前需要先了解Lifecycle,如果还不知道Lifecycle,可以看下这篇文章:Android框架组件–Lifecycle的使用
本文主要介绍如何使用LiveData。
2.LiveData使用例子
下面来看下如何使用LiveData。
3.1 添加依赖
在相应的moudle目录下的build.gradle中添加以下依赖:
dependencies {
//...
def lifecycle_version = "1.1.1"
//仅仅依赖LiveData
implementation "android.arch.lifecycle:livedata:$lifecycle_version"
}
3.2 创建LiveData对象
google官网提倡LiveData配合ViewModel一起使用。为了专注LiveData,这里先不用ViewModel,后面再补充说明如何跟ViewModel一起使用。直接看例子:
public class TestModel {
private MutableLiveData
public MutableLiveData
if (status == null)
status = new MutableLiveData<>();
return status;
}
}
MutableLiveData继承自LiveData,表示可变数据。这里创建一个保存String类型数据的LiveData。
3.3 观察LiveData对象
通常,会在Activity的onCreate()方法中开始对LiveData的观察:
public class TestActivity extends AppCompatActivity {
private static final String TAG = "test";
private TextView mTextView;
private TestModel mTestModel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_activity);
mTextView = findViewById(R.id.tv_test);
initVariable();
}
private void initVariable() {
//创建一个观察者去更新UI
final Observer
@Override
public void onChanged(@Nullable final String newName) {
Log.d(TAG, "onChanged: " + newName);
mTextView.setText(newName);
}
};
//创建TestModel对象
mTestModel = new TestModel();
//观察LiveData对象
//这里的this指的是LifecycleOwner,即LiveData会跟生命周期联系起来
mTestModel.getStatus().observe(this, statusObserver);
}
}
观察者可以通过observe()方法对LiveData进行监听,observe()方法需要传递一个LifecycleOwner参数进去,这表示LiveData会跟生命周期联系起来。
另外还有一个observeForever()方法,只需传递一个观察者进去就行,这意味着它跟生命周期没有任何关系,可以持续的观察,只要数据发生变化,都会通知观察者回调onChanged()。
3.4 更新LiveData对象
MutableLiveData提供了setValue(T)(主线程使用)和postValue(T)(子线程使用)两个方法来修改数据。LiveData并没有提供类似方法。当调用MutableLiveData的setValue(T)或postValue(T)方法后,Observer的onChanged()方法将会被回调,从而实现更新UI的操作。
注意,这是在观察者处于STARTED或RESUMED状态时,LiveData才会通知观察者数据变化;当观察者处于其他状态时,即使LiveData的数据变化了,也不会通知。
当组件的状态为DESTROYED时会自动移除观察者,这样Activity或Fragment就可以安全地观察LiveData而不用担心造成内存泄露。
我们来看个完整的例子:
public class TestActivity extends AppCompatActivity {
private static final String TAG = "test";
private TextView mTextView;
private TestModel mTestModel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_activity);
mTextView = findViewById(R.id.tv_test);
initVariable();
}
private void initVariable() {
//创建一个观察者去更新UI
final Observer
@Override
public void onChanged(@Nullable final String newName) {
Log.d(TAG, "onChanged: " + newName);
mTextView.setText(newName);
}
};
//创建TestModel对象
mTestModel = new TestModel();
//观察LiveData对象
//这里的this指的是LifecycleOwner,即LiveData会跟生命周期联系起来
mTestModel.getStatus().observe(this, statusObserver);
mTestModel.getStatus().setValue("onCreate");
}
@Override
protected void onStart() {
super.onStart();
mTestModel.getStatus().setValue("onStart");
}
@Override
protected void onResume() {
super.onResume();
mTestModel.getStatus().setValue("onResume");
}
@Override
protected void onPause() {
super.onPause();
mTestModel.getStatus().setValue("onPause");
}
@Override
protected void onStop() {
super.onStop();
mTestModel.getStatus().setValue("onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
mTestModel.getStatus().setValue("onDestroy");
}
}
一个完整的生命周期走下来,其输出结果为:
07-26 22:03:34.798 20995-20995/com.test.start D/test: onChanged: onStart
07-26 22:03:34.801 20995-20995/com.test.start D/test: onChanged: onResume
07-26 22:03:36.456 20995-20995/com.test.start D/test: onChanged: onPause
可以看到,只在onStart()、onResume()、onPause()时观察者才会收到数据更新的通知,其他状态下即使更新了数据也不会收到通知。
3. LiveData配合ViewModel使用
下面来看下跟ViewModel是如何一起使用的。
3.1 添加依赖
使用ViewModel还需添加ViewModel的依赖:
dependencies {
//...
def lifecycle_version = "1.1.1"
//ViewModel 和 LiveData
implementation "android.arch.lifecycle:extensions:$lifecycle_version"
}
3.2 创建ViewModel
public class TestViewModel extends ViewModel {
private MutableLiveData
public MutableLiveData
if (status == null)
status = new MutableLiveData<>();
return status;
}
}
继承ViewModel后即可。
3.3 观察并更新LiveData对象
public class TestActivity extends AppCompatActivity {
private static final String TAG = "test";
private TextView mTextView;
private TestViewModel mTestModel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_activity);
mTextView = findViewById(R.id.tv_test);
initVariable();
}
private void initVariable() {
final Observer
@Override
public void onChanged(@Nullable final String newName) {
Log.d(TAG, "onChanged: " + newName);
mTextView.setText(newName);
}
};
// mTestModel = new TestModel();
//改为通过ViewModel来创建对象
mTestModel = ViewModelProviders.of(this).get(TestViewModel.class);
mTestModel.getStatus().observe(this, statusObserver);
}
@Override
protected void onResume() {
super.onResume();
//更新数据
mTestModel.getStatus().setValue("onResume");
}
}
ViewModel跟普通Model流程都是一样的,只是创建对象时不一样而已。
Google建议LiveData配合ViewModel一起使用。
4. 扩展LiveData
除了使用MutableLiveData外,我们还可以通过继承LiveData类来扩展一些功能。
比如:只有观察者观察了LiveData的数据时,才开始进行进行获取数据的操作,这样可以节省资源,代码如下所示:
4.1 继承LiveData
public class TestLiveData extends LiveData
private static TestLiveData sInstance;
private LocationUtil mLocationUtil;
//设计为单例模式
public static TestLiveData getInstance() {
if (sInstance == null) {
sInstance = new TestLiveData();
}
return sInstance;
}
private TestLiveData() {
//创建一个获取位置的对象
mLocationUtil = new LocationUtil();
}
@Override
protected void onActive() {
//开始获取位置信息
mLocationUtil.start(mLocationListener);
}
@Override
protected void onInactive() {
//停止获取位置信息
mLocationUtil.stop();
}
//创建一个位置监听器
private LocationListener mLocationListener = new LocationListener() {
@Override
public void onReceiveLocation(String location) {
//接受到位置信息后,更新数据
setValue(location);
}
};
}
将LiveData设计成单例模式后,可以在多个Activity、Fragment和Service之间共享它。
下面我们来重点关系一下LiveData的三个方法:onActive()、onInactive()、setValue()。
onActive():当有一个处于活跃状态的观察者监听LiveData时会被调用,这表示开始获取位置信息。
onInactive():当没有任何处于活跃状态的观察者监听LiveData时会被调用。由于没有观察者在监听了,所以也没必要继续去获取位置信息了,这只会消耗更多的电量等等,因此就可以停止获取位置信息了。
setValue():更新LiveData的值,并通知到观察者。
4.2 使用自定义的LiveData
下面来看下怎么使用这个自定义的LiveData:
public class TestActivity extends AppCompatActivity {
private static final String TAG = "test";
private TextView mTextView;
private TestLiveData mTestLiveData;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_activity);
mTextView = findViewById(R.id.tv_test);
initVariable();
}
private void initVariable() {
final Observer
@Override
public void onChanged(@Nullable final String newName) {
Log.d(TAG, "onChanged: " + newName);
mTextView.setText(newName);
}
};
//通过单例获取对象
mTestLiveData =TestLiveData.getInstance();
//同样是去观察LiveData
mTestLiveData.observe(this, statusObserver);
}
}
跟之前的一样,还是通过一个观察者去监听这个mTestLiveData对象。
5. LiveData变换
LiveData变换主要有两种变换:map和switchMap,都是Transformations类提供的。
5.1 map变换
public class TestViewModel extends ViewModel {
private MutableLiveData
//通过Transformations.map()将Integer类型的值转换为String类型
private LiveData
public MutableLiveData
return mNumLiveData;
}
public LiveData
return mStrLiveData;
}
}
map变换可以直接修改返回的值和类型。
5.2 switchMap变换
public class TestViewModel extends ViewModel {
private MutableLiveData
//switchMap变换
private LiveData
//返回一个LiveData
private LiveData
MutableLiveData
liveData.setValue(num + "a");
return liveData;
}
public MutableLiveData
return mNumLiveData;
}
public LiveData
return mNameLiveData;
}
}
switchMap变换需要返回一个LiveData对象,这就是跟map变换的区别。
6. 合并多个LiveData数据源
如果有多个LiveData,可以使用MediatorLiveData来合并这些LiveData,一旦其中一个LiveData发生变化,MediatorLiveData都会通知观察者。比如:一个UI界面,依赖于网络数据和数据库,因此就会存在两个LiveData。使用MediatorLiveData将两个LiveData合并后,UI界面只需要观察一个MediatorLiveData即可。当其中一个LiveData数据发生变化时都会通知UI界面去更新。
6.1 MediatorLiveData使用例子
MediatorLiveData的简单使用如下所示:
LiveData liveData1 = ...;
LiveData liveData2 = ...;
MediatorLiveData liveDataMerger = new MediatorLiveData<>();
liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));
然后在UI界面直接观察这个liveDataMerger即可。
6.2 MediatorLiveData的方法
MediatorLiveData相比于LiveData,主要是多了以下两个方法:
addSource():添加源LiveData,并且开始监听给定的源LiveData,当源LiveData的数据发生变化时,观察者的onChanged()方法将会被调用,前提是MediatorLiveData处于活跃状态。
removeSource():移除LiveData。
再来看个例子:
liveDataMerger.addSource(liveData1, new Observer() {
private int count = 1;
@Override public void onChanged(@Nullable Integer s) {
count++;
liveDataMerger.setValue(s);
if (count > 10) {
liveDataMerger.removeSource(liveData1);
}
}
});
监听liveData1的数据,当监听了10次之后,移除对liveData1监听。
6.3 使用Transformations实现自定义变换
MediatorLiveData除了可以合并数据外,实际上还可以用来实现自定义的变换方法,上面Transformations的map()和switchMap()如果不能满足变换需求的话,那么就可以用MediatorLiveData来实现。
7. LiveData优点
使用LiveData具有以下优点:
1.确保UI跟数据状态一致
组件处于活跃状态时,当数据发生变化,LiveData会通知观察者去更新UI,从而使得他们保持一致。
2.没有内存泄露
由于观察者绑定到了Lifecycle对象上,因此在Lifecycle被销毁后,观察者会被自行清理掉。
3.停止Activity不会造成崩溃
如果Activity处于非活跃的状态,比如Activity在后台时,那么它不会接受到LiveData的数据变更事件。
4.无需手动处理生命周期
UI组件仅仅需要观察相应的数据即可,无需手动去停止或恢复观察。因为LiveData会自动管理这所有的,它在观察时能够意识到相关的生命周期状态变化。
5.始终保持最新数据
如果生命周期处于不活跃的状态,那么当它变为活跃状态时将会收到最新的数据。比如:后台Activity变为前台时将会收到最新的数据。
6.适当的配置更改
如果由于配置更改而重新去创建Activity或Fragment,那么会立即接收最新的可用数据。
7.资源共享
你可以继承LiveData并使用单例模式来扩展系统的服务,这样你就可以共享它。这个自定义的LiveData对象只需连接一次系统服务,其他需要这些资源的观察者只需观察这个LiveData即可。可以参看第4小节:扩展LiveData。