Android EventBus3源码解析(下)

Android EventBus3源码解析(上):讲解了订阅者注册注销原理。
Android EventBus3源码解析(中):讲解了事件发送接收原理。
本文主要讲解EventBus创建原理、反射的不足及解决方案。

EventBus创建

        学会了EventBus的基本操作后,相信大家都注意到大部分时候我们都是在用默认的EventBus来完成任务的,例如下面这样。

 EventBus.getDefault().postSticky(new StrMessage("incoming message"));

当然,我们也可以对EventBus进行定制。那么EventBus的创建是怎样实现的呢?EventBus的创建代码如下。

EventBus.builder()
                .addIndex(...)
                .executorService(...)
                .logger(...)
                .build();

很明显,这里用到了建造者模式。这就是建造者模式的优势所在,可以对一个对象进行深度定制。看看源码。

...
	public static EventBusBuilder builder() {
        return new EventBusBuilder();
    }
...
/** Builds an EventBus based on the current configuration. */
	public EventBus build() {
        return new EventBus(this);
	}

        代码很简单,builder方法就是返回一个EventBus建造者对象。链式调用设定好参数后调用build方法把定制的EventBusn对象创建出来就好了。
        如果只是想在默认EventBus上做修改,把build方法换成installDefaultEventBus方法就行了。

EventBus.builder()
                .addIndex(...)
                .executorService(...)
                .logger(...)
                .installDefaultEventBus();

看看installDefaultEventBus方法做了什么。

public EventBus installDefaultEventBus() {
        synchronized (EventBus.class) {
            if (EventBus.defaultInstance != null) {
                throw new EventBusException("Default instance already exists." +
                        " It may be only set once before it's used the first time to ensure consistent behavior.");
            }
            EventBus.defaultInstance = build();
            return EventBus.defaultInstance;
        }
    }

        其实也是差不多的,只是在创建前做了一个判断。如果默认的EventBus已经创建了,那就抛出异常。也就是说默认EventBus是不可以重复创建的,参数设定好就不能改了。
        这里再回顾一下EventBus.getDefault()做了什么。

 /** Convenience singleton for apps using a process-wide EventBus instance. */
    public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }

        调用这个方法时如果默认EventBus还没创建,就立即创建它,不修改任何参数。这就是为什么EventBus教程里强调初始化默认EventBus操作要放在Application的onCreate方法里了。

反射的不足及解决方案

        在Android EventBus3源码解析(上)我们知道EventBus为了获取订阅者的注解消息而用到了反射。总所周知,反射会对程序性能造成影响。如果每次是通过反射来寻找订阅者的接收方法再调用,那程序的性能就大打折扣了。
       那么EventBus又是怎么解决的呢?我们可以先回顾一下图片的三级加载机制。内存里没有这张图片,就去本地查找,本地还没有最后才从网络上下载下来。同理,EventBus也使用了这种缓存的策略,回顾一下订阅者注册时调用的方法findSubscriberMethods()

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        //1
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }

		//2
        if (ignoreGeneratedIndex) {
        	//3
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
        	//4
            subscriberMethods = findUsingInfo(subscriberClass);
        }
        if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass
                    + " and its super classes have no public methods with the @Subscribe annotation");
        } else {
            METHOD_CACHE.put(subscriberClass, subscriberMethods);
            return subscriberMethods;
        }
    }

       看到代码块1就很明白了,EventBus是会把订阅者的接收方法缓存到内存的,要的时候就先看看缓存里有没有,有就拿出来,没有就反射调用,顺便缓存起来。这样一来,反射带来的影响也就只是在第一次调用接收方法的时候,之后就没事了。
       Android EventBus3源码解析(上)并没有对代码块2进行详细解析,现在是时候了。ignoreGeneratedIndex这个参数在定制EventBus的时候是可以设定的,它是指是否无视索引。如果无视,就走代码3的反射法。不无视,就走代码4的索引法。
       在分析索引法之前,我们先简单回顾使用索引的步骤。

//第一步:在gradle文件设置索引文件
//com.github.cyc.eventbus.subscriberindexdemo.MyEventbusIndex
//是指索引文件的package路径
javaCompileOptions {
            annotationProcessorOptions {
                arguments = [eventBusIndex: 'com.github.cyc.eventbus.subscriberindexdemo.MyEventbusIndex']
            }
        }

//第二步,编写订阅者的事件处理方法,例如在MainActivity里编写如下代码
//StrMessage是自己定义的事件
@Subscribe(threadMode = ThreadMode.MAIN,priority = 3)
    public void onHandleMessage(StrMessage msg){
        button.setText(msg.getMsg());
}

rebuild一下项目,就会自动生成一个索引文件。

/** This class is generated by EventBus, do not edit. */
public class MyEventBusIndex implements SubscriberInfoIndex {
    private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;

    static {
        SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();

		//1
        putIndex(new SimpleSubscriberInfo(com.pyjtlk.eventbus.MainActivity.class, true, new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onHandleMessage", com.pyjtlk.eventbus.StrMessage.class, ThreadMode.MAIN, 3, false),
        }));

    }

    private static void putIndex(SubscriberInfo info) {
        SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
    }

    @Override
    public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
        SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
        if (info != null) {
            return info;
        } else {
            return null;
        }
    }
}

       代码1处可以看到我们写的订阅者的事件方法的相关信息(注解信息、方法名等等)被保存封装起来并保存在一个HashMap里。这些代码都是EventBus利用反射获取注册者信息后自动编写的。
       需要注意的是,这里的反射是在程序编译时执行的。而我们上文所讲的反射调用是在运行时进行的。
       再回顾我们添加索引的步骤。

public class MyApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
    }
}

跳进addIndex方法。

/** Adds an index generated by EventBus' annotation preprocessor. */
    public EventBusBuilder addIndex(SubscriberInfoIndex index) {
        if (subscriberInfoIndexes == null) {
            subscriberInfoIndexes = new ArrayList<>();
        }
        subscriberInfoIndexes.add(index);
        return this;
    }

很简单,就是放进一个列表里。接着回到我们刚刚提到的代码4,通过索引法来查询订阅者事件处理方法。

else {
      //4
      subscriberMethods = findUsingInfo(subscriberClass);
}

...

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
        	//1
            findState.subscriberInfo = getSubscriberInfo(findState);
            if (findState.subscriberInfo != null) {
                SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
                //2
                for (SubscriberMethod subscriberMethod : array) {
                    if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                        findState.subscriberMethods.add(subscriberMethod);
                    }
                }
            } else {
            	//3
                findUsingReflectionInSingleClass(findState);
            }
            //4
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }

代码1获取到索引集。代码块2循环遍历寻找注册者的事件处理方法。没有找到对应的索引情况下,就只能像之前那样使用反射(运行时)来查找了(代码3处)。看到代码4,同样,索引法也会顺便在注册者的父类找找事件响应方法。

关于代码自动生成

       使用了EventBus的索引后,终于体会到自动生成代码的强大了。不仅是EventBus,其实ButterKnife、GreenDAO、ARouter等框架也有自动生成代码的功能,都是通过注解处理器来实现的,感兴趣的朋友查阅AbstractProcessor的相关资料来学习。

参考文章

EventBus索引:https://blog.csdn.net/augfun/article/details/82635116

EventBus注解处理器:https://www.jianshu.com/p/6c6231b094e2

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值