移动架构 (四) EventBus源码分析及实现自己的轻量级 EventBus 框架

EventBus 我相信大家不会很默认,应该也都在项目中使用过,虽然 EventBus 在项目中不便于管理,发射出去的消息不便于跟踪或者阅读,但是不可否认它是一个优秀的开源项目,还是值得我们大家学习的,我个人感觉唯一不足的就是 EventBus 功能上不能根据 TAG 来进行发射和接收消息,只能通过注解 + 消息类型进行找消息。那么我们自己可以实现这个功能吗?不可否认,当然可以! 想要实现这个功能,我们先大概简要的了解下 EventBus 使用及源码是怎么实现的。

EventBus 简单使用

EventBus 可以代替 Android 传统的 Intent, Handler, Broadcast 或接口函数, 在 Fragment, Activity, Service 线程间进行数据传递。

添加 EventBus 到项目中

implementation 'org.greenrobot:eventbus:3.1.1'

register

EventBus.getDefault().register(this);

注解实现接收的 Event

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void receive(String event){
        Log.d(TAG,"接收到 EventBus post message:" + event);
    }

发射数据

EventBus.getDefault().post("发射一个测试消息");

注销注册的事件

EventBus.getDefault().unregister(this);

这里就简单介绍下 EventBus 使用,想详细了解的可以看 EventBus GitHub

EventBus 3.1.1 源码分析

上面小节咱们学习了 EventBus 的简单使用,那么我们就根据上面使用到的来进行源码分析。

register

流程图:

register 代码:

		/**
		*
		*EventBus register
		*/
    public void register(Object subscriber) {
      //1. 拿到当前注册 class
        Class<?> subscriberClass = subscriber.getClass();
      //2. 查找当前 class 类中所有订阅者的方法
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
              //3. subscribe(subscriber, subscriberMethod);
            }
        }
    }

subscribe(x,x)代码:

    //必须加锁调用
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
      //拿到订阅者参数类型
        Class<?> eventType = subscriberMethod.eventType;
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
      //根据参数类型,拿到当前所有订阅者
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
      //4. 如果没有拿到,则存进去缓存中
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
          //如果已经存在相同类型的注册事件,就抛出异常
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType);
            }
        }

       ......
    }
  1. 拿到当前传入进来的 this 对象。

  1. 查找当前 this 类中所有订阅的函数。

  1. subscriptionsByEventType 完成数据初始化。

subscriptionsByEventType = new HashMap();

// 参考上图注释 4 ,根据参数类型存储订阅者和订阅方法

post

流程图:

代码

    /** 发送事件*/
    public void post(Object event) {
      //在当前线程中取出变量数据
        PostingThreadState postingState = currentPostingThreadState.get();
      //将需要发送的数据添加进当前线程的队列中
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event);

        if (!postingState.isPosting) {
            postingState.isMainThread = isMainThread();postingState.isPosting = true;
            if (postingState.canceled) {
                throw new EventBusException("Internal error. Abort state was not reset");
            }
            try {
                while (!eventQueue.isEmpty()) {
                  //开启取出数据
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;postingState.isMainThread = false;
            }
        }
    }
    private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        if (eventInheritance) {
          //查找所有的事件类型
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();
            for (int h = 0; h < countTypes; h++) {
                Class<?> clazz = eventTypes.get(h);
                subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
            }
        } else {
          //发送事件
            subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
        }
        if (!subscriptionFound) {
            if (logNoSubscriberMessages) {
                logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
            }
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                post(new NoSubscriberEvent(this, event));
            }
        }
    }
    private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
          //这里的容器就是 register 的时候添加进行去的,现在拿出来
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        if (subscriptions != null && !subscriptions.isEmpty()) {
            for (Subscription subscription : subscriptions) {
                postingState.event = event;postingState.subscription = subscription;
                boolean aborted = false;
                try {
                  //发送到订阅者哪里去
                    postToSubscription(subscription, event, postingState.isMainThread);aborted = postingState.canceled;
                } finally {
                    postingState.event = null;postingState.subscription = null;postingState.canceled = false;
                }
                if (aborted) {
                    break;
                }
            }
            return true;
        }
        return false;
    }
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
      //根据订阅者 threadMode 来进行发送switch (subscription.subscriberMethod.threadMode) {
            case POSTING:
            //通过反射发送
                invokeSubscriber(subscription, event);
                break;
            case MAIN:
            //如果是主线程直接在当前线程发送if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                  //开启一个子线程发送
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case MAIN_ORDERED:
                if (mainThreadPoster != null) {
                    mainThreadPoster.enqueue(subscription, event);
                } else {
                    // temporary: technically not correct as poster not decoupled from subscriber
                    invokeSubscriber(subscription, event);
                }
                break;
            case BACKGROUND:
                if (isMainThread) {
                    backgroundPoster.enqueue(subscription, event);
                } else {
                    invokeSubscriber(subscription, event);
                }
                break;
            case ASYNC:
                asyncPoster.enqueue(subscription, event);
                break;
            default:
                thrownew IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }
    }

