Android 注解反射实现 EventButs

2019-05-21  本文已影响0人  程序员阿兵

背景:

Android组件之间通讯的方式有很多,广播 、intent 、handler、接口等这些都可以实现。网上开源框架EventBus是一款针对Android优化的发布/订阅事件总线。主要功能是替代Intent,Handler,BroadCast在Fragment,Activity,Service,线程之间传递消息.优点是开销小,代码更优雅。以及将发送者和接收者解耦。通过EventBus源码自己实现线程之间消息传递。

1.介绍EventButs的使用方法:

一:注册EventButs事件:
EventBus.getDefault().register(this);
二:监听EventButs事件:
 @Subscribe(threadMode = ThreadMode.MAIN)
    public void getEvent(EventBean bean){
        Log.e("====>", "thread = " + Thread.currentThread().getName());
        Log.e("======> ",bean.toString());
    }
三:发送EventButs事件:
   EventBus.getDefault().post(new EventBean("1111","22222"));

通过上面的代码就可以实现事件消息在组件之间的传递,下面我将不改变开源框架的代码格式,自己去实现EventButs:

第一步:定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,jvm在加载时可以通过反射的方式获取该注解的内容
public @interface Subscribe {

   ThreadMode threadMode() default ThreadMode.MAIN;
}

上面添加注解的枚举类:定义的两种类型一种指定主线程另一种为子线程

ublic enum ThreadMode {

    MAIN,
    BACKGROUND

}

第二步:注册事件
 public void register(Object obj){
        // 先去数据源中获取subcribleMethods,如果不存在,则去寻找方法并添加
        List<SubscribleMethod> list = cacheMap.get(obj);
        if(list == null){
            list = findSubscribleMethods(obj);
            cacheMap.put(obj,list);
        }
    }

上面的代码主要为从缓存中获取是否添加过监听,避免重复添加。

重点:

findSubscribleMethods 中主要将当前类以及父类中所有添加的注解一一遍历出来,获取注解类的回调方法、数据返回的线程模式、回调方法中的参数类 添加到map缓存 。具体代码如下:

    public List<SubscribleMethod> findSubscribleMethods(Object obj){
        List<SubscribleMethod> list = new ArrayList<>();
        Class<?> clazz = obj.getClass();
        // 循环去查找父类是否存在subscrible注解方法
        while (clazz != null){

            // 判断当前是否是系统类,如果是,就退出循环
            String name = clazz.getName();
            if(name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")){
                break;
            }

            // 得到所有的方法,这个clazz在本项目中,暂时指代的是MainActivity
            Method[] methods = clazz.getMethods();

            for(Method method : methods){
                // 通过注解找到我们需要注册的方法
                Subscribe subscribe = method.getAnnotation(Subscribe.class);
                if(subscribe == null){
                    continue;
                }
                // 获取方法中的参数,并判断
                Class<?>[] types = method.getParameterTypes();
                if(types.length != 1){
                    throw new RuntimeException("EventBus只能接受一个参数");
                }
                // 获取线程模式
                ThreadMode threadMode = subscribe.threadMode();
                SubscribleMethod subscribleMethod = new SubscribleMethod(method,threadMode,types[0]);
                list.add(subscribleMethod);
            }
            clazz = clazz.getSuperclass();
        }
        return list;
    }
第三步:发送事件

发生事件为根据当前的发送的对象获取缓存中的注解类,根据当前的线程调度通过反射去执行注解类方法,实现数据的传递。

    public void post(final Object type){
        Set<Object> set = cacheMap.keySet();
        Iterator<Object> iterator = set.iterator();
        while (iterator.hasNext()){
            final Object obj = iterator.next();
            List<SubscribleMethod> list = cacheMap.get(obj);
            for(final SubscribleMethod subscribleMethod : list){
                // 简单的理解:两个列对比一下,看看是否一致 (不严谨的说法)
                // a(subscribleMethod.getType())对象所对应的类信息,是b(type.getClass())对象所对应的类信息的父类或者父接口
                if(subscribleMethod.getType().isAssignableFrom(type.getClass())){
                    switch (subscribleMethod.getThreadMode()){
                        // 不管你在post是在主线程 还是在子线程,我都在主线程接受
                        case MAIN:
                            // 主 - 主
                            if(Looper.myLooper() == Looper.getMainLooper()){
                                invoke(subscribleMethod,obj,type);
                            }else{
                                // 子 - 主
                                mHandler.post(new Runnable() {
                                    @Override
                                    public void run() {
                                        invoke(subscribleMethod,obj,type);
                                    }
                                });
                            }
                            break;
                        case BACKGROUND:
                            // 主 - 子
                            if(Looper.myLooper() == Looper.getMainLooper()){
                                mExecutorService.execute(new Runnable() {
                                    @Override
                                    public void run() {
                                        invoke(subscribleMethod,obj,type);
                                    }
                                });
                            }else{
                                // 子 - 子
                                invoke(subscribleMethod,obj,type);
                            }
                            break;
                    }
                    
                }
            }
        }
    }

    private void invoke(SubscribleMethod subscribleMethod, Object obj, Object type) {
        Method method = subscribleMethod.getMethod();
        try {
            method.invoke(obj,type);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
image.png
 private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
            case POSTING:
                invokeSubscriber(subscription, event);
                break;
            case MAIN:
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case BACKGROUND:
                if (isMainThread) {
                    backgroundPoster.enqueue(subscription, event);
                } else {
                    invokeSubscriber(subscription, event);
                }
                break;
            case ASYNC:
                asyncPoster.enqueue(subscription, event);
                break;
            default:
                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }
    }
上一篇 下一篇

猜你喜欢

热点阅读