鸿蒙系统事件总线框架—好雨的实现原理

时间:2021-7-20 作者:qvyue

一、前言

1、好雨是一款鸿蒙系统上的,基于ActiveData的事件总线框架。好雨一词来源于“好雨知时节,当春乃发生”,好雨知道下雨的节气,正是在春天植物萌发生长的时候。好雨是一款基于ActiveData的事件总线框架。ActiveData知道发送数据给观察者的时候正是宿主活跃的时候,宿主销毁的时候正是移除观察者的时候。
2、在鸿蒙系统事件总线框架—好雨这篇文章中,我们介绍了好雨的用法,如果你还不熟悉好雨的用法,建议你先去看下。本文会介绍好雨的实现原理,带领大家手写好雨。
3、这里先给出源码。

二、ActiveData的用法

好雨是基于ActiveData的,所以需要先熟悉ActiveData的用法,ActiveData的详解可以阅读“好雨知时节,当春乃发生”的ActiveData这篇文章。我们先来回顾下ActiveData的用法。
创建DataObserver对象,在onStart方法里面创建ActiveData对象,调用ActiveDatasetData方法发送数据,调用DataObserversetLifecycle方法来给观察者设置生命周期,调用addObserver方法给ActiveData添加观察者,addObserver方法的第二个参数为false,这就意味着,只有当宿主处在活跃状态的时候,观察者才会接收到数据。

public class MainAbility extends Ability {

    private DataObserver mDataObserver1 = new DataObserver() {

        /**
         * 接收ActiveData发送过来的数据
         * 
         * @param s
         */
        @Override
        public void onChanged(String s) {
            LogUtils.info("yunfei", s);
        }
    };

    private DataObserver mDataObserver2 = new DataObserver() {

        /**
         * 接收ActiveData发送过来的数据
         * 
         * @param s
         */
        @Override
        public void onChanged(String s) {
            LogUtils.info("yunfei", s);
        }
    };

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setMainRoute(MainAbilitySlice.class.getName());
        // 创建ActiveData对象,泛型为string,这也就意味着,ActiveData发送一个字符串给观察者
        ActiveData activeData = new ActiveData();
        // 发送数据
        activeData.setData("更新页面");
        // 给观察者设置生命周期,直接调用宿主的getLifecycle()方法
        mDataObserver1.setLifecycle(getLifecycle());
        mDataObserver2.setLifecycle(getLifecycle());
        // 添加观察者,第二个参数false,这就意味着,只有当宿主处在活跃状态的时候,activeData才把数据发送给观察者
        activeData.addObserver(mDataObserver1, false);
        activeData.addObserver(mDataObserver2, false);
        LogUtils.info("yunfei", "onStart");
    }

    @Override
    protected void onActive() {
        super.onActive();
        LogUtils.info("yunfei", "onActive");
    }
}

在上面的代码中,创建了两个观察者,ActiveData只发送了一次消息,但两个观察者都收到了同一个消息。这在一定程度上会给我们带来麻烦。比如,发送登录事件,登录完成后,又来了一个观察者,新的观察者也能收到登录事件,此时又会进行登录。接下来就来手写好雨框架,并且解决这个问题。

三、手写好雨框架

3、1 ActiveDataBus类

3、1、1 创建ActiveDataBus,继承ActiveData。在ActiveDataBus里面有个mVersion属性,每当发送消息时,该属性的值加一。该类会重写ActivaDatasetData方法,并且提供注册观察者的方法。下面具体讲解ActivaDataBus里面的方法。

public class ActiveDataBus extends ActiveData {

    /**
     * 事件名称
     */
    private final String eventName;
    /**
     * 发送的消息
     */
    public T value;
    // 发送消息时,该属性的值加一
    public int mVersion;

    public ActiveDataBus(String eventName) {
        this.eventName = eventName;
    }

    /**
     * 发送数据
     *
     * @param value 数据
     */
    @Override
    public void setData(T value) {
        // 版本加一
        mVersion++;
        this.value = value;
        super.setData(value);
    }

    /**
     * 注册观察者,监听普通消息,只有当宿主活跃时,观察者才会接收到消息。
     *
     * @param iLifecycle 宿主的生命周期
     * @param observer 观察者
     */
    public void observe(ILifecycle iLifecycle, DataObserver observer) {
        addObserver(iLifecycle, observer, false, false);
    }

    /**
     * 注册观察者,监听普通消息,在任何生命周期状态下都将消息发送给观察者,即便宿主不活跃,观察者也会接收到消息。
     *
     * @param iLifecycle 宿主的生命周期
     * @param observer 观察者
     */
    public void observeAlways(ILifecycle iLifecycle, DataObserver observer) {
        addObserver(iLifecycle, observer, false, true);
    }

