美文网首页
LiveData实现消息总线(零反射)

LiveData实现消息总线(零反射)

作者: wzmyyj | 来源:发表于2020-05-10 10:29 被阅读0次

app开发中,我们常遇见不同页面之间要保持数据同步。送最初的onActivityResult,到后面的第三方库EventBus,RxBus,LiveEventBus。现在我们需要利用livedata自己写一个简单的消息总线。

对比过去:

onActivityResult:使用较为繁琐,并且在多页面下(比如栈:A,B,C。C的事件要传递给A就使用不方便了)。
EventBus:时代的眼泪,使用方便,但性能内存体积都是缺陷。
RxBus:不是库,是个文件,实现只有短短30行代码。结合Rxjava可以很方便的使用。
LiveEventBus:利用系统的livedata。体积小。且可以感知生命周期。

消息总线 延迟发送 有序接收消息 Sticky 生命周期感知 跨进程/APP 线程分发
EventBus
RxBus
LiveEventBus

它们都很优秀。但我们不用它们。自己写个简单的实现方案。主要参考LiveEventBus的核心逻辑。

代码:

LiveData做消息,需要解决的是它的粘性问题(会接收注册前的消息)。网上有很多利用反射的方式修改lastVersion的方法。这里提供一个不用反射的方式。

自己写一个,也叫LiveEventBus。😄

public class LiveEventBus {

    private LiveEventBus() {
    }

    private static volatile LiveEventBus INSTANCE = null;

    public static LiveEventBus getInstance() {
        if (INSTANCE == null) {
            synchronized (LiveEventBus.class) {
                if (INSTANCE == null) {
                    INSTANCE = new LiveEventBus();
                }
            }
        }
        return INSTANCE;
    }

    private final Map<String, EventLiveData<?>> map = new HashMap<>();

    @SuppressWarnings("unchecked")
    public <T> MutableLiveData<T> get(String key, Class<T> clz) {
        if (!map.containsKey(key)) {
            EventLiveData<T> liveData = new EventLiveData<>();
            map.put(key, liveData);
            return liveData;
        }
        return (MutableLiveData<T>) map.get(key);
    }

    private static class EventLiveData<T> extends MutableLiveData<T> {

        private int version = 0;
        private final Map<Observer<?>, EventObserver<T>> observerMap = new HashMap<>();

        /**
         * postValue 最终也会调 setValue ,所以只需要在这里统计 version。
         */
        @Override
        public void setValue(T value) {
            super.setValue(value);
            version++;
        }

        @Override
        public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
            super.observe(owner, getEventObserver(observer));
        }

        @Override
        public void observeForever(@NonNull Observer<? super T> observer) {
            super.observeForever(getEventObserver(observer));
        }

        @Override
        public void removeObserver(@NonNull Observer<? super T> observer) {
            EventObserver<T> eventObserver = observerMap.remove(observer);
            if (eventObserver != null) super.removeObserver(eventObserver);
        }

        private EventObserver<T> getEventObserver(Observer<? super T> observer) {
            if (observerMap.get(observer) != null) throw new RuntimeException("observer重复添加了!");
            EventObserver<T> eventObserver = new EventObserver<>(observer, version);
            observerMap.put(observer, eventObserver);
            return eventObserver;
        }

        // 装饰者
        private class EventObserver<E> implements Observer<E> {
            private int observerVersion;
            private final Observer<? super E> observer;

            public EventObserver(Observer<? super E> observer, int version) {
                this.observer = observer;
                this.observerVersion = version;
            }

            @Override
            public void onChanged(E t) {
                if (observerVersion < EventLiveData.this.version) {
                    observerVersion = EventLiveData.this.version;
                    observer.onChanged(t);
                }
            }
        }
    }
}

使用姿势:

// 获取:(一般会把这步封装起来)
LiveEventBus .with<Event>(Key) //or 
LiveEventBus .with(Key,Event::class.java)
// 订阅:
observe(owner,observer)// 绑定生命周期。
observeForever(observer) // 永久有效。
// 发布:
setValue(event) // 主线程。
postValue(event) // 子线程。
// 移除:
同liveData。

实战:

  1. 封装:同一模块的事件放在一起。key 与 event 对应。
  2. key 命名规范:模块名+子模块名+具体事件。
  3. event类上加上ID:例如:ChangeGroupNameEvent加上groupCode明确是那个群名称变了。
// 示例
object IMEventHelper {
    private const val CHANGE_GROUP_NAME = "im/group/changeGroupName"
    private const val CHANGE_NOTIFICATION = "im/group/changeNotification"
    private const val CHANGE_MEMBER_LIST = "im/group/changeMemberList"
    private const val CHANGE_GROUP_LIST = "im/group/changeGroupList"
    private const val QUIT_GROUP = "im/group/quitGroup"
    private const val CHOOSE_AT = "im/group/chooseAt"
    /**
     * 群名称修改。
     */
    fun changeGroupName() = LiveEventBus .with<ChangeGroupNameEvent>(CHANGE_GROUP_NAME)
    /**
     * 公告修改。
     */
    fun changeNotification() = LiveEventBus .with<ChangeNotificationEvent>(CHANGE_NOTIFICATION)
    /**
     * 成员列表变换。
     */
    fun changeMemberList() = LiveEventBus .with<ChangeMemberListEvent>(CHANGE_MEMBER_LIST)
    /**
     * 群聊列表变换。
     */
    fun changeGroupList() = LiveEventBus .with<ChangeGroupListEvent>(CHANGE_GROUP_LIST)
    /**
     * 退出群聊。
     */
    fun quitGroup() = LiveEventBus .with<QuitGroupEvent>(QUIT_GROUP)
    /**
     * 选择AT 成员。
     */
    fun chooseAt() = LiveEventBus .with<AtMemberEvent>(CHOOSE_AT)
}

event类:

class ChangeGroupNameEvent(val groupCode: String, val newName: String)

使用:

// 在 fragment 初始化里订阅事件。
IMEventHelper
    .changeGroupName()
    .observeEvent(this) { vm.changeName(it) }
// 在 其它地方发布事件。
IMEventHelper
    .changeGroupName()
    .postValue(ChangeGroupNameEvent(groupCode, newName))

总结:

要点:

  1. 利用LiveData,感知生命周期的特性。
  2. 不接收注册观察之前的消息。(装饰者模式)
配一张图

相关文章

网友评论

      本文标题:LiveData实现消息总线(零反射)

      本文链接:https://www.haomeiwen.com/subject/fvidnhtx.html