步骤

  1. post

  1. 获取事件类型

  1. 根据类型,获取订阅者和订阅方法

  1. 根据订阅者的 threadMode 来判断线程

  1. 通过反射直接调用订阅者的订阅方法来完成本次通信息

Subscribe

这里是订阅者的意思,通过自定义注解实现

@Documented
@Retention(RetentionPolicy.RUNTIME) //注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Target({ElementType.METHOD}) //只能在方法上声明
public @interface Subscribe {
    ThreadMode threadMode() default ThreadMode.POSTING; //默认在 post 线程

    boolean sticky() default false; //默认不是粘性事件

    int priority() default 0; //线程优先级默认
}

threadMode

package org.greenrobot.eventbus;

public enum ThreadMode {
    POSTING, // post 线程
    MAIN,//指定主线程
    MAIN_ORDERED,
    BACKGROUND,//后台进行
    ASYNC;//异步

    private ThreadMode() {
    }
}

unregister

流程:

EventBus 源码总结

到这里我们就简单的分析了 注册 - > 订阅 - > 发送 - >接收事件 简单的流程就是这样了,如果想更深入的话,建议下载源码来看 EventBus GitHub 架构方面还是要注重基础知识,比如 注解 + 反射 + 设计模式 (设计模式的话建议去看 《Android 源码设计模式》一书 ),现在开源项目几乎离不开这几项技术。只有我们掌握了基础 + 实现原理。我们模仿着也能写出来同样的项目。

下面我们就简单模仿下 EventBus 原理,实现自己的 EventBus 框架。

YEventBus 根据 TAG 实现接收消息架构实现

说明一点,这里我们还是根据 EventBus 的核心原理实现,并不会有 EventBus 那么多功能,我们只是学习 EventBus 原理的同时,能根据它的原理,自己写一套轻量级的 EventBus 架构。

最后一共差不多 300 行代码实现根据 TAG 发送/接收事件,下面是效果图:

以下我就直接贴代码了 每一步代码都有详细的注释,相信应该不难理解。

使用方式

  • 添加依赖

  allprojects {
  	repositories {
  		...
  		maven { url 'https://jitpack.io' }
  	}
  }

  dependencies {
          implementation 'com.github:YEventBus:Tag'
  }
  • 注册事件

//开始注册事件。模仿 EventBus
 YEventBus.getDefault().register(this);
  • 订阅消息

    /**
     * 这里是自定义的注册,最后通过反射来获取当前类里面的订阅者
     *
     * @parammeg
     */
    @YSubscribe(threadMode = YThreadMode.MAIN, tag = Constants.TAG_1)
    public void onEvent(String meg) {
        Toast.makeText(getApplicationContext(), "收到:" + meg, Toast.LENGTH_SHORT).show();
    }
  • 发送消息

YEventBus.getDefault().post(Constants.TAG_1, "发送 TAG 为 1 的消息");

