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<String> status;
public MutableLiveData<String> getStatus() {
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<String> statusObserver = new Observer<String>() {
@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<String> statusObserver = new Observer<String>() {
@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<String> status;
public MutableLiveData<String> getStatus() {
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<String> statusObserver = new Observer<String>() {
@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<String> {
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<String> statusObserver = new Observer<String>() {
@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<Integer> mNumLiveData= new MutableLiveData<>();
//通过Transformations.map()将Integer类型的值转换为String类型
private LiveData<String> mStrLiveData = Transformations.map(mNumLiveData, num -> num + "");
public MutableLiveData<Integer> getNumLiveData() {
return mNumLiveData;
}
public LiveData<String> getStrLiveData() {
return mStrLiveData;
}
}
map
变换可以直接修改返回的值和类型。
5.2 switchMap变换
public class TestViewModel extends ViewModel {
private MutableLiveData<Integer> mNumLiveData = new MutableLiveData<>();
//switchMap变换
private LiveData<String> mNameLiveData = Transformations.switchMap(mNumLiveData, num -> getName(num));
//返回一个LiveData
private LiveData<String> getName(Integer num) {
MutableLiveData<String> liveData = new MutableLiveData<>();
liveData.setValue(num + "a");
return liveData;
}
public MutableLiveData<Integer> getNumLiveData() {
return mNumLiveData;
}
public LiveData<String> getNameLiveData() {
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。