美文网首页
200行代码手写一套EventBus事件总线

200行代码手写一套EventBus事件总线

作者: Harvie1208 | 来源:发表于2019-07-19 23:09 被阅读0次


    理论千万篇,不如实战来一篇。

    源码 https://github.com/harvie1208/EventBus

    关键词:观察者模式、反射、自定义注解、线程调度

    手写200行代码,一步一步实现EventBus核心功能,看完可以写一套属于自己的事件总线库啦!

    不知大家平常在看博客的时候有没有和我遇到一样的问题,就是看的是懂非懂,好像懂了,又好像没懂。

    主要有以下两点:

        1.文章缺少部分实现思路,导致自己实现时卡住。

        2.术语太过专业化,不易理解。

    在求知的路上,我也看了不少文章,有非常优秀的,也有缺这少那的。一路走来填了不少坑,后面我会将所学知识点整理出来分享给大家,尽量做到通俗易懂的理论加完整案例源码。一方面是对自己知识点的总结回顾,另一方面也希望能帮助到有需要的同学少走弯路。因技术水平有限,如有不正之处,还望各位不吝指教。

    EventBus简介

    EventBus顾名思义就是事件总线,实际上就是一个`事件发布者/事件监听者(订阅者)` 的框架, 发布者发布事件,Bus自动处理与分发,监听者被动的接受。简化各种异步和跳转的通信。

    ![](https://user-gold-cdn.xitu.io/2019/7/18/16c059b4885766f6?w=1000&h=374&f=png&s=111792)

    使用场景示例

        1.短信验证码登陆场景

            主登陆界面A->输入手机号界面B->短信验证码界面C->登陆成功跳转首页D

            需求:登陆成功后需要关闭A、B、C三个页面

        2.音乐播放场景

            假如首页有5个tab(包含5个fragment),每个fragment中都有音乐播放状态小图标

            需求:音乐播放或暂停时,需要更新所有播放状态图标

    核心思路

        使用观察者模式,在需要接收事件的方法上添加订阅注解标识,并将此方法所在对象添加到订阅者集合,发送事件时遍历订阅者集合,在通过反射调用相关订阅方法。

    代码实战

    1.编写EventBus核心类,使用单例模式提供唯一实例

    public class EventBus {

        private static EventBus myBus;

        public static EventBus getInstance(){

            if (myBus==null){

                synchronized (EventBus.class){

                    if (myBus==null){

                        myBus = new EventBus();

                    }

                }

            }

            return myBus;

        }

    }

    2.给订阅方法添加@Subscribe标识

        创建自定义注解@Subscribe用来标示订阅方法

        注解Annontation是Java5开始引入的新特征,通俗来说就是为程序的元素(类、方法、成员变量)添加标记用的

        @Target(ElementType.METHOD) //表示此注解作用域在方法上

        @Retention(RetentionPolicy.RUNTIME) //编译程序处理完注解信息后存储在class中,可由VM读入

        public @interface Subscribe {

            ThreadModel thread();//用于指定被注解方法执行时所在的线程

        }

        使用注解

        public class MainActivity extends AppCompatActivity {

            @Subscribe(thread = ThreadModel.BACKGROUND)//指定在子线程中执行

            public void haha(LoginEvent loginEvent){

                Log.e("EventBus",loginEvent.getLoginStatus()+Thread.currentThread().getName());

            }

        }

    3.注册订阅关系

        * 先声明一个集合用于存储类对象和被注解的方法及线程模式

        * 遍历注册对象的所有方法,取出带有@Subscribe注解的方法

        * 获取参数类型数组,当前仅支持一个参数

        * 获取指定线程模式

        * 构建订阅者实例(方法、参数类型、线程模式),加入订阅集合

        public class EventBus {

            //存储订阅类及方法参数

            private Map<Object,List<Subscriber>> subscribeMethod;

            public void register(Object obj){

                if (obj==null){

                    return;

                }

                Class<?> mclazz = obj.getClass();

                //获取本类所有方法

                Method[] methods = mclazz.getDeclaredMethods();

                List<Subscriber> methods1 = new ArrayList<>();

                for (Method method : methods){

                    //获取带有我们Subscribe注解的方法

                    Subscribe subscribe = method.getAnnotation(Subscribe.class);

                    if (subscribe==null){

                        continue;

                    }

                    //获取参数类型集合

                    Class<?>[] typeVariable = method.getParameterTypes();

                    if (typeVariable.length!=1){

                        continue;

                    }

                    ThreadModel threadModel = subscribe.thread();

                    Subscriber busMethod = new Subscriber(method,threadModel,typeVariable[0]);

                    methods1.add(busMethod);

                }

                if (methods1.size()>0){

                    subscribeMethod.put(obj,methods1);

                }

            }

        }

    4.注销订阅

        将此订阅对象移除订阅集合

        public void unRegister(Object object){

            if (subscribeMethod.containsKey(object)){

                subscribeMethod.remove(object);

            }

        }

    5.发送事件

        * 根据发送事件参数类型,遍历集合找到对应方法

        * 判断线程模式,主线程用handler处理,子线程用线程池处理

        * 反射调用方法将事件传过去

        public class EventBus {

            //存储订阅类及方法参数

            private Map<Object,List<Subscriber>> subscribeMethod;

            //线程调度

            private Handler mHandler;

            //线程池

            private ExecutorService executorService;

            private EventBus(){

                subscribeMethod = new HashMap<>();

                mHandler = new Handler(Looper.getMainLooper());

                executorService = Executors.newCachedThreadPool();

            }

            public void postEvent(Object eventParam){

                Set<Object> set = subscribeMethod.keySet();

                Iterator<Object> iterable =set.iterator();

                while (iterable.hasNext()){

                    Object obj = iterable.next();

                    List<Subscriber> busMethodList = subscribeMethod.get(obj);

                    for (Subscriber busMethod : busMethodList){

                        if(busMethod.getParamsType() == eventParam.getClass()){

                            invoke(obj,busMethod,eventParam);

                        }

                    }

                }

            }

            private void invoke(final Object obj, final Subscriber busMethod, final Object eventParam){

                switch (busMethod.getThreadModel()){

                    case MAIN:

                        //通过handler调度到主线程

                        mHandler.post(new EventRunable(busMethod, obj, eventParam));

                        break;

                    default:

                        //交由线程池处理

                        executorService.execute(new EventRunable(busMethod, obj, eventParam));

                        break;

                }

            }

        }

        事件参数与接收参数类型一致即可,方法名随意

        EventBus.getInstance().postEvent(new LoginEvent("登录成功"));

    ## 总结

    很多看似高大上的框架其实也没我们想的那么难,写着写着就顺手了,知而不行为不知,快动起手来吧!

    源码 https://github.com/harvie1208/EventBus

    相关文章

      网友评论

          本文标题:200行代码手写一套EventBus事件总线

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