    /**
     * 注册观察者,监听粘性消息,只有当宿主活跃时,观察者才会接收到消息。
     * 粘性消息指的是消息先发送,后注册的观察者也能接收到之前发送的信息。
     *
     * @param iLifecycle 宿主的生命周期
     * @param observer 观察者
     */
    public void observeSticky(ILifecycle iLifecycle, DataObserver observer) {
        addObserver(iLifecycle, observer, true, false);
    }

    /**
     * 注册观察者,监听粘性消息,在任何生命周期状态下都将消息发送给观察者,即便宿主不活跃,观察者也会接收到消息。
     *
     * @param iLifecycle 宿主的生命周期
     * @param observer 观察者
     */
    public void observeStickyAlways(ILifecycle iLifecycle, DataObserver observer) {
        addObserver(iLifecycle, observer, true, true);
    }

    /**
     * 添加观察者
     *
     * @param iLifecycle 宿主的生命周期
     * @param observer 观察者
     * @param sticky 观察者是否接收粘性消息
     * @param always false表示当宿主活跃的时候才将消息发生给观察者,
     *               true表示在任何生命周期状态下都将消息发送给观察者,即便宿主不活跃,观察者也会接收到消息。
     */
    public void addObserver(ILifecycle iLifecycle, DataObserver observer, boolean sticky, boolean always) {
        Lifecycle lifecycle = iLifecycle.getLifecycle();
        ActiveDataObserver activeDataObserver = new ActiveDataObserver(this, sticky, observer);
        // 给观察者设置生命周期
        activeDataObserver.setLifecycle(lifecycle);
        super.addObserver(activeDataObserver, always);
    }
}

3、1、2 重写setData方法,每当调用setData方法时,将mVersion属性加一,然后调用ActiveDatasetData方法。

    /**
     * 发送数据
     *
     * @param value 数据
     */
    @Override
    public void setData(T value) {
        // 版本加一
        mVersion++;
        this.value = value;
        super.setData(value);
    }

3、1、3 注册观察者的方法。
observe方法监听普通消息,只有当宿主活跃时,观察者才会接收到消息。
observeAlways方法监听普通消息,在任何生命周期状态下都将消息发送给观察者,即便宿主不活跃,观察者也会接收到消息。
observeSticky方法监听粘性消息,只有当宿主活跃时,观察者才会接收到消息。
observeStickyAlways方法监听粘性消息,在任何生命周期状态下都将消息发送给观察者,即便宿主不活跃,观察者也会接收到消息。

    /**
     * 注册观察者,监听普通消息,只有当宿主活跃时,观察者才会接收到消息。
     *
     * @param iLifecycle 宿主的生命周期
     * @param observer 观察者
     */
    public void observe(ILifecycle iLifecycle, DataObserver observer) {
        addObserver(iLifecycle, observer, false, false);
    }

    /**
     * 注册观察者,监听普通消息,在任何生命周期状态下都将消息发送给观察者,即便宿主不活跃,观察者也会接收到消息。
     *
     * @param iLifecycle 宿主的生命周期
     * @param observer 观察者
     */
    public void observeAlways(ILifecycle iLifecycle, DataObserver observer) {
        addObserver(iLifecycle, observer, false, true);
    }

    /**
     * 注册观察者,监听粘性消息,只有当宿主活跃时,观察者才会接收到消息。
     * 粘性消息指的是消息先发送,后注册的观察者也能接收到之前发送的信息。
     *
     * @param iLifecycle 宿主的生命周期
     * @param observer 观察者
     */
    public void observeSticky(ILifecycle iLifecycle, DataObserver observer) {
        addObserver(iLifecycle, observer, true, false);
    }

    /**
     * 注册观察者,监听粘性消息,在任何生命周期状态下都将消息发送给观察者,即便宿主不活跃,观察者也会接收到消息。
     *
     * @param iLifecycle 宿主的生命周期
     * @param observer 观察者
     */
    public void observeStickyAlways(ILifecycle iLifecycle, DataObserver observer) {
        addObserver(iLifecycle, observer, true, true);
    }

