EventBus

小记

       之前在公司写的一个Demo,埋头只顾着实现需求,本想着一个Demo 就演示给别人看看的也没必要写多好,等到时候确认要的时候再去搞的好一点,谁知道后期直接就从demo要变成开发代码。我这一手MVC写的代码如何是好,想改成MVP吧时间上又有点赶紧,最后搞了一个EventBus来假装自己是MVP。具体就是把所有的逻辑放到一块去处理,然后根据Type去区分逻辑的处理,最后通过EventBus 通知到对应的页面。还是要养成敲代码的好习惯。

EventBus的使用

        这是官方的地址,依赖,使用方法说的很清楚,这里就不多说。
https://github.com/greenrobot/EventBus
偷张图

EventBus_第1张图片
EventBus.png

        Publisher post 出来消息,然后进入EventBus这个黑盒,然后就转给了Subscriber,谁订阅了 就给谁返回,完成了一次Event的传递。

EventBus的实现

       今儿就对EventBus的这个中间的黑盒进行研究。结合他的代码。首先来看一下EventBus的成员。

EventBus_第2张图片
EventBus成员.png

       1. getDefault() 这显然是个单例,然后调用了regist()方法 和 unregister()这两个方法。这里有个注意点就是register()和unregister这两个方法里面的参数都是Object,很多时候在Activity中用到很多的 this都是当 context 来用的,带来的问题就是在fragment 中其实传入的也要是页面本身,即当前的Fragment,这种就是错误的

   EventBus.getDefault().register(mContext);

正确的为

    EventBus.getDefault().register(this);

       2. @SubScirbe
这是一个JAVA注解 点进去一看

EventBus_第3张图片
SubScribe.png

里面又冒出个ThreadMode , default ThreadMode.POSTING;点击查看之后又是一个枚举,然后后面两个Stick和Priority分别代表是否是粘性事件跟优先级。
       3. ThreadMode 其实就有几种线程模式。点击可以直接查看一下一共有几种还有介绍。查看之后发现就是 POSTINGMAIN等。
       4. 最后就是post这个方法里面是一个Object类相当于把所需要发的数据丢出去,官方意思是写一个实体因为这能发送多个数据,都在一个实体里面。这里也不支持多个参数的方法,在后面介绍就知道是为什么了。
       更多详细的EventBus为什么实现可以看一下源码。下面开始抄袭。

实现简单的EventBus

       首先回顾一下上面的流程基本上可以理解为 注册--->post--->@Subscriber方法处理--->取消注册。

  • 线程类型
           就是在什么线程进行回调,ThreadMode 这里是个枚举。这里只写两,实际还有BACKGROUND等。

     public enum ThreadMode {
        POSTING,
        MAIN
      }
    
  • Subscriber注解
            搞这个注解就是为了方便后面回调这个方法方便。可以理解为一个标记,通过@Subscriber来进行方法的查找。

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

       @Target 代表的是在什么上面使用,@Retention代表的是在什么时候使用。

  • 存储实体 SubscribeMethod(随便取的名字)
           里面有一个ThreadMode,一个Method(后面调用.invoke方法用的),还有一个Class,即自己的类型,用实体对象来存所有register过的Object。

           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;
        }
       //get() Set() 方法省          
     }
    
  • EventBus方法
            首先是单例

      private static volatile MyEventBus instance;
      private MyEventBus() {
        cacheMap = new HashMap<>();
      }
      public static MyEventBus getDefault() {
          if (instance == null) {
             synchronized (MyEventBus.class) {
                 if (instance == null) {
                     instance = new MyEventBus();
                }
            }
        }
         return instance;
     }
    

        其次看一下register方法

               private Map> cacheMap;
               public void register(Object object) {
                    List list = cacheMap.get(object);
                      //判断是否 存过
                    if (list == null) {
                    list = findSubcirbeMethod(object);
                    cacheMap.put(object, list);
                  }
           }

       首先准备了一个map ,key为 register进来的对象,value 为SubscribeMethod列表,List list = cacheMap.get(object);先取目的是为了去除重复的,假如不为null 说明已经register 不重复注册。findSubcirbeMethod(Object obj)方法来进行查找这个Object中有多少个被@Subscriber注解的方法,全部取出来进行分类。

