美文网首页
EventBus探索和实现

EventBus探索和实现

作者: 文子产品笔记 | 来源:发表于2018-12-25 16:10 被阅读0次

    今天这里实现的是一个简化版本的EventBus,实现组件和控件之间的通信,不涉及线程切换。

    首先了解下什么是EventBus:

    EventBus是一种用于Android的发布/订阅事件总线。它有很多优点:简化应用组件间的通信;解耦事件的发送者和接收者;避免复杂和容易出错的依赖和生命周期的问题;很快,专门为高性能优化过等等。

    EventBus使用了发布者/订阅者模式,的基本原理可以用一张图表示:

    D9D1414D5CB8A1B89A1C841C767565B4.jpg

    事件的发布者通过post将事件发送到eventbus事件缓存池中,然后事件缓存池找到相应注册的事件,通过函数的invoke将事件发送到每一个订阅者。

    EventBus实现的三要素:
    1, 事件
    2,事件订阅者
    3,事件发布者

    事件:其实就是一个消息,其实就是一个model对象,用于存储我们想要存储的内容,事件氛围一般事件和sticky事件,相对于一般事件,sticky事件不同之处在于,当事件发布后,再有订阅者订阅该类型事件,依然能收到该类型事件最近一个sticky事件。

    订阅者:订阅某个事件类型的对象。当某个事件发布后,Eventbus会执行订阅该事件的onEvent函数,这个函数叫做事件响应函数。例如下面,Subscribe是订阅事件的关键字:

        @Subscribe(threadMode = ThreadMode.MAIN)
        public void onMessageEvent(MessageEvent event) {
            Log.i(TAG, "message is " + event.getMessage());
            // 更新界面
            mTvMessage.setText(event.getMessage());
        } 
    

    发布者:发布某个事件的对象,通过post接口发布事件。

    EventBus.getDefault().post(messageEvent);
    

    EventBus的四种线程模型:

    POSTING (默认): 表示事件处理函数的线程跟发布事件的线程在同一个线程。
    MAIN :表示事件处理函数的线程在主线程(UI)线程,因此在这里不能进行耗时操作。
    BACKGROUND :表示事件处理函数的线程在后台线程,因此不能进行UI操作。如果发布事件的线程是主线程(UI线程),那么事件处理函数将会开启一个后台线程,如果果发布事件的线程是在后台线程,那么事件处理函数就使用该线程。
    ASYNC :表示无论事件发布的线程是哪一个,事件处理函数始终会新建一个子线程运行,同样不能进行UI操作。

    注意点:

    (1)如果某个类里面的订阅者想要接收发布的事件,需要在onCreate中注册事件。

     //这句话内部实现了查找该类中所有订阅者方法,并且添加都订阅者事件执行表中。
    EventBus.getDefault().register(this);     
    

    (2)某个类注册了事件,在onDestroy中必须要销毁,不然会引起内存泄漏。

    //这句话内部实现的就是在订阅者事件执行表中查找该类所有的订阅事件,并且移除这些事件。
    EventBus.getDefault().unregister(this);
    

    我在网上找到了两张订阅事件和发布事件的流程图,如下:

    3DEC439453715E508DB4216F6A3F1788.jpg 3B7ABD017D12BD108764426A2C019A6F.jpg

    大概就是这个样子了,用法很简单,但EventBus考虑的比较全面,今天我这边只想根据它的原理实现一个精简版的事件发送和订阅功能。

    首先,定义一个注解类,用于注册消息订阅者:

    /**
     * Created by dcw on 2018/12/24.
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Subscribe {
        String[] value();    //这里value里面接受的是订阅者的识别标签
    }
    

    第二步:定义一个订阅者方法保存类,这个类的作用就是用于保存所有的订阅事件的响应函数,这里保存的三个变量,一个是识别标签,一个是方法,一个是方法的参数类型。

    /**
     * Created by dcw on 2018/12/24.
     */
    public class SubscribeMethod {
    
        private String label;            //标签
        private Method method;           //方法
        private Class[] parmterClass;    //类型的参数
    
        public SubscribeMethod(String label, Method method, Class[] parmterClass) {
            this.label = label;
            this.method = method;
            this.parmterClass = parmterClass;
        }
    
        public String getLabel() {
            return label;
        }
    
        public Class[] getParmterClass() {
            return parmterClass;
        }
    
        public Method getMethod() {
            return method;
        }
    }
    

    第三步:定义一个Subscription类,这个类用于保存SubscribeMethod和SubscribeMethod所归属的类名称,方便后期查找。

    /**
     * Created by dcw on 2018/12/24.
     */
    public class Subscription {
    
        SubscribeMethod subscribeMethod;
        Object subscribe;
    
        public Subscription(SubscribeMethod subscribeMethod, Object subscribe) {
            this.subscribeMethod = subscribeMethod;
            this.subscribe = subscribe;
        }
    
        public Object getSubscribe() {
            return subscribe;
        }
    
        public SubscribeMethod getSubscribeMethod() {
            return subscribeMethod;
        }
    }
    
    

    最后写我们的DDBus主要类,这个类我注释得很详细,这里就不多解释了:

    package com.ivalleytech.ddbus;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Map;
    
    /**
    * Created by dcw on 2018/12/24.
    */
    public class DDBus {
    
       private static DDBus instance;
       private Map<Class,List<SubscribeMethod>> METHOD_CACHE = new HashMap<>();    //以类名归类,用于缓存每个类中所有注册的响应方法,一个标签一个方法
       private Map<String,List<Subscription>> SUBSCRIBES = new HashMap<>();        //以标签归类,存储一个标签下所有方法
       private Map<Class,List<String>> REGISTER = new HashMap<>();                 //存储这个类下所有的标签,用于协助注销类下的所有方法
    
       public static DDBus getInstance(){
           if (null == instance){
               synchronized (DDBus.class){
                   if (null == instance){
                       instance = new DDBus();
                   }
               }
           }
           return instance;
       }
    
    
       //注册订阅者
       public void registe(Object object){
           Class<?> subcribeClass = object.getClass();
           //找到对于类中所有被Subscribe标注的函数
           //将其Method,Label,执行函数需要的参数类型数组缓存
           List<SubscribeMethod> subscribes = findSubscribe(subcribeClass);
    
           //为了方便注销
           List<String> labels = REGISTER.get(subcribeClass);
           if (null == labels){
               labels = new ArrayList<>();
               REGISTER.put(subcribeClass,labels);
           }
    
           //执行表制作
           for (SubscribeMethod subscribeMethod:subscribes){
               //拿到标签
               String label = subscribeMethod.getLabel();
               if (!labels.contains(label)){
                   labels.add(label);
               }
    
               //执行表数据是否存在
               List<Subscription> subscriptions = SUBSCRIBES.get(label);
               if (null == subscriptions){
                   subscriptions = new ArrayList<>();
                   SUBSCRIBES.put(label,subscriptions);
               }
               subscriptions.add(new Subscription(subscribeMethod,object));
           }
       }
    
       private List<SubscribeMethod> findSubscribe(Class<?> subcribeClass) {
           //先检测缓存中是否有这个注册
           List<SubscribeMethod> subscribeMethods = METHOD_CACHE.get(subcribeClass);
           //如果缓存中没有
           if (null == subscribeMethods){
               subscribeMethods = new ArrayList<>();
               //获得所有methods
               Method[] methods = subcribeClass.getDeclaredMethods();
               for (Method method: methods){
                   Subscribe subscribe = method.getAnnotation(Subscribe.class);
                   if (null!=subscribe){
                       String[] values = subscribe.value();
                       //获得这个函数的参数类型
                       Class<?>[] parameterTypes = method.getParameterTypes();
                       for (String value:values) {
                           //设置权限,方法可访问
                           method.setAccessible(true);
                           subscribeMethods.add(new SubscribeMethod(value,method,parameterTypes));
                       }
                   }
               }
               METHOD_CACHE.put(subcribeClass,subscribeMethods);
           }
           return subscribeMethods;
       }
    
       /**
        * 发送事件给订阅者
        * @param lable
        */
       public void post(String lable, Object... params) {
           // params
           List<Subscription> subscriptions = SUBSCRIBES.get(lable);
           if (null == subscriptions) {
               return;
           }
    
           for (Subscription subscription : subscriptions) {
               SubscribeMethod subscribeMethod = subscription.getSubscribeMethod();
               //执行函数需要的参数类型数组
               Class[] paramterClass = subscribeMethod.getParmterClass();
               //真实的参数
               Object[] realParams = new Object[paramterClass.length];
               if (null != params) {
                   for (int i = 0; i < paramterClass.length; i++) {
                       //传进来的参数 类型是method需要的类型
                       if (i < params.length && paramterClass[i].isInstance(params[i])) {
                           realParams[i] = params[i];
                       } else {
                           realParams[i] = null;
                       }
                   }
               }
               try {
                   subscribeMethod.getMethod().invoke(subscription.getSubscribe(), realParams);
               } catch (IllegalAccessException e) {
                   e.printStackTrace();
               } catch (InvocationTargetException e) {
                   e.printStackTrace();
               }
           }
       }
    
       //清空所有
       public void clear(){
           METHOD_CACHE.clear();
           SUBSCRIBES.clear();
           REGISTER.clear();
       }
    
       //反注册销毁
       public void unregister(Object object) {
           //对应对象类型的所有 注册的标签
           List<String> labels = REGISTER.get(object.getClass());
           if (null != labels){
               for (String label : labels) {
                   //获得执行表中对应label的所有函数
                   List<Subscription> subscriptions = SUBSCRIBES.get(label);
                   if (null != subscriptions){
                       Iterator<Subscription> iterator = subscriptions.iterator();
                       while (iterator.hasNext()){
                           Subscription subscription = iterator.next();
                           //对象是同一个才删除
                           if (subscription.getSubscribe() == object){
                               iterator.remove();
                           }
                       }
                   }
               }
           }
       }
    }
    
    上面涉及到三个数据结构,分别是缓存表(METHOD_CACHE),执行表(SUBSCRIBES),注销表(REGISTER),他们之间的结构如下: 03875C01FD80F54DEDCD7489D430B944.jpg

    MainActivity中执行如下:

    
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            DDBus.getInstance().registe(this);
    
            //发送事件给带1标签的订阅者
            DDBus.getInstance().post("1","123","213");
        }
    
        //订阅1,2,3标签的事件
        @Subscribe({"1","2","3"})
        private void test(String msg,String msg1){
            Log.e("==","===MSG:"+msg+"===msg1:"+msg1);
        }
    
        @Subscribe({"1","2"})
        private void test1(String msg,Integer msg1){
            Log.e("==","MSG:"+msg+"===msg1:"+msg1);
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
    
            //注销事件
            DDBus.getInstance().unregister(this);
        }
    }
    
    

    大概就是这样,一个简单的EventBus实现,这里也没有实现线程切换,如果想要实现可以在Subscription加一个标注,然后在

    subscribeMethod.getMethod().invoke(subscription.getSubscribe(), realParams);
    

    中取出subscription标注判断进行切换即可,原理就是这样,所有代码都贴在这里了,如果有疑问欢迎来讨论。

    相关文章

      网友评论

          本文标题:EventBus探索和实现

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