美文网首页
移动架构<第七篇>:手写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