3、1、4 上面所有的注册观察者的方法都会调用到addObserver方法。先创建ActiveDataObserver对象,将调用者传递过来的观察者对象包装成ActiveDataObserver对象,然后设置生命周期,因为最终还是利用ActiveData来添加观察者,所以需要调用ActivaDataaddObserver方法来添加观察者。

    /**
     * 添加观察者
     *
     * @param iLifecycle 宿主的生命周期
     * @param observer 观察者
     * @param sticky 观察者是否接收粘性消息
     * @param always false表示当宿主活跃的时候才将消息发生给观察者,
     *               true表示在任何生命周期状态下都将消息发送给观察者,即便宿主不活跃,观察者也会接收到消息。
     */
    public void addObserver(ILifecycle iLifecycle, DataObserver observer, boolean sticky, boolean always) {
        Lifecycle lifecycle = iLifecycle.getLifecycle();
        // 将调用者传递过来的观察者对象包装成ActiveDataObserver对象
        ActiveDataObserver activeDataObserver = new ActiveDataObserver(this, sticky, observer);
        // 给观察者设置生命周期
        activeDataObserver.setLifecycle(lifecycle);
        super.addObserver(activeDataObserver, always);
    }

3、2 ActiveDataObserver类

3、2、1 在上面的代码中,我们将调用者传递过来的观察者对象包装成ActiveDataObserver对象。ActiveDataObserver继承了DataObserverActiveDataObserver也是观察者。当有消息过来时,就会回调ActiveDataObserveronChange方法。这样我们就拥有了主动权,从而判断要不要将消息分发给调用者传递过来的观察者。每个观察者都有lastVersion属性,该属性默认跟ActiveData的mVersion属性对齐。
3、2、2 在onChange方法里面,如果先发送消息,那么mActiveDataBus.mVersion属性就会加一,然后注册观察者,此时就会在构造方法里面将lastVersion属性跟ActiveData的mVersion对齐。此时lastVersion >= mActiveDataBus.mVersion等式成立。如果lastVersion >= mActiveDataBus.mVersion,说明是粘性消息,也就是先发送了消息,后注册观察者。在这种情况下,正常是不需要将消息分发给观察者的。但是,如果观察者监听粘性事件,此时仍然要消息分发给观察者。如果lastVersion ,则需要将消息发送给观察者。

public class ActiveDataObserver extends DataObserver {

    private final ActiveDataBus mActiveDataBus;
    /**
     * 调用者传递过来的观察者
     */
    private final DataObserver observer;

    private int lastVersion;
    /**
     * 观察者是否接收粘性消息
     */
    private final boolean sticky;

    public ActiveDataObserver(ActiveDataBus activeDataBus, boolean sticky, DataObserver observer) {
        this.mActiveDataBus = activeDataBus;
        this.observer = observer;
        // 观察者的lastVersion属性跟ActiveData的mVersion对齐
        lastVersion = activeDataBus.mVersion;
        this.sticky = sticky;
    }

    @Override
    public void onChanged(T t) {
        /*
         * 如果先发送消息,那么mActiveDataBus.mVersion属性就会加一,然后注册观察者,
         * 此时就会在构造方法里面将lastVersion属性跟ActiveData的mVersion对齐,
         * 此时lastVersion >= mActiveDataBus.mVersion等式成立。
         * 如果lastVersion >= mActiveDataBus.mVersion,说明是粘性消息,也就是先发送了消息,后注册观察者,
         * 在这种情况下,正常是不需要将消息分发给观察者的,
         * 但是,如果观察者监听粘性事件,此时仍然要消息分发给观察者。
         */
        if (lastVersion >= mActiveDataBus.mVersion) {
            // 观察者监听粘性事件,此时仍然要消息分发给观察者
            if (sticky && mActiveDataBus.value != null) {
                // 观察者监听粘性事件,此时仍然要消息分发给观察者
                observer.onChanged(mActiveDataBus.value);
            }
            return;
        }
        lastVersion = mActiveDataBus.mVersion;
        // 需要将消息发送给观察者
        observer.onChanged(t);
    }
}

3、3 Observable接口

在上面的代码中,我们创建ActiveDataBus,继承ActiveData。由于我们并不想将ActiveData里面的方法暴露给调用者。所以我们创建一个接口Observable,在该接口里面提供方法给外界调用。主要是提供发送消息和注册观察者的方法。

public interface Observable {

    /**
     * 发送消息
     *
     * @param value 消息
     */
    void post(T value);

    /**
     * 延迟发送消息,当延迟时间到了后,只有宿主处在开始状态或者活跃状态时,才发送消息
     *
     * @param lifecycle 宿主的生命周期
     * @param value 消息
     * @param delay 延迟时间
     */
    void postDelay(ILifecycle lifecycle, T value, long delay);

