美文网首页
教你快速实现一个EventBus框架

教你快速实现一个EventBus框架

作者: 千雨茶 | 来源:发表于2019-10-11 12:46 被阅读0次

    前言

    EventBus相信大多数人都用过,其具有方便灵活、解耦性强、体积小、简单易用等优点,虽然现在也有很多优秀的替代方案如RxBus、LiveDataBus等,但不可否认EventBus开创了消息总线时代,有很多优秀的思想可以供我们来借鉴学习。下面就让我们来撸一个超简单的EventBus,领略其实现方式的主要思想。

    实现思路

    先看一下GitHub中官方用法介绍:
    1.定义消息实体类

    public static class MessageEvent { /* Additional fields if needed */ }
    

    2.准备订阅者:声明并注解订阅方法

    @Subscribe(threadMode = ThreadMode.MAIN)  
    public void onMessageEvent(MessageEvent event) {/* Do something */};
    

    注册和注销订阅者

    @Override
     public void onStart() {
         super.onStart();
         EventBus.getDefault().register(this);
     }
    
     @Override
     public void onStop() {
         super.onStop();
         EventBus.getDefault().unregister(this);
     }
    

    3.发送事件

    EventBus.getDefault().post(new MessageEvent());
    

    EventBus通过register方法,查找注册对象中有Subscribe注解的方法,并把这些方法维护在EventBus自己的Map中,在某处调用post()方法发送消息时,通过消息载体Bean类,在Map中查找订阅了接收该Bean类的消息接收方法,调用该方法,达到消息传递的目的。

    功能实现

    • 准备工作

    1.定义线程枚举类

    public enum ThreadMode {
        MAIN,
        BACKGROUND
    }
    

    2.定义注解,注解目标是函数,运行时注解

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Subscribe {
        ThreadMode threadMode() default ThreadMode.MAIN;
    }
    

    3.定义方法封装类,用来描述使用了subscribe注解的消息接收方法,将维护在EventBus类中。

    public class SubscribeMethod {
    
        /**
         * 方法本身
         */
        private Method method;
    
        /**
         * 线程模式
         */
        private ThreadMode threadMode;
    
        /**
         * 参数类型
         */
        private Class<?> type;
    
    
        public SubscribeMethod(Method method, ThreadMode threadMode, Class<?> type) {
            this.method = method;
            this.threadMode = threadMode;
            this.type = type;
        }
    
    
        public Method getMethod() {
            return method;
        }
    
        public void setMethod(Method method) {
            this.method = method;
        }
    
        public ThreadMode getThreadMode() {
            return threadMode;
        }
    
        public void setThreadMode(ThreadMode threadMode) {
            this.threadMode = threadMode;
        }
    
        public Class<?> getType() {
            return type;
        }
    
        public void setType(Class<?> type) {
            this.type = type;
        }
    }
    
    • 定义EventBus类
      单例模式,定义一个Map维护订阅的方法
        private static volatile EventBus instance;
        private Map<Object, List<SubscribeMethod>> cacheMap;
    
        private EventBus() {
            cacheMap = new HashMap<>();
        }
    
        public static EventBus getDefault() {
            if (instance == null) {
                synchronized (EventBus.class) {
                    if (instance == null) {
                        instance = new EventBus();
                    }
                }
            }
            return instance;
        }
    

    定义注册方法,在该方法中,查找订阅的方法,维护在map中,该map的key是注册消息的对象,如Activity、Fragment等

        public void register(Object obj) {
            //找到消息监听方法,如onMessageEvent,获取信息,统一管理
            List<SubscribeMethod> list = cacheMap.get(obj);
            if (list == null) {
                list = findSubscribeMethods(obj);
                cacheMap.put(obj, list);
            }
        }
    

    其中findSubscribeMethods(obj)方法是用来查找对象中订阅方法,下面是其实现

        /**
         * 查找定义的消息接收方法
         *
         * @param obj 查找对象
         * @return
         */
        private List<SubscribeMethod> findSubscribeMethods(Object obj) {
            List<SubscribeMethod> list = new ArrayList<>();
            Class<?> clazz = obj.getClass();
            //寻找自身及父类subscribe注解的方法
            while (clazz != null) {
                //如果是系统的类,过滤掉
                String name = clazz.getName();
                if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {
                    break;
                }
                Method[] declaredMethods = clazz.getDeclaredMethods();
                for (Method method : declaredMethods) {
                    //找到带有subscribe注解的方法
                    Subscribe subscribe = method.getAnnotation(Subscribe.class);
                    if (subscribe == null) {
                        continue;
                    }
                    Class<?>[] parameterTypes = method.getParameterTypes();
                    //这里仅考虑只有一个参数的情况
                    if (parameterTypes.length != 1) {
                        throw new IllegalArgumentException("消息监听方法有且只能有一个参数!");
                    }
                    ThreadMode threadMode = subscribe.threadMode();
                    SubscribeMethod subscribeMethod = new SubscribeMethod(method, threadMode, parameterTypes[0]);
                    list.add(subscribeMethod);
                }
                clazz = clazz.getSuperclass();
            }
            return list;
        }
    

    定义消息发送方法

        /**
         * 发送消息
         *
         * @param type 消息实体
         */
        public void post(Object type) {
            //循环cacheMap,找到对应的方法进行回调
            Set<Object> set = cacheMap.keySet();
            Iterator<Object> iterator = set.iterator();
            while (iterator.hasNext()) {
                Object obj = iterator.next();
                List<SubscribeMethod> list = cacheMap.get(obj);
                for (SubscribeMethod subscribeMethod : list) {
                    //匹配发送的消息bean和接收的消息bean是否相同
                    if (subscribeMethod.getType().isAssignableFrom(type.getClass())) {
                        invoke(subscribeMethod, obj, type);
                    }
                }
            }
        }
    

    其中invoke(subscribeMethod, obj, type)反射调用回调的方法,实现如下

        /**
         * 反射调用回调方法
         *
         * @param subscribeMethod 方法对象
         * @param obj             被反射对象
         * @param type            方法参数
         */
        private void invoke(SubscribeMethod subscribeMethod, Object obj, Object type) {
            Method method = subscribeMethod.getMethod();
            try {
                method.invoke(obj, type);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    

    OK!这么一个最简单的EventBus就写完了,现在让我们来测试一下吧

    测试效果

    我们打算在MainActivity中订阅,在SecondActivity中发送消息来测试
    首先定义一个消息实体类

    public class MessageBean {
    
        private String param1;
    
        private String param2;
    
        public MessageBean(String param1, String param2) {
            this.param1 = param1;
            this.param2 = param2;
        }
    
        public String getParam1() {
            return param1;
        }
    
        public void setParam1(String param1) {
            this.param1 = param1;
        }
    
        public String getParam2() {
            return param2;
        }
    
        public void setParam2(String param2) {
            this.param2 = param2;
        }
    
    
        @Override
        public String toString() {
            return "MessageBean{" +
                    "param1='" + param1 + '\'' +
                    ", param2='" + param2 + '\'' +
                    '}';
        }
    }
    

    MainActivity中注册订阅

    public class MainActivity extends AppCompatActivity {
    
        private static final String TAG = "Main";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            EventBus.getDefault().register(this);
    
            startActivity(new Intent(this, SecondActivity.class));
        }
    
    
        @Subscribe(threadMode = ThreadMode.MAIN)
        public void onMessageEvent(MessageBean event) {
            if (event != null) {
                Log.e(TAG, event.toString());
            }
        }
    }
    

    收到消息后会打印出消息内容
    最后在SecondActivity中发送消息

    public class SecondActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_second);
    
            findViewById(R.id.tv_post).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    EventBus.getDefault().post(new MessageBean("SecondActivity","消息内容"));
                }
            });
        }
    }
    

    运行后,点击SecondActivity中的发送按钮后,打印log如下:

    com.cwq.eventbusdemo E/Main: MessageBean{param1='SecondActivity', param2='消息内容'}
    

    大功告成!
    当然了,跟正式的EventBus相比,我们都很多东西没有处理,比如注销、非主线程的消息处理、粘性消息、甚至新版的进程间通讯等等,这里仅仅实现最简单的功能,让大家清晰明白EventBus的工作方式,更多细节还要研习源码来学习,一个优秀的框架就是一个学习宝藏,都值得我们去借鉴和学习。

    相关文章

      网友评论

          本文标题:教你快速实现一个EventBus框架

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