EventBus的使用及实现原理

1.EventBus的介绍与使用

1.1常见的组件间的通信方式

  • Intent:跳转+传参(局限性非常大)
  • Handler:通常用来更新主线程UI,使用不当容易出现内存泄露
  • Interface:接口回调,仅限于同一线程中数据交互
  • BroadcastReceiver:有序广播+无序广播
  • AIDL:跨进程通信,代码阅读性不友好,维护成本偏高

EventBus优点

  • 代码简单,快
  • Jar包小,~50k
  • Activity,Fragment以及线程通信优秀
  • 稳定,在一亿+应用中得到实践

1.2EventBus定义

EventBus是一个Android优化的publish/subscribe消息事件总线,简化了应用程序内各个组件间、组件与后台线程间的通信。如Activites、Fragments、Threads、Services

1.3使用场景

比如网络请求,返回时通过Handler或者BroadCastReceiver通知更新主线程UI

N个Fragment之间需要通过Listener(监听)通信

这些需求都可以通过EventBus完成和实现。可以在任何地方发布事件,任何地方订阅消费事件。完美的简化了跨组件、跨线程等方面通信的问题。

1.4官方架构图

EventBus的使用及实现原理

1.5简单使用

  • 导入EventBus库:implementation ‘org.greenrobot:eventbus:3.1.1’

  • 注册和注销

    @Override
    protected void onCreate() {
        super.onStart();
        EventBus.getDefault().register(this);
    }
    
    
    @Override
    protected void onDestroy() {
        super.onStop();
        if (EventBus.getDefault().isRegistered(this)) {
        	EventBus.getDefault().unregister(this);
        }
    }
    
  • 官方三步曲

    • 定义事件

      public class MessageEvent {
          /*Additional fields if needed*/
      }
      
    • 订阅事件(注解+方法指定参数)

      @Subscribe(threadMode = ThreadMode.MAIN)
      public void onMessageEvent(MessageEvent event) {
          /*Do something*/
          //监听到了发布的事件(消息)
          //进行相关操作,比如更新UI等
      }
      
    • 发步事件(发送潇潇)

      EventBus.getDefault().post(new MessageEvent());
      
  • EventBus粘性事件(@Subscribe注解中加入:sticky = true 作用:延时消费或者初始化)

    @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
    public void onMessageEvent(UserInfo user) {
        Log.d(TAG, user.toString());
        /*Do something*/
        //监听到了发布的事件(消息)
        //进行相关操作,比如更新UI等
        //如果请求接口然后再发送事件再进行控件的更新。有时候该控件所在的页面可能没有初始化好。	//这时候eventbus所发送的事件就不会起作用。这时候就要用到粘性事件。粘性事件可以先发送     //事件,待接收方订阅后接受事件。其实就是解决异步所带来的问题
    }
    
  • 订阅方法执行的优先级(如果两个或多个地方订阅同一事件,优先级高的先接收)

    @Subscribe(threadMode = ThreadMode.MAIN, priority = 1)
    public void onMessageEvent(UserInfo user) {
        //消费事件/消息
        textView.setText(user.toString());
        Log.d(TAG, "onMessageEvent");
    }
    
    //priority数值越大,优先级越高,默认都为0
    @Subscribe(threadMode = ThreadMode.MAIN, priority = 5)
    public void onMessageEvent(UserInfo user) {
        //消费事件/消息
        textView.setText(user.toString());
        Log.d(TAG, "onMessageEvent");
    }
    

2.手写EventBus

2.1四个类实现EventBus

  • ThreadMode:创建线程模式
  • Subscribe:创建注解
  • EventBus:封装方法类
  • MethodManager:“存储”方法,并通过反射进行调用
2.1.1创建线程模式
public enum ThreadMode {
    //事件的处理和事件的发送在相同的线程,所以事件处理时间不能太长,不然影响事件的发送线程,而这个线程可能是UI线程
    POSTING,

    //事件的处理在主线程中执行,事件处理不应耗时太长
    MAIN,

    //后台进程,处理如保存数据等操作
    BACKGROUND,

    //异步执行,另起线程操作,事件处理会在单独的线程中执行,主要用于在后台线程中执行耗时操作
    ASYNC
}

