转载请标明出处:https://blog.csdn.net/zhaoyanjun6/article/details/99749323
本文出自【赵彦军的博客】
LiveData
是一个可以被观察的数据持有类,它可以感知 Activity、Fragment或 Service 等组件的生命周期。简单来说,他主要有一下优点。
回想一下,在你的项目中,是不是经常会碰到这样的问题,当网络请求结果回来的时候,你经常需要判断 Activity 或者 Fragment 是否已经 Destroy, 如果不是 destroy,才更新 UI。而当你如果使用 Livedata 的话,因为它是在 Activity 处于 onStart 或者 onResume 的状态时,他才会进行相应的回调,因而可以很好得处理这个问题,不必谢一大堆的 activity.isDestroyed()。接下来,让我们一起来看一下 LiveData 的使用。
LiveData
是一个抽象类,它的实现子类有 MutableLiveData
,MediatorLiveData
。在实际使用中,用得比较多的MutableLiveData
。他常常结合 ViewModel
一起使用。下面,让我们一起来看一下怎样使用它?
import android.arch.lifecycle.MutableLiveData
import android.arch.lifecycle.Observer
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.widget.Button
import android.widget.TextView
class MainActivity : AppCompatActivity() {
lateinit var tv: TextView
var liveData = MutableLiveData<String>() //定义liveData
private val changeObserver = Observer<String> { value ->
value?.let { tv.text = it }
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tv = findViewById(R.id.tv1)
liveData.value = "123"
liveData.observe(this, changeObserver) //注册观察者
findViewById<Button>(R.id.cancel).setOnClickListener {
liveData.value = "456"
}
}
}
注意事项
必须要从主线程调用 setValue(T)
方法来更新 LiveData
对象;如果代码在工作线程中执行, 你可以使用 postValue(T)
方法来更新LiveData
对象
LiveDataBus的实现及其简单
相对 EventBus 复杂的实现,LiveDataBus 只需要一个类就可以实现。LiveDataBus可以减小APK包的大小
由于LiveDataBus只依赖 Android 官方 Android Architecture Components 组件的 LiveData ,没有其他依赖,本身实现只有一个类。作为比较,EventBus JAR包大小为57kb,RxBus依赖RxJava和RxAndroid,其中RxJava2 包大小 2.2 MB,RxJava1 包大小 1.1 MB,RxAndroid 包大小 9 kb。使用 LiveDataBus 可以大大减小 APK 包的大小。LiveDataBus依赖方支持更好
LiveDataBus 只依赖 Android 官方 Android Architecture Components 组件的 LiveData ,相比 RxBus 依赖的 RxJava 和 RxAndroid,依赖方支持更好。LiveDataBus具有生命周期感知
LiveDataBus 具有生命周期感知,在 Android 系统中使用调用者不需要调用反注册,相比 EventBus 和 RxBus 使用更为方便,并且没有内存泄漏风险。LiveDataBus
的组成
消息
消息可以是任何的 Object,可以定义不同类型的消息,如 Boolean 、String 。也可以定义自定义类型的消息。消息通道
LiveData 扮演了消息通道的角色,不同的消息通道用不同的名字区分,名字是 String 类型的,可以通过名字获取到一个LiveData 消息通道。消息总线
消息总线通过单例实现,不同的消息通道存放在一个 HashMap 中。订阅
订阅者通过 getChannel 获取消息通道,然后调用 observe 订阅这个通道的消息。发布
发布者通过 getChannel 获取消息通道,然后调用 setValue 或者 postValue 发布消息。public final class LiveDataBus {
private final Map<String, MutableLiveData<Object>> bus;
private LiveDataBus() {
bus = new HashMap<>();
}
private static class SingletonHolder {
private static final LiveDataBus DATA_BUS = new LiveDataBus();
}
public static LiveDataBus get() {
return SingletonHolder.DATA_BUS;
}
public <T> MutableLiveData<T> getChannel(String target, Class<T> type) {
if (!bus.containsKey(target)) {
bus.put(target, new MutableLiveData<>());
}
return (MutableLiveData<T>) bus.get(target);
}
public MutableLiveData<Object> getChannel(String target) {
return getChannel(target, Object.class);
}
}
短短二十行代码,就实现了一个通信总线的全部功能,并且还具有生命周期感知功能,并且使用起来也及其简单:
注册订阅
:
LiveDataBus.get().getChannel("key_test", Boolean.class)
.observe(this, new Observer<Boolean>() {
@Override
public void onChanged(@Nullable Boolean aBoolean) {
}
});
发送消息
:
LiveDataBus.get().getChannel("key_test").setValue(true);
我们发送了一个名为”key_test”
,值为true
的事件。
这个时候订阅者就会收到消息,并作相应的处理,非常简单。
问题出现
对于 LiveDataBus 的第一版实现,我们发现,在使用这个 LiveDataBus 的过程中,订阅者会收到订阅之前发布的消息。对于一个消息总线来说,这是不可接受的。无论 EventBus 或者 RxBus,订阅方都不会收到订阅之前发出的消息。对于一个消息总线, LiveDataBus 必须要解决这个问题。
问题原因总结
对于这个问题,总结一下发生的核心原因。对于 LiveData,其初始的 version是-1,当我们调用了其 setValue 或者 postValue ,其 vesion 会+1;对于每一个观察者的封装 ObserverWrapper,其初始 version 也为-1,也就是说,每一个新注册的观察者,其version 为-1;当LiveData设置这个 ObserverWrapper 的时候,如果 LiveData 的 version 大于 ObserverWrapper的version,LiveData 就会强制把当前 value 推送给 Observer。
如何解决这个问题
明白了问题产生的原因之后,我们来看看怎么才能解决这个问题。很显然,根据之前的分析,只需要在注册一个新的订阅者的时候把 Wrapper 的 version 设置成跟 LiveData 的 version 一致即可。
那么怎么实现呢,看看 LiveData的observe 方法,他会在步骤1创建一个 LifecycleBoundObserver,LifecycleBoundObserver 是ObserverWrapper 的派生类。然后会在步骤 2 把这个 LifecycleBoundObserver 放入一个私有 Map 容器 mObservers 中。无论ObserverWrapper 还是 LifecycleBoundObserver 都是私有的或者包可见的,所以无法通过继承的方式更改 LifecycleBoundObserver 的 version。
那么能不能从 Map 容器 mObservers 中取到 LifecycleBoundObserver ,然后再更改 version 呢?答案是肯定的,通过查看SafeIterableMap 的源码我们发现有一个 protected 的 get方法。因此,在调用 observe 的时候,我们可以通过反射拿到LifecycleBoundObserver,再把 LifecycleBoundObserver 的 version 设置成和 LiveData 一致即可。
LiveDataBus最终实现
public final class LiveDataBus {
private final Map<String, BusMutableLiveData<Object>> bus;
private LiveDataBus() {
bus = new HashMap<>();
}
private static class SingletonHolder {
private static final LiveDataBus DEFAULT_BUS = new LiveDataBus();
}
public static LiveDataBus get() {
return SingletonHolder.DEFAULT_BUS;
}
public <T> MutableLiveData<T> with(String key, Class<T> type) {
if (!bus.containsKey(key)) {
bus.put(key, new BusMutableLiveData<>());
}
return (MutableLiveData<T>) bus.get(key);
}
public MutableLiveData<Object> with(String key) {
return with(key, Object.class);
}
private static class ObserverWrapper<T> implements Observer<T> {
private Observer<T> observer;
public ObserverWrapper(Observer<T> observer) {
this.observer = observer;
}
@Override
public void onChanged(@Nullable T t) {
if (observer != null) {
if (isCallOnObserve()) {
return;
}
observer.onChanged(t);
}
}
private boolean isCallOnObserve() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
if (stackTrace != null && stackTrace.length > 0) {
for (StackTraceElement element : stackTrace) {
if ("android.arch.lifecycle.LiveData".equals(element.getClassName()) &&
"observeForever".equals(element.getMethodName())) {
return true;
}
}
}
return false;
}
}
private static class BusMutableLiveData<T> extends MutableLiveData<T> {
private Map<Observer, Observer> observerMap = new HashMap<>();
@Override
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
super.observe(owner, observer);
try {
hook(observer);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void observeForever(@NonNull Observer<T> observer) {
if (!observerMap.containsKey(observer)) {
observerMap.put(observer, new ObserverWrapper(observer));
}
super.observeForever(observerMap.get(observer));
}
@Override
public void removeObserver(@NonNull Observer<T> observer) {
Observer realObserver = null;
if (observerMap.containsKey(observer)) {
realObserver = observerMap.remove(observer);
} else {
realObserver = observer;
}
super.removeObserver(realObserver);
}
private void hook(@NonNull Observer<T> observer) throws Exception {
//get wrapper's version
Class<LiveData> classLiveData = LiveData.class;
Field fieldObservers = classLiveData.getDeclaredField("mObservers");
fieldObservers.setAccessible(true);
Object objectObservers = fieldObservers.get(this);
Class<?> classObservers = objectObservers.getClass();
Method methodGet = classObservers.getDeclaredMethod("get", Object.class);
methodGet.setAccessible(true);
Object objectWrapperEntry = methodGet.invoke(objectObservers, observer);
Object objectWrapper = null;
if (objectWrapperEntry instanceof Map.Entry) {
objectWrapper = ((Map.Entry) objectWrapperEntry).getValue();
}
if (objectWrapper == null) {
throw new NullPointerException("Wrapper can not be bull!");
}
Class<?> classObserverWrapper = objectWrapper.getClass().getSuperclass();
Field fieldLastVersion = classObserverWrapper.getDeclaredField("mLastVersion");
fieldLastVersion.setAccessible(true);
//get livedata's version
Field fieldVersion = classLiveData.getDeclaredField("mVersion");
fieldVersion.setAccessible(true);
Object objectVersion = fieldVersion.get(this);
//set wrapper's version
fieldLastVersion.set(objectWrapper, objectVersion);
}
}
}
注册订阅
LiveDataBus.get()
.with("key_test", String.class)
.observe(this, new Observer<String>() {
@Override
public void onChanged(@Nullable String s) {
}
});
发送消息
LiveDataBus.get().with("key_test").setValue(s);
源码说明
LiveDataBus 的源码可以直接拷贝使用,也可以前往作者的 GitHub 仓库查看下载:
https://github.com/JeremyLiao/LiveDataBus 。
Android LiveData 使用详解
Android消息总线的演进之路:用LiveDataBus替代RxBus、EventBus