    /**
     * 注册观察者,监听普通消息,只有当宿主活跃时,观察者才会接收到数据。
     *
     * @param iLifecycle 宿主的生命周期
     * @param observer 观察者
     */
    void observe(ILifecycle iLifecycle, DataObserver observer);

    /**
     * 注册观察者,监听普通消息,在任何生命周期状态下都将消息发送给观察者,即便宿主不活跃,观察者也会接收到消息。
     *
     * @param iLifecycle 宿主的生命周期
     * @param observer 观察者
     */
    void observeAlways(ILifecycle iLifecycle, DataObserver observer);

    /**
     * 注册观察者,监听粘性消息,只有当宿主活跃时,观察者才会接收到数据。
     * 粘性消息指的是消息先发送,后注册的观察者也能接收到之前发送的信息。
     *
     * @param iLifecycle 宿主的生命周期
     * @param observer 观察者
     */
    void observeSticky(ILifecycle iLifecycle, DataObserver observer);

    /**
     * 注册观察者,监听粘性消息,在任何生命周期状态下都将消息发送给观察者,即便宿主不活跃,观察者也会接收到消息。
     *
     * @param iLifecycle 宿主的生命周期
     * @param observer 观察者
     */
    void observeStickyAlways(ILifecycle iLifecycle, DataObserver observer);

}

3、4 LiveData类

LiveData是个委托类,它实现了Observable接口。为了不让外界直接调用ActiveData里面的方法,创建一个委托类,在委托类里面调用ActiveData里面的方法。LiveData里面持有ActiveDataBus对象,当调用LiveData里面的方法都会调用到ActiveDataBus里面的方法。发送消息和注册观察者都是在主线程中进行,如果当前是主线程,则直接进行发送消息或者注册观察者。如果当时不是主线程,则会使用EventHandler切换到主线程进行发送消息或者注册观察者。

public class LiveData implements Observable {

    private String eventName;
    private final ActiveDataBus mActiveDataBus;
    private final EventHandler mMainHandler = new EventHandler(EventRunner.getMainEventRunner());

    public LiveData(String eventName) {
        this.eventName = eventName;
        mActiveDataBus = new ActiveDataBus(eventName);
    }

    /**
     * 发送消息
     *
     * @param value 消息
     */
    @Override
    public void post(T value) {
        if (ThreadUtils.isMainThread()) {
            mActiveDataBus.setData(value);
        } else {
            mMainHandler.postTask(new Runnable() {
                @Override
                public void run() {
                    mActiveDataBus.setData(value);
                }
            });
        }
    }

    /**
     * 延迟发送消息,当延迟时间到了后,只有宿主处在开始状态或者活跃状态时,才发送消息
     *
     * @param lifecycle 宿主的生命周期
     * @param value 消息
     * @param delay 延迟时间
     */
    @Override
    public void postDelay(ILifecycle lifecycle, T value, long delay) {
        Utils.checkNotNull(lifecycle, "lifecycle must not be null");
        mMainHandler.postTask(new PostLifecycleValue(value, lifecycle), delay);
    }

    /**
     * 注册观察者,监听普通消息,只有当宿主活跃时,才将消息发送给观察者。
     *
     * @param iLifecycle 宿主的生命周期
     * @param observer 观察者
     */
    @Override
    public void observe(ILifecycle iLifecycle, DataObserver observer) {
        if (ThreadUtils.isMainThread()) {
            mActiveDataBus.observe(iLifecycle, observer);
        } else {
            mMainHandler.postTask(new Runnable() {
                @Override
                public void run() {
                    mActiveDataBus.observe(iLifecycle, observer);
                }
            });
        }
    }

    /**
     * 注册观察者,监听普通消息,在任何生命周期状态下都将消息发送给观察者,即便宿主不活跃,也会将消息发送给观察者。
     *
     * @param iLifecycle 宿主的生命周期
     * @param observer 观察者
     */
    @Override
    public void observeAlways(ILifecycle iLifecycle, DataObserver observer) {
        if (ThreadUtils.isMainThread()) {
            mActiveDataBus.observeAlways(iLifecycle, observer);
        } else {
            mMainHandler.postTask(new Runnable() {
                @Override
                public void run() {
                    mActiveDataBus.observeAlways(iLifecycle, observer);
                }
            });
        }
    }

    /**
     * 注册观察者,监听粘性消息,只有当宿主活跃时,才将消息发送给观察者。
     * 粘性消息指的是消息先发送,后注册的观察者也能接收到之前发送的信息。
     *
     * @param iLifecycle 宿主的生命周期
     * @param observer 观察者
     */
    @Override
    public void observeSticky(ILifecycle iLifecycle, DataObserver observer) {
        if (ThreadUtils.isMainThread()) {
            mActiveDataBus.observeSticky(iLifecycle, observer);
        } else {
            mMainHandler.postTask(new Runnable() {
                @Override
                public void run() {
                    mActiveDataBus.observeSticky(iLifecycle, observer);
                }
            });
        }
    }