2.1.2创建注解
@Target(ElementType.METHOD)//作用在方法之上
@Retention(RetentionPolicy.RUNTIME)//jvm在运行时通过反射获取注解的值
public @interface Subscribe {
    ThreadMode threadMode() default ThreadMode.POSTING;
}
2.1.3封装方法类
/**
 * 保存符合要求订阅方法的封装类
 */
public class MethodManager {

    //参数类型
    private Class<?> type;
    //线程模式
    private ThreadMode threadMode;
    //执行订阅方法
    private Method method;

    public MethodManager(Class<?> type, ThreadMode threadMode, Method method) {
        this.type = type;
        this.threadMode = threadMode;
        this.method = method;
    }


    public Class<?> getType() {
        return type;
    }

    public void setType(Class<?> type) {
        this.type = type;
    }

    public ThreadMode getThreadMode() {
        return threadMode;
    }

    public void setThreadMode(ThreadMode threadMode) {
        this.threadMode = threadMode;
    }

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }

    @Override
    public String toString() {
        return "MethodManager{" +
                "type=" + type +
                ", threadMode=" + threadMode +
                ", method=" + method +
                '}';
    }

}
2.1.4“存储”,方法,并通过反射进行调用
public class EventBus {

    private static volatile EventBus instance;

    //用来保存带注解方法(满足订阅条件的方法)
    private Map<Object, List<MethodManager>> cacheMap;

    private EventBus() {
        cacheMap = new HashMap<>();

    }

    public static EventBus getDefault() {
        if (instance == null) {
            synchronized (EventBus.class) {
                if (instance == null) {
                    instance = new EventBus();
                }
            }
        }
        return instance;
    }

    //收到信息方法
    //找到MainActivity所有符合注解的方法
    public void register(Object getter) {
        List<MethodManager> methodList = cacheMap.get(getter);
        if (methodList == null) {
            methodList = findAnnotationMethod(getter);
            //key代表MainActivity标识,value代表MainActivity中符合注解的方法的集合
            cacheMap.put(getter, methodList);
        }
    }

    /**
     * 通过注解找到对应的方法
     * @return
     */
    private List<MethodManager> findAnnotationMethod(Object getter) {
        List<MethodManager> methodList = new ArrayList<>();
        Class<?> clazz = getter.getClass();
//        Method[] methods = clazz.getDeclaredMethods();//获取当前类的所有方法
        Method[] methods = clazz.getMethods();//获取当前类和父类的所有方法
        //采用第一种方案就没有问题,采用第二种方案可以进一步优化
        while (clazz != null) {
            String clazzName = clazz.getName();
            if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) {
                break;
            }

            for (Method method : methods) {
                Subscribe subscribe = method.getAnnotation(Subscribe.class);
                if (subscribe == null) {
                    continue;
                }
                //校验方法是否满足条件
                //校验方法返回值类型
                Type returnType = method.getGenericReturnType();
                if (!"void".equals(returnType.toString())) {
                    throw new RuntimeException(method.getName() + "方法返回值类型必须是 void");
                }
                //校验方法参数
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length != 1) {
                    throw new RuntimeException(method.getName() + "方法参数有且仅有一个");
                }
                //更多条件的校验
                //...
                //完全符合要求,添加到集合
                MethodManager manager = new MethodManager(parameterTypes[0], subscribe.threadMode(), method);
                methodList.add(manager);
            }
            //不断循环找出父类含有订阅方法,直至为空
            clazz = clazz.getSuperclass();
        }
        return methodList;
    }

    //发送操作,发布事件
    public void post(Object setter) {
        Set<Object> set = cacheMap.keySet();
        //获取MainActivity对象,即注册要发布消息的Activity或Fragment对象
        for (Object getter : set) {
            //获取MainActivity中所有带注解的方法
            List<MethodManager> managerList = cacheMap.get(getter);
            if (managerList != null) {
                //循环执行每个订阅方法
                for (MethodManager manager : managerList) {
                    //isAssignableFrom(用来匹配发布者和订阅者参数是否一致)
                    if (manager.getType().isAssignableFrom(setter.getClass())) {
                        invoke(manager, getter, setter);
                    }
                }
            }
        }
    }

    /**
    * 通过反射调用订阅(满足条件的带注解)的方法
    */
    private void invoke(MethodManager manager, Object getter, Object setter) {
        Method method = manager.getMethod();
        try {
            method.invoke(getter, setter);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

demo链接:https://github.com/mitufengyun/EventBusDemo

上一篇:Vue组件之间的通信(上)


下一篇:Guava----EventBus