美文网首页
移动架构<第七篇>:手写EventBus架构

移动架构<第七篇>:手写EventBus架构

作者: NoBugException | 来源:发表于2020-05-31 21:50 被阅读0次

    [线程模式]

    这个比较简单,直接复制ThreadMode源码即可,最后修改文件名和枚举名称。

    public enum NoThreadMode {
        /**
         * Subscriber will be called directly in the same thread, which is posting the event. This is the default. Event delivery
         * implies the least overhead because it avoids thread switching completely. Thus this is the recommended mode for
         * simple tasks that are known to complete in a very short time without requiring the main thread. Event handlers
         * using this mode must return quickly to avoid blocking the posting thread, which may be the main thread.
         */
        POSTING,
    
        /**
         * On Android, subscriber will be called in Android's main thread (UI thread). If the posting thread is
         * the main thread, subscriber methods will be called directly, blocking the posting thread. Otherwise the event
         * is queued for delivery (non-blocking). Subscribers using this mode must return quickly to avoid blocking the main thread.
         * If not on Android, behaves the same as {@link #POSTING}.
         */
        MAIN,
    
        /**
         * On Android, subscriber will be called in Android's main thread (UI thread). Different from {@link #MAIN},
         * the event will always be queued for delivery. This ensures that the post call is non-blocking.
         */
        MAIN_ORDERED,
    
        /**
         * On Android, subscriber will be called in a background thread. If posting thread is not the main thread, subscriber methods
         * will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single
         * background thread, that will deliver all its events sequentially. Subscribers using this mode should try to
         * return quickly to avoid blocking the background thread. If not on Android, always uses a background thread.
         */
        BACKGROUND,
    
        /**
         * Subscriber will be called in a separate thread. This is always independent from the posting thread and the
         * main thread. Posting events never wait for subscriber methods using this mode. Subscriber methods should
         * use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number
         * of long running asynchronous subscriber methods at the same time to limit the number of concurrent threads. EventBus
         * uses a thread pool to efficiently reuse threads from completed asynchronous subscriber notifications.
         */
        ASYNC
    }
    

    [自定义注解]

    和EventBus类似,保留线程模式以及粘贴事件

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface NoSubscribe {
        //线程模式
        NoThreadMode threadMode() default NoThreadMode.POSTING;
        //粘贴性事件开关
        boolean sticky() default false;
    }
    

    [方法信息的封装]

    定义一个方法,用于存储方法、线程模式以及形式参数类型。

    /**
     * 记录方法的信息
     */
    public class SubscribleMethod {
        //注册方法
        private Method method;
        //线程模式
        private NoThreadMode threadMode;
        //参数类型
        private Class<?> eventType;
    
        public SubscribleMethod(Method method, NoThreadMode threadMode, Class<?> eventType) {
            this.method = method;
            this.threadMode = threadMode;
            this.eventType = eventType;
        }
    
        public Method getMethod() {
            return method;
        }
    
        public void setMethod(Method method) {
            this.method = method;
        }
    
        public NoThreadMode getThreadMode() {
            return threadMode;
        }
    
        public void setThreadMode(NoThreadMode threadMode) {
            this.threadMode = threadMode;
        }
    
        public Class<?> getEventType() {
            return eventType;
        }
    
        public void setEventType(Class<?> eventType) {
            this.eventType = eventType;
        }
    }
    

    [NoEventBus代码]

    NoEventBus是核心代码,封装了注册、反注册、单例、发送事件、发送粘贴事件、事件处理、粘贴性事件处理、线程模式的处理

    public class NoEventBus {
    
        //存储多个被NoSubscribe修饰的方法,一个对象可对应多个方法
        private Map<Object, List<SubscribleMethod>> cacheMap;
        //存储粘贴事件
        private Map<Object, Class<?>> stickyEvents;
    
        private Handler handler;
    
        //线程池
        private ExecutorService newCachedThreadPool, newSingleThreadExecutor;
    
        private NoEventBus(){
            this.cacheMap = new HashMap<>();
            this.stickyEvents = new ConcurrentHashMap<>();
            handler = new Handler(Looper.getMainLooper());
            //并发处理事件
            newCachedThreadPool = Executors.newCachedThreadPool();
            //并行处理事件
            newSingleThreadExecutor = Executors.newSingleThreadExecutor();
        }
    
        static class NoEventBusHolder{
            public static NoEventBus instance = new NoEventBus();
        }
    
        /**
         * 获取NoEventBus对象,这个对象必须是单例,因为需要保证cacheMap在内存中唯一
         * @return
         */
        public static NoEventBus getDefault(){
            return NoEventBusHolder.instance;
        }
    
        /**
         * 注册:其实就是将被NoSubscribe修饰的方法对象存储到cacheMap集合中
         * @param subscriber 注册时绑定的对象
         */
        public void register(Object subscriber) {
            List<SubscribleMethod> subscribleMethods = cacheMap.get(subscriber);
            //如果已经注册,就不需要注册
            if (subscribleMethods == null) {
                subscribleMethods = getSubscribleMethods(subscriber);
                cacheMap.put(subscriber, subscribleMethods);
    
                //粘贴事件处理
                stickyEvent(subscriber);
            }
        }
    
        /**
         * 获取当前对象下接收事件的方法信息的集合
         * @param subscriber
         * @return
         */
        private List<SubscribleMethod> getSubscribleMethods(Object subscriber) {
            List<SubscribleMethod> list = new ArrayList<>();
            Class<?> aClass = subscriber.getClass();
            while (aClass != null) {
                Method[] declaredMethods = aClass.getDeclaredMethods();
                for (Method method : declaredMethods) {
                    NoSubscribe annotation = method.getAnnotation(NoSubscribe.class);
                    if (annotation == null) {
                        continue;
                    }
    
                    //校验方法参数的合法性,被NoSubscribe修饰的方法有且只有一个形式参数
                    Class<?>[] parameterTypes = method.getParameterTypes();
                    if (parameterTypes.length != 1) {
                        throw new RuntimeException("NoEventBus只能有一个形式参数!");
                    }
    
                    //获取线程模式
                    NoThreadMode NoThreadMode = annotation.threadMode();
                    SubscribleMethod subscribleMethod = new SubscribleMethod(method, NoThreadMode, parameterTypes[0]);
                    list.add(subscribleMethod);
                }
                aClass = aClass.getSuperclass();
            }
            return list;
        }
    
    
        /**
         * 粘贴事件处理
         */
        private void stickyEvent(Object subscriber){
    
            Class<?> aClass = subscriber.getClass();
            while (aClass != null) {
                Method[] declaredMethods = aClass.getDeclaredMethods();
                for (Method method : declaredMethods) {
                    NoSubscribe annotation = method.getAnnotation(NoSubscribe.class);
                    if (annotation == null) {
                        continue;
                    }
    
                    //校验方法参数的合法性,被NoSubscribe修饰的方法有且只有一个形式参数
                    Class<?>[] parameterTypes = method.getParameterTypes();
                    if (parameterTypes.length != 1) {
                        throw new RuntimeException("NoEventBus只能有一个形式参数!");
                    }
    
                    Class<?> eventType = parameterTypes[0];
    
                    //校验粘贴性方法开关是否打开
                    if(annotation.sticky()){
                        Set<Map.Entry<Object, Class<?>>> entries = stickyEvents.entrySet();
                        for (Map.Entry<Object, Class<?>> entry : entries) {
                            Class<?> candidateEventType = entry.getValue();
                            if (eventType.isAssignableFrom(candidateEventType)) {
                                Object stickyEvent = entry.getKey();
                                if(stickyEvent != null){
                                    //发送事件
                                    post(stickyEvent);
                                }
                            }
                        }
                    }
    
                }
                aClass = aClass.getSuperclass();
            }
        }
    
        /**
         * 反注册:其实就是将对应对象的方法集合从集合中删除
         * @param subscriber
         */
        public void unregister(Object subscriber) {
            List<SubscribleMethod> list = cacheMap.get(subscriber);
            //如果获取到
            if (list != null) {
                cacheMap.remove(subscriber);
            }
            //清除粘贴事件
            //stickyEvents.clear();
        }
    
        /**
         * 发送事件
         * @param obj
         */
        public void post(final Object obj) {
            Set<Object> set = cacheMap.keySet();
            Iterator<Object> iterator = set.iterator();
            while (iterator.hasNext()) {
                //拿到注册类
                final Object next = iterator.next();
                //获取类中所有添加注解的方法
                List<SubscribleMethod> list = cacheMap.get(next);
                for (final SubscribleMethod subscribleMethod : list) {
                    //判断这个方法是否应该接收事件,isAssignableFrom类似于instanceof
                    if (subscribleMethod.getEventType().isAssignableFrom(obj.getClass())) {
                        switch (subscribleMethod.getThreadMode()) {
                            case MAIN://接收方法在主线程种情况,事件的处理是阻塞性的,也就是按照顺序梳理
                            case MAIN_ORDERED://接收方法在主线程种情况,事件的处理是非阻塞性的,由于在主线程上,技术上应该无法做到让事件的处理非阻塞,所以就默认认为和MAIN一致
                                //如果接收方法在主线程执行的情况
                                if(Looper.myLooper() == Looper.getMainLooper()){
                                    invoke(subscribleMethod, next, obj);
                                } else {
                                    //post方法执行在子线程中,接收消息在主线程中
                                    handler.post(new Runnable() {
                                        @Override
                                        public void run() {
                                            invoke(subscribleMethod, next, obj);
                                        }
                                    });
                                }
                                break;
    
                            case ASYNC://接收方法在子线程种情况,适合做耗时操作
                                //post方法执行在主线程中
                                if(Looper.myLooper() == Looper.getMainLooper()){
                                    newCachedThreadPool.execute(new Runnable() {
                                        @Override
                                        public void run() {
                                            invoke(subscribleMethod, next, obj);
                                        }
                                    });
                                } else {
                                    //post方法执行在子线程中
                                    invoke(subscribleMethod, next, obj);
                                }
                                break;
                            case BACKGROUND://接收方法在子线程种情况,由于消息是按顺序处理的,所以不适合做耗时操作
                                //post方法执行在主线程中
                                if(Looper.myLooper() == Looper.getMainLooper()){
                                    newSingleThreadExecutor.execute(new Runnable() {
                                        @Override
                                        public void run() {
                                            invoke(subscribleMethod, next, obj);
                                        }
                                    });
                                } else {
                                    //post方法执行在子线程中
                                    invoke(subscribleMethod, next, obj);
                                }
                                break;
                            case POSTING:
                                //默认
                                break;
                        }
    
                    }
                }
            }
        }
    
        /**
         * 发送粘贴性事件
         * @param event
         */
        public void postSticky(Object event) {
            synchronized (stickyEvents) {
                stickyEvents.put(event, event.getClass());
            }
    
            post(event);
        }
    
        /**
         * 通过反射,执行对应的方法
         * @param subscribleMethod
         * @param next
         * @param obj
         */
        private void invoke(SubscribleMethod subscribleMethod, Object next, Object obj) {
            Method method = subscribleMethod.getMethod();
            try {
                method.invoke(next, obj);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    
    }
    

    以上代码就是手写EventBus架构的全部代码了,可以直接使用。

    另外,如果项目中使用了RxJava,那么可以自己封装一个RxBus架构,和EventBus架构原理一致。

    [本章完...]

    相关文章

      网友评论

          本文标题:移动架构<第七篇>:手写EventBus架构

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