Android Jetpack之LiveData
LiveData概述
LiveData是一个可观察的数据持有者类,和常规的observable不同,它是用来感知生命周期的,这意味着它遵守其他应用组件(activity、fragment、service等)的生命周期。这就确保它只会更新处于活动状态的组件。
如果一个类实现了android.arch.lifecycle.Observer,那么LiveData就认为这个类是STARTED或者RESUMED状态。LiveData只会通知活动的观察者更新信息,不处于活动状态的观察者不会受到信息。
我们可以注册一个observer和实现了LifecycleOwner的类一起使用(在Support Library 26.1.0 和以上的版本中,activity和fragment已经默认实现了LifecycleOwner接口)。当生命周期改变的时候,可以删除观察者,这对activity和fragment非常有用,它们可以安全的观察LiveData而不用担心内存泄露。当activity和fragment的生命周期执行到destroyed的时候立即取消订阅。
使用LiveData
- 创建一个LiveData用来保存特定的类型数据的实例,这通常在ViewModel中完成
- 创建一个Observer,定义onChanged()方法当数据改变的时候会执行这个方法,我们可以在activitt、fragment等UI控制器中创建这个Observer。
- 使用observe()方法把Observer添加到LiveData上。这个方法持有一个LifecycleOwner对象,一般在 activity 或 fragment中添加
可以通过observeForever(Observer)这个方法,注册一个永久的观察者,那么这个观察者就被认为是永远活动的,它也就一直能收到变化的通知,可以使用removeObserver(Observer)方法将它删除
LiveData是一个包装器,可以和任何对象使用。包括实现了Collections的对象,比如List,一个LiveData对象一般存在一个ViewModel中,通过一个getter方法获得。
示例1
2
3
4
5
6
7
8
9
10
11
12public class NameViewModel extends ViewModel {
private MutableLiveData<String> currentName;
// 创建一个String类型的LiveData
public MutableLiveData<String> getCurrentName() {
if (currentName == null) {
currentName = new MutableLiveData<String>();
}
return currentName;
}
}
确保存在LiveData中的数据不是activity和fragment,原因是他们是用来显示数据的而不是保存数据的状态。并且把LiveData和activity和fragment分离,并允许LiveData对象在配置更改后继续存在
观察LiveData对象
多数情况下,我们在app组件的onCreate()方法中来观察LiveData对象。原因如下
- 确保activity和fragment不会再onResume()中进行冗余的调用
- 确保activity和fragment在变为活动状态的时候能立即收到并显示数据
LiveData一般情况下只在数据发生改变的时候才会通知在活动状态下的activity或者fragment更新,还有当观察者从非活动状态变为活动状态的时候也会改变。此外,如果观察者第二次从非活动状态更改为活动状态,则只有在自上次活动状态以来该值发生更改时才会收到更新。
更新LiveData对象
LiveData没有公开的方法来更新它存储的对象,它的子类MutableLiveData提供了setValue(T) 和postValue(T)两个方法来更新数据,setValue(T)是在主线程中更新数据,postValue(T)是在子线程中更新数据
例子:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43public class NameViewModel1 extends ViewModel {
private MutableLiveData<String> currentName;
public MutableLiveData<String> getCurrentName() {
if (currentName == null) {
currentName = new MutableLiveData<String>();
loadData();
}
return currentName;
}
private void loadData() {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
currentName.postValue("Lily");
}
}, 1000);
}
}
public class NameActivity extends AppCompatActivity {
private NameViewModel1 mViewModel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_name);
final TextView textView = findViewById(R.id.tv_name);
//// 获取ViewModel.
mViewModel = ViewModelProviders.of(this).get(NameViewModel1.class);
// 观察LiveData,传入当前的LifecycleOwner和观察者
mViewModel.getCurrentName().observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
textView.setText(s);
}
});
}
}
上面例子中,ViewModel中模拟一秒钟之后改变MutableLiveData中的值,NameActivity中也会跟着改变。
使用LiveData的优点
- 确保UI跟数据匹配,LiveData遵循观察者模式,可以在数据更改时更改UI
- 没有内存泄漏,观察者绑定Lifecycle对象,会在其销毁的时候自行清理
- 不会因为stopped activities而崩溃,如果观察者处于非活动状态,那么它是不会收到任何的LiveData的事件
- 不用在手动处理生命周期,UI组件只观察相关的数据,不会停止或者恢复观察,LiveData会自动管理数据,它在观察UI组件的时候,可以感知到生命周期的变化。
- 始终保持最新数据,后台activity返回前台时,可以立即接收到数据
- 共享资源,我们可以扩展LiveData对象,使用单例模式包装系统服务,这样就可以在app中共享服务,只要LiveData 连接到系统服务,其他要使用系统服务的观察者只需观察这个LiveData对象即可。
扩展LiveData
直接看官网的例子,继承LiveData之后,重写其onActive()和onInactive()方法,在onActive()中注册一个监听,在onInactive()方法中取消这个监听。
1 | public class StockLiveData extends LiveData<BigDecimal> { |
- onActive(),当LiveData对象处于活动状态的时候会回调此方法
- onInactive(),当LiveData对象没有活动的观察者的时候回调此方法,我们可以做一些清除操作
- setValue(T),更新LiveData实例的值,并通知其他处于活动状态的观察者改变
LiveData可以感知生命周期,这意味着我们可以在多个activity、fragment、service中共享它
为了保持实例的一致性,可以把它设置成单例的1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41public class StockLiveData extends LiveData<BigDecimal> {
private static StockLiveData sInstance;
private StockManager stockManager;
private SimplePriceListener listener = new SimplePriceListener() {
@Override
public void onPriceChanged(BigDecimal price) {
setValue(price);
}
};
@MainThread
public static StockLiveData get(String symbol) {
if (sInstance == null) {
sInstance = new StockLiveData(symbol);
}
return sInstance;
}
private StockLiveData(String symbol) {
stockManager = new StockManager(symbol);
}
@Override
protected void onActive() {
stockManager.requestPriceUpdates(listener);
}
@Override
protected void onInactive() {
stockManager.removeUpdates(listener);
}
}
public class MyFragment extends Fragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
StockLiveData.get(symbol).observe(this, price -> {
// Update the UI.
});
}
}
多个activity和fragment都可以观察上面自定义LiveData的实例,LiveData只会更新当前处于活动状态的组件。
转换LiveData
在LiveData对象把更新的数据分发给观察者之前,我们可以通过转换操作,对数据进行加工。android.arch.lifecycle类中提供了转换的方法。
Transformations.map() 可转换LiveData的输出
上面NameActivity的例子中我们可以更改一下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_name);
final TextView textView = findViewById(R.id.tv_name);
mViewModel = ViewModelProviders.of(this).get(NameViewModel1.class);
LiveData<String> userNameMap = Transformations.map(mViewModel.getCurrentName(), new Function<String, String>() {
@Override
public String apply(String input) {
return input + "哈哈哈";
}
});
userNameMap.observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
textView.setText(s);
}
});
}
上面代码会在在name后面拼接一个哈哈哈,这只是最简单的逻辑,我们可以在map函数中添加更多的逻辑,比如我们的LiveData接收的是一个User对象,最后输出的是名字的字符串等
Transformations.switchMap() 更改被LiveData观察的对象
和map()方法类似,区别是switchMap()给出的是LiveData,而map给出的是具体值。1
2
3
4
5
6private LiveData<User> getUser(String id) {
...;
}
LiveData<String> userId = ...;
LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );
比如上面的方法,通过switchMap,把用户的id和LiveData相关联,当用户的id发生改变的时候,会重新调用查找用户的方法,最后返新用户的LiveData的数据,这样不管查询多少个用户,界面都只需观察生成的LiveData一次
MediatorLiveData 提供自定义的转换
可以使用MediatorLiveData来自定义转换,该类监听其他LiveData对象并处理它们发出的事件。MediatorLiveData正确地将其状态传播到LiveData对象,更多参考Transformations
MediatorLiveData是LiveData的子类,允许合并多个LiveData源。MediatorLiveData只要任何原始LiveData源对象发生更改,就会触发对象的观察者。
例如,如果LiveDataUI中有一个可以从本地数据库或网络更新的对象,则可以将以下源添加到该 MediatorLiveData对象:
- LiveData与存储在数据库中的数据关联的对象。
- LiveData与从网络访问的数据关联的对象。
LiveData源码:
上面的内容大部分都是官网的文档,通过看文档,知道了LiveData怎么用,下面来看看它的实现原理。
从注册开始(LiveData.observe()方法)
1 | public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) { |
- 此方法传入的两个对象其实就是被观察者和观察者
- 如果当前生命周期是销毁状态,直接返回
- 把被观察者和观察者包装成一个LifecycleBoundObserver对象。
- mObservers是一个链表,用来缓存包装对象,这里判断一下当前的包装对象是否已经存在了。
- 最后把这个包装对象注册到生命周期组件(被观察者owner)中,addObserver方法在上一篇lifcycles中看过了。
上一篇Lifecyles中我们知道,当被观察者owner(fragment、activity等)生命周期变化的时候,会调用传入的观察者的onStateChanged方法。这里传入的是LifecycleBoundObserver,所以会调用LifecycleBoundObserver的onStateChanged方法,它里面又调用了我们传入的observer的onChanged方法,我们就能拿到数据了。
下面看看这个LifecycleBoundObserver
1 |
|
- LifecycleBoundObserver继承自抽象类ObserverWrapper,实现了LifecycleEventObserver接口,LifecycleEventObserver中有个onStateChanged方法。当生命周期组件的生命周期发生变化的时候,就调用这个onStateChanged方法。
- onStateChanged中,判断如果当前是销毁状态,移除当前观察者并返回,这里它自动移除了观察者,我们也就不用手动移除观察者了。如果不是调用父类ObserverWrapper中的activeStateChanged方法。
- 前面看文档我们知道,LiveData只有在activiy或者fragment等生命周期组件处于前台的时候才会收到事件。就在activeStateChanged这个方法中判断了是不是活动状态
- onActive();和 onInactive();这两个是空方法,前面文档中如果我们自定义一个LiveData,就会重写这两个方法。
- 如果是活动状态,调用dispatchingValue方法分发事件,传入当前包装对象
1 | void dispatchingValue(@Nullable ObserverWrapper initiator) { |
- 如果正在分发着事件返回
如果传入的包装对象为空,就去前面说的链表mObservers中遍历,如果找到了或者我们传入的不为空,就调用considerNotify方法通知观察者。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
return;
}
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
//noinspection unchecked
observer.mObserver.onChanged((T) mData);
}如果mLastVersion小于当前的mVersion值才会继续往下执行。mVersion在setValue方法中++的,调用一个setValue方法,mVersion就+1,所以,当生命周期发生了变化,即使我们调用了considerNotify方法,但是当前的值没有改变,也不会执行观察者的onChanged方法。
observer.mObserver就是我们最开始传入的观察者,调用它的onChanged方法传入data,我们在activity或者fragment中就能收到这个data值了。
mData的值是在setValue方法中被赋值的。LiveData中有两种赋值的方法:setValue方法。和postValue,一个是在主线程中赋值,一个是在子线程中赋值。postValue其实就是在一个Runnable中地哦啊用setValue方法。
1
2
3
4
5
6protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
可以看到没次setValue,mVersion都会加一,并且调用dispatchingValue方法进行消息分发。
Ok到这里源码就看完了。下面思考一下,前面文档中我们知道,如果一个activity或者fragment在后台,他是收不到数据更新的。当切换到前台拿上就能收到。怎么实现的呢?
前面activeStateChanged方法中我们知道,只有activity或者fragment是活动状态才能会调用 dispatchingValue方法,如果它们在后台,这时候不调用dispatchingValue方法。当它们切换回到前台的时候,生命周期发生改变,会调用LifecycleBoundObserver中的onStateChange方法,然后又会进入到activeStateChanged方法中,这时候状态是活动的就可以调用dispatchingValue方法分发了。