Android LiveData组件详解以及LiveDataBus

转载请标明出处:https://blog.csdn.net/zhaoyanjun6/article/details/99749323
本文出自【赵彦军的博客】

一、LiveData简介

LiveData 是一个可以被观察的数据持有类,它可以感知 Activity、Fragment或 Service 等组件的生命周期。简单来说,他主要有一下优点。

  • 它可以做到在组件处于激活状态的时候才会回调相应的方法,从而刷新相应的 UI,不用担心发生内存泄漏
  • 当 config 导致 activity 重新创建的时候,不需要手动取处理数据的储存和恢复。它已经帮我们封装好了。
  • 当 Actiivty 不是处于激活状态的时候,如果你想 livedata setValue 之后立即回调 obsever 的 onChange 方法,而不是等到 Activity 处于激活状态的时候才回调 obsever 的 onChange 方法,你可以使用 observeForever 方法,但是你必须在 onDestroy 的时候 removeObserver。

回想一下,在你的项目中,是不是经常会碰到这样的问题,当网络请求结果回来的时候,你经常需要判断 Activity 或者 Fragment 是否已经 Destroy, 如果不是 destroy,才更新 UI。而当你如果使用 Livedata 的话,因为它是在 Activity 处于 onStart 或者 onResume 的状态时,他才会进行相应的回调,因而可以很好得处理这个问题,不必谢一大堆的 activity.isDestroyed()。接下来,让我们一起来看一下 LiveData 的使用。

二、使用

LiveData 是一个抽象类,它的实现子类有 MutableLiveDataMediatorLiveData。在实际使用中,用得比较多的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

3.1 为什么要用LiveDataBus替代EventBus和RxBus?

  • 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 使用更为方便,并且没有内存泄漏风险。

3.2 LiveDataBus的设计和架构

LiveDataBus的组成

  • 消息 消息可以是任何的 Object,可以定义不同类型的消息,如 Boolean 、String 。也可以定义自定义类型的消息。
  • 消息通道 LiveData 扮演了消息通道的角色,不同的消息通道用不同的名字区分,名字是 String 类型的,可以通过名字获取到一个LiveData 消息通道。
  • 消息总线 消息总线通过单例实现,不同的消息通道存放在一个 HashMap 中。
  • 订阅 订阅者通过 getChannel 获取消息通道,然后调用 observe 订阅这个通道的消息。
  • 发布 发布者通过 getChannel 获取消息通道,然后调用 setValue 或者 postValue 发布消息。

3.3 LiveDataBus原理图

在这里插入图片描述

3.4 LiveDataBus的实现

3.4.1 第一个实现

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章