register

    /**
     * 注册方法
     */
    public void register(Object subscriber) {
        //拿到当前注册的所有的订阅者
        List<YSubscribleMethod> ySubscribleMethods = mChacheSubscribleMethod.get(subscriber);
        //如果订阅者已经注册了 就不需要再注册了
        if (ySubscribleMethods == null) {
            //开始反射找到当前类的订阅者
            ySubscribleMethods = getSubscribleMethods(subscriber);
            //注册了就存在缓存中,避免多次注册
            mChacheSubscribleMethod.put(subscriber, ySubscribleMethods);
        }
    }

    /**
     * 拿到当前注册的所有订阅者
     *
     * @param subscriber
     * @return
     */
    private List<YSubscribleMethod> getSubscribleMethods(Object subscriber) {
        //拿到注册的 class
        Class<?> subClass = subscriber.getClass();
        //定义一个容器,用来装订阅者
        List<YSubscribleMethod> ySubscribleMethodList = new ArrayList<>();
        //开始循环找到
        while (subClass != null) {
            //1. 开始进行筛选,如果是系统的就不需要进行下去
            String subClassName = subClass.getName();
            if (subClassName.startsWith(Constants.JAVA) ||
                    subClassName.startsWith(Constants.JAVA_X) ||
                    subClassName.startsWith(Constants.ANDROID) ||
                    subClassName.startsWith(Constants.ANDROID_X)
            ) {
                break;
            }
            //2. 遍历拿到当前 class
            Method[] declaredMethods = subClass.getDeclaredMethods();
            for (Method declaredMethod : declaredMethods) {
                //3. 检测当前方法中是否有 我们的 订阅者 注解也就是 YSubscribe
                YSubscribe annotation = declaredMethod.getAnnotation(YSubscribe.class);
                //如果没有直接跳出查找
                if (annotation == null)
                    continue;

                // check 这个方法的参数是否有多个
                Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
                if (parameterTypes.length > 1) {
                    throw new RuntimeException("YEventBus 只能接收一个参数");
                }

                //4. 符合要求,最后添加到容器中//4.1 拿到需要在哪个线程中接收事件
                YThreadMode yThreadMode = annotation.threadMode();
                //只能在当前 tag 相同下才能接收事件
                String tag = annotation.tag();
                YSubscribleMethod subscribleMethod = new YSubscribleMethod(tag, declaredMethod, yThreadMode, parameterTypes[0]);
                ySubscribleMethodList.add(subscribleMethod);

            }
            //去父类找订阅者
            subClass =   subClass.getSuperclass();
        }
        return ySubscribleMethodList;
    }