    /**
     * 注册观察者,监听粘性消息,在任何生命周期状态下都将消息发送给观察者,即便宿主不活跃,也会将消息发送给观察者。
     *
     * @param iLifecycle 宿主的生命周期
     * @param observer 观察者
     */
    @Override
    public void observeStickyAlways(ILifecycle iLifecycle, DataObserver observer) {
        if (ThreadUtils.isMainThread()) {
            mActiveDataBus.observeStickyAlways(iLifecycle, observer);
        } else {
            mMainHandler.postTask(new Runnable() {
                @Override
                public void run() {
                    mActiveDataBus.observeStickyAlways(iLifecycle, observer);
                }
            });
        }
    }

    private class PostLifecycleValue implements Runnable {

        private final Object value;
        private final Lifecycle mLifecycle;

        public PostLifecycleValue(Object value, ILifecycle lifecycle) {
            this.value = value;
            Utils.checkNotNull(lifecycle, "lifecycle must not be mull");
            mLifecycle = lifecycle.getLifecycle();
        }

        @Override
        public void run() {
            if (mLifecycle != null) {
                if (mLifecycle.getLifecycleState() == Lifecycle.Event.ON_START
                        || mLifecycle.getLifecycleState() == Lifecycle.Event.ON_ACTIVE) {
                    // 当宿主处在开始状态或者活跃状态时,才发送消息
                    post((T) value);
                }
            }
        }
    }
}

3、5 HaoYu类

最后创建HaoYu类,很多时候需要跨页面发送消息,所以HaoYu对象必须是一个单例。这里使用静态内部类来实现单例。
with方法用来获取被观察者对象,被观察者对象保存在ConcurrentHashMap,首先根据事件名称从ConcurrentHashMap里面查找被观察者对象,如果能查找到则直接返回,否则创建被观察者对象,并将被观察者对象保存到ConcurrentHashMap
需要注意的是,with方法的第二个参数是Class对象,为什么需要传递Class对象呢?这是因为with方法是泛型方法,为了显示的确定泛型的类型,这里需要调用者传递Class对象,Class对象就是用来确定泛型的类型。如果不传递Class对象,那么泛型就是ObjectObject并不是我们想要的类型。当我们在发送消息的时候,可以调用一个参数的with方法,此时可以不确切的知道具体的泛型。而在注册观察者的时候,一定要调用两个参数的with方法,因为此时需要显示的确定泛型的类型了。

public class HaoYu {

    /**
     * 保存被观察者
     */
    private final ConcurrentHashMap> eventMap = new ConcurrentHashMap();

    /**
     * 使用静态内部类来实现单例模式
     *
     * @return HaoYu对象
     */
    public static HaoYu get() {
        return HaoYuHolder.mInstance;
    }
    
    private static final class HaoYuHolder {
        private static final HaoYu mInstance = new HaoYu();
    }

    /**
     * 获取被观察者对象
     *
     * @param eventName 事件的名称
     * @param  泛型的类型
     * @return 被观察者对象
     */
    public  Observable with(String eventName) {
        return (Observable) with(eventName, Object.class);
    }

    /**
     * 获取被观察者对象
     *
     * @param eventName 事件的名称
     * @param type 指定泛型的类型
     * @param  指定泛型的类型
     * @return 被观察者对象
     */
    public  Observable with(String eventName, Class type) {
        if (!eventMap.containsKey(eventName)) {
            eventMap.put(eventName, new LiveData(eventName));
        }
        return (Observable) eventMap.get(eventName);
    }
}

至此,好雨框架写完了。可以看到,代码并不难,大家可以将源码下载下来,结合注释,相信大家可以理解好雨框架的原理。

四、总结

本文介绍了好雨框架的实现原理,好雨框架是基于ActiveData实现的,并且带领大家手写了好雨框架。

  • 因为需要跨页面发送数据,所以创建了一个单例的HaoYu对象。
  • 为了不每次创建被观察者对象,使用map来保存被观察者对象。
  • 为了不将ActiveData的方法暴露给外界,创建了一个LiveDataLiveData里面持有ActiveData对象。当调用LiveData里面的方法都会调用到ActiveData里面的方法。
  • 为了解决ActiveData存在的问题,创建了一个子类继承ActiveData
声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:qvyue@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。