private List findSubcirbeMethod(Object object) {
    List list = new ArrayList<>();
    Class clazz = object.getClass();
    //这个只负责自己的方法   getMethod 包含父类的方法
    Method[] methods = clazz.getDeclaredMethods();
    Log.e(TAG, "findSubcirbeMethod: " + methods.toString());
    //寻找带有注解的的Subscribe的注解
    while (clazz != null) {
    //去除系统的包里面的东西
         String name = clazz.getName();
         if(name.startsWith("java.")||name.startsWith("javax.")||name.startsWith("android.")){
             break;
         }
        for (Method method : methods) {
            //这里选得到的是哪个
            //override 为什么没有用 因为RetentionPolicy.SOURCE 无效
           //取出注解的@Subscribe的方法
            Subscribe subscribe = method.getAnnotation(Subscribe.class);
            if (subscribe == null) {
                //为空就继续
                continue;
            }
            //判断带有subScribe中的参数类型
           //这里判断的是 返回的参数类型,一般是一个实体,所以不能搞两个参数的。省得麻烦。
            Class[] types = method.getParameterTypes();
            if (types.length != 1) {
                Log.e(TAG, "findSubcirbeMethod: type!=1+  Exception");
            }
            //获取线程的模式
            ThreadMode threadMode = subscribe.threadMode();
           //构建SubscribeMethod 对象,传入方法,线程模式,构造参数。
            SubscribeMethod subscribeMethod = new SubscribeMethod(method, threadMode, types[0]);
           //添加到列表 因为一个页面可能有多个
            list.add(subscribeMethod);
        }
        //父类的查找,防止写一些BaseActivity 
        clazz = clazz.getSuperclass();
    }
    return list;
}

这里得到的是一个List,下面看一下post(Object obj )方法:

   public void post(final Object object) {
     //直接从cache里面取出就行
    Set set  = cacheMap.keySet();
    Iterator iterator = set.iterator();
    while(iterator.hasNext()){
        final Object obj = iterator.next();
        List list = cacheMap.get(obj);
        for(final SubscribeMethod subscribeMethod:list){
            
          /**
             * 这里判断的是 subscribeMethod.getType() 是不是等于 post里面的"实体",
             * 等于才能发送,一个Activity中完全可能有多个@Subscribe注册的方法。
             */
            if(subscribeMethod.getType().isAssignableFrom(object.getClass())){
                switch (subscribeMethod.getThreadMode()){
               //这里判断线程是什么,然后进行对应的线程切换,用什么切线程各显神通。Handler ,Rx 都可以。 
                    case MAIN:
                        if(Looper.myLooper()  == Looper.getMainLooper()){
                            Log.e(TAG, "post: 主线程");
                            notifySetDataChanged(subscribeMethod,obj,object);
                        }else{
                            Log.e(TAG, "post: 子线程");
                            handler.post(new Runnable() {
                                @Override
                                public void run() {
                                    notifySetDataChanged(subscribeMethod,obj,object);
                                }
                            });
                        }
                        break;
                    case POSTING:
                        //切换到子线程
                        break;
                }
            }
        }
    }
}

notifySetDataChanged方法:

private void notifySetDataChanged(SubscribeMethod subscribeMethod, Object obj, Object object) {
    //获取到方法。
    Method method = subscribeMethod.getMethod();
    try {
        //method.invoke() 见 https://blog.csdn.net/weixin_42124622/article/details/81909180
        method.invoke(obj,object);
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
}

这样就可以实现EventBus 的效果。

后记

       其实这个EventBus 其实还有很多的内容,这里只是写了一个大概,具体还有很多的功能没有做,只是记录一下学习的过程,看别人开源的框架,然后再抄一个出来的话,其实还是有不少好处的,一个好的开源框架着实用到了很多平时开发中不常使用的东西,比如这个EventBus用到了反射跟注解,正好可以再学习一波巩固一下知识点。后面有时间再来补充其他的EventBus里面的实现内容。

       学习资料来自于网易公开课。

你可能感兴趣的:(EventBus)