post (这里不是粘性事件)

    /**
     * post 方法
     */
    public void post(String tag, Object object) {
        //拿到当前所有订阅者持有的类
        Set<Object> subscriberClass = mChacheSubscribleMethod.keySet();
        //拿到迭代器,
        Iterator<Object> iterator = subscriberClass.iterator();
        //进行循环遍历
        while (iterator.hasNext()) {
            //拿到注册 class
            Object subscribleClas = iterator.next();
            //获取类中所有添加订阅者的注解
            List<YSubscribleMethod> ySubscribleMethodList = mChacheSubscribleMethod.get(subscribleClas);
            for (YSubscribleMethod subscribleMethod : ySubscribleMethodList) {
                //判断这个方法是否接收事件
                if (!TextUtils.isEmpty(tag) && subscribleMethod.getTag().equals(tag) //注解上面的 tag 是否跟发送者的 tag 相同,相同就接收
                        && subscribleMethod.getEventType().isAssignableFrom(object.getClass() //判断类型
                )
                ) {
                    //根据注解上面的线程类型来进行切换接收消息
                    postMessage(subscribleClas, subscribleMethod, object);
                }

            }

        }

    }

    private void postMessage(final Object subscribleClas, final YSubscribleMethod subscribleMethod, final Object message) {

        //根据需要的线程来进行切换
        switch (subscribleMethod.getThreadMode()) {
            case MAIN:
                //如果接收的是主线程,那么直接进行反射,执行订阅者的方法
                if (isMainThread()) {
                    postInvoke(subscribleClas, subscribleMethod, message);
                } else {//如果接收消息在主线程,发送线程在子线程那么进行线程切换
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            postInvoke(subscribleClas, subscribleMethod, message);
                        }
                    });
                }
                break;
            case ASYNC://需要在子线程中接收
                if (isMainThread())
                    //如果当前 post  是在主线程中,那么切换为子线程
                    ThreadUtils.executeByCached(new ThreadUtils.Task<Boolean>() {
                        @Nullable
                        @Override
                        public Boolean doInBackground() throws Throwable {
                            postInvoke(subscribleClas, subscribleMethod, message);
                            return true;
                        }

                        @Override
                        public void onSuccess(@Nullable Boolean result) {
                            Log.i(TAG, "执行成功");
                        }

                        @Override
                        public void onCancel() {

                        }

                        @Override
                        public void onFail(Throwable t) {

                        }
                    });
                else
                    postInvoke(subscribleClas, subscribleMethod, message);
                break;
            case POSTING:
            case BACKGROUND:
            case MAIN_ORDERED:
                postInvoke(subscribleClas, subscribleMethod, message);
                break;
            default:
                break;
        }
    }

    /**
     * 反射调用订阅者
     *
     * @param subscribleClas
     * @param subscribleMethod
     * @param message
     */
    private void postInvoke(Object subscribleClas, YSubscribleMethod subscribleMethod, Object message) {
        Log.i(TAG, "post message: " + "TAG:" + subscribleMethod.getTag() + " 消息体:" + message);
        Method method = subscribleMethod.getMethod();
        //执行
        try {
            method.invoke(subscribleClas, message);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

Subscribe 订阅者

@Target(ElementType.METHOD) //target 描述此注解在哪里使用
@Retention(RetentionPolicy.RUNTIME) //retention 描述此注解保留的时长 这里是在运行时
public @interface YSubscribe {
    YThreadMode threadMode() default YThreadMode.POSTING; //默认是在 post 线程接收数据

    String tag() default "";//根据消息来接收事件
}

TreadMode 线程模式

public enum  YThreadMode {
    /**
     * Subscriber will be called directly in the same thread, which is posting the event. This is the default. Event delivery
     * implies the least overhead because it avoids thread switching completely. Thus this is the recommended mode for
     * simple tasks that are known to complete in a very short time without requiring the main thread. Event handlers
     * using this mode must return quickly to avoid blocking the posting thread, which may be the main thread.
     */
    POSTING,

    /**
     * On Android, subscriber will be called in Android's main thread (UI thread). If the posting thread is
     * the main thread, subscriber methods will be called directly, blocking the posting thread. Otherwise the event
     * is queued for delivery (non-blocking). Subscribers using this mode must return quickly to avoid blocking the main thread.
     * If not on Android, behaves the same as {@link #POSTING}.
     */
    MAIN,

    /**
     * On Android, subscriber will be called in Android's main thread (UI thread). Different from {@link #MAIN},
     * the event will always be queued for delivery. This ensures that the post call is non-blocking.
     */
    MAIN_ORDERED,

    /**
     * On Android, subscriber will be called in a background thread. If posting thread is not the main thread, subscriber methods
     * will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single
     * background thread, that will deliver all its events sequentially. Subscribers using this mode should try to
     * return quickly to avoid blocking the background thread. If not on Android, always uses a background thread.
     */
    BACKGROUND,

    /**
     * Subscriber will be called in a separate thread. This is always independent from the posting thread and the
     * main thread. Posting events never wait for subscriber methods using this mode. Subscriber methods should
     * use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number
     * of long running asynchronous subscriber methods at the same time to limit the number of concurrent threads. EventBus
     * uses a thread pool to efficiently reuse threads from completed asynchronous subscriber notifications.
     */
    ASYNC

}

unRegister取消注册

    /**
     * 取消注册订阅者
     */
    public void unRegister(Object subscriber) {
        Log.i(TAG, "unRegister start:当前注册个数" + mChacheSubscribleMethod.size());
        Class<?> subClas = subscriber.getClass();
        List<YSubscribleMethod> ySubscribleMethodList = mChacheSubscribleMethod.get(subClas);
        if (ySubscribleMethodList != null)
            mChacheSubscribleMethod.remove(subscriber);

        Log.i(TAG, "unRegister success:当前注册个数" + mChacheSubscribleMethod.size());
    }

框架怎么实现根据 TAG 接收消息

这个其实很简单,拿到 post 发送的 tag,跟订阅者的 tag 比较下就行了。其实只要了解原理也没有那么难得。

//判断这个方法是否接收事件
if (!TextUtils.isEmpty(tag) && subscribleMethod.getTag().equals(tag) //注解上面的 tag 是否跟发送者的 tag 相同,相同就接收
        && subscribleMethod.getEventType().isAssignableFrom(object.getClass() //判断类型
)

总结

最后我们根据开源项目 EventBus 实现了自己 YEventBus 框架,可以根据 TAG 发送/接收消息。只要了解开源框架原理,根据自己需求改动原有框架或者实现自己的框架都不是太难,加油!

系列文章

移动架构 (一) 详解架构设计中UML图的使用

移动架构 (二) Android 中 Handler 架构分析,并实现自己简易版本 Handler 框架

移动架构 (三) AMS 源码分析

移动架构 (四) EventBus源码分析及实现自己的轻量级 EventBus 框架

移动架构 (五) 了解Android数据持久化

移动架构 (六) 轻量级进程间通信框架设计

移动架构 (七) 组件化架构设计思想

移动架构 (八) 动态化加载插件技术模型实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

金戈鐡馬

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值