一、EventBus原理概述
翻看大部分EventBus源码分析文章,都在大谈特谈EventBus的原理和使用,但是对于牛逼的你,一旦用过EventBus,从它暴露出来的api,也能对它的原理说出个大概。
public static class MessageEvent { /* Additional fields if needed */ }
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {/* Do something */};
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
EventBus.getDefault().post(new MessageEvent());
在注册时传入的是订阅者对象,那EventBus作为被观察者代理人。自然有责任和义务去管理(添加和删除)这些订阅者,register和unRegister方法自然被需要,分发对应的事件,当然就需要post方法去遍历通知所有的订阅者。至此,eventBus源码原理解释完毕。
二、EventBus需要解决哪些问题,怎么解决的?
1. 怎么保证事件处理的串行和并行?
ThreadMode.BACKGROUND 对应BackgroundPoster 串行执行。
ThreadMode.POSTING 串行执行
ThreadMode.MAIN 与 ThreadMode.MAIN_ORDERED 几乎一模一样,无需刻意区分,串行执行
ThreadMode.ASYNC 对应AsyncPoster 并行执行
ThreadMode.BACKGROUND 不是java的后台线程啊,如果发送事件是在主线程,那么将是从EventBus默认的线程池中提供一个线程去执行。如果发送事件本身是在用户的子线程,那接收也是在那个子线程。
//以下这个是主线程发,子线程接收保证串行
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
final class BackgroundPoster implements Runnable, Poster {
....//发送事件是为保证串行 先将事件出入队列,各种同步锁
//把任务丢给线程池,让其处理。
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!executorRunning) {
executorRunning = true;
eventBus.getExecutorService().execute(this);
}
}
}
}
ThreadMode.POSTING 无需任何Poster类,发送和接收均在同一个线程,同一个线程的run方法,默认顺序执行,直接就是串行执行了啊。
ThreadMode.MAIN 与 ThreadMode.MAIN_ORDERED 这两个真的没法区分,我不清楚源码里面要区分是干啥。直接考虑两种场景吧,1. 发送在主线程,接收也在主线程,安卓主线程就一个,自然无需处理就是串行了;2. 发送在子线程,接收在主线程,那你做线程切换用的是同一个Handler啊,handler.sendMessage和handler.handleMessage有MessageQueue队列加持,应该也无需做特殊处理额,源码中的HandlerPoster写的太啰嗦了吧。(多个线程同时发送同一个Event的场景除外)
//贴下我改写的代码吧,如有错误,大家轻喷!
public class HandlerPoster implements Handler.Callback {
public EventBus eventBus;
public Handler handler;
public HandlerPoster(EventBus eventBus) {
this.eventBus = eventBus;
//主线程的handler
handler=new Handler(Looper.getMainLooper(),this);
}
public void enqueue(Subscription subscription, Object eventType) {
PostPending postPending=PostPending.obtainPostPending(eventType,subscription);
Message message=Message.obtain();
message.obj=postPending;
handler.sendMessage(message);
}
@Override
public boolean handleMessage(@NonNull Message msg) {
if( msg.obj instanceof PostPending){
PostPending postPending= (PostPending) msg.obj;
eventBus.invokeSubscriber(postPending);
}
return true;
}
}
ThreadMode.ASYNC 异步处理,只要有一个event到达,就加入线程池执行,自然就是并行处理了。
public class AsyncPoster implements Runnable {
......
public void enqueue(Subscription subscription, Object eventType) {
PostPending postPending = PostPending.obtainPostPending(eventType, subscription);
Log.e("eventBus", "AsyncPoster obtainPostPending " +postPending);
queue.enqueue(postPending);
eventBus.executorService.execute(this);
}
真正需要额外处理的场景:
- 指定Thread.Mode为ThreadMode.BACKGROUND,发送在主线程,接收在子线程,同时要保证串行。如果让你来实现,你会怎么做?
你是不是会想到单生产-单消费模型 啊,源码就是基于这个模型的, 生产就加入队列,消费就从队列里面取出执行。
public class BackgroundPoster implements Runnable {
public EventBus eventBus;
public PostPendingQueue queue;
public BackgroundPoster(EventBus eventBus) {
this.eventBus = eventBus;
this.queue = new PostPendingQueue();
}
//使用volatile保证可见性
private volatile boolean executorRunning;
public void enqueue(Subscription subscription,Object eventType) {
PostPending postPending=PostPending.obtainPostPending(eventType,subscription);
synchronized (this) {
queue.enqueue(postPending);
if (!executorRunning) {
executorRunning = true;
eventBus.executorService.execute(this);
}
}
}
@Override
public void run() {
try {
try {
while (true) {
PostPending postPending = null;
postPending = queue.poll(1000);
if (postPending == null) {
synchronized (this) {
postPending = queue.poll();
if (postPending == null) {
executorRunning = false;
return;
}
}
}
eventBus.invokeSubscriber(postPending);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
executorRunning = false;
}
}
}
忍不住贴一下EventBus里面的队列,很经典
public class PostPendingQueue {
PostPending head;
PostPending tail;
public synchronized void enqueue(PostPending postPending){
//注意顺序
if(tail!=null){
tail.next=postPending;
tail=postPending;
}else if (head==null){
head=tail=postPending;
}
notifyAll();
}
public synchronized PostPending poll(long time) throws InterruptedException {
if(head==null){
wait(time);
}
return poll();
}
public synchronized PostPending poll() {
PostPending postPending=head;
if(postPending!=null){
head=postPending.next;
if(head==null){
tail=null;
}
}
return postPending;
}
}
2. EventBus可给java后台使用,也可给android用,那它怎么判断是android的?
static {
boolean android = false;
try {
android = Class.forName("android.util.Log") != null;
} catch (ClassNotFoundException e) {
// OK
}
ANDROID_LOG_AVAILABLE = android; //判断通过这个标志位
}
3. 如果方法的Event参数类型是泛型,怎么获取到这个泛型的真实类型?
VariableElement 是编译期间才用的api。 represents a field, enum constant, method or constructor parameter, local variable, resource variable, or exception parameter。(代表一个字段、枚举常量、方法参数、构造参数、本地变量、资源变量、异常参数)
private TypeMirror getParamTypeMirror(VariableElement param, Messager messager) {
TypeMirror typeMirror = param.asType();
// 判断是否是泛型
if (typeMirror instanceof TypeVariable) {
TypeMirror upperBound = ((TypeVariable) typeMirror).getUpperBound();
//获取到实际的声明类型,以上界为准
if (upperBound instanceof DeclaredType) {
typeMirror = upperBound;
}
}
return typeMirror;
}
4. 判断订阅方法的修饰符为Public:使用位运算,大大提高效率
private static final int BRIDGE = 0x40;
private static final int SYNTHETIC = 0x1000;
private static final int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC;
public static final int PUBLIC = 0x00000001;
public static final int PRIVATE = 0x00000002;
public static final int PROTECTED = 0x00000004;
public static final int STATIC = 0x00000008;
public static final int FINAL = 0x00000010;
public static final int ABSTRACT = 0x00000400;
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
.....
}
5. EventBus源码中是用ThreadLocal干啥?
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
ThreadLocal作用:相同线程数据共享,不同线程数据隔离。 其对象实例与ThreadLocal变量的映射关系由Thread维护。
EventBus使用的场景:订阅事件设置线程ThreadMode = ThreadMode.POSTING, 且设置了不同的优先级,优先级高的订阅者在接收到事件后,调用了 EventBus.getDefault().cancelEventDelivery(event);那后续优先级低的订阅者将不会收到事件。
6. 粘性事件?
当之前有相应的粘性事件发送了,但是订阅者还没注册,这时候订阅者再注册,可以在注册时收到之前的事件。
@Subscribe(threadMode = ThreadMode.POSTING, sticky = true)
public void show5(MyMessage myMessage) {
Log.e("eventBus1", "show5: 接收数据message");
}
EventBus.getDefault().postSticky(event);
//最后才注册:
EventBus.getDefault().register(MainActivity.this);
7. 解析注解生成新类,只能使用javapoet吗?
EventBus可没有引入这个javapoet库哦,用的Filer配合 BufferedWriter 的writer写入文件。
private void createFile() {
BufferedWriter bufferedWriter = null;
try {
JavaFileObject source = mFiler.createSourceFile(mIndex);
bufferedWriter = new BufferedWriter(source.openWriter());
int index = mIndex.lastIndexOf(".");
String packageName = mIndex.substring(0, index);
bufferedWriter.write("package " + packageName + ";\n\n");
bufferedWriter.write("import java.util.HashMap;\n");
bufferedWriter.write("import java.util.Map;\n");
bufferedWriter.write("import " + ThreadMode.class.getCanonicalName() + ";\n");
bufferedWriter.write("public class " + mIndex.substring(index + 1) + " implements ISubscriberMethodIndex{\n\n");
bufferedWriter.write(" private final static Map<Class<?>, SubscribeMethodApt[]> SUBSCRIBER_INDEX;\n");
bufferedWriter.write(" static {" + "\n");
bufferedWriter.write(" SUBSCRIBER_INDEX = new HashMap<>();\n");
bufferedWriter.write(writeKeyLines());
bufferedWriter.write(" }" + "\n\n");
bufferedWriter.write(" @Override" + "\n");
bufferedWriter.write(" public SubscribeMethodApt[] getSubScriberMethod(Class<?> clazz){\n");
bufferedWriter.write(" return SUBSCRIBER_INDEX.get(clazz);\n");
bufferedWriter.write(" }\n");
bufferedWriter.write("}\n");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bufferedWriter != null) {
try {
bufferedWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
8. 在gradle中配置annotationProcessorOptions有什么用?
javaCompileOptions {
annotationProcessorOptions {
arguments=[eventBusIndex:'com.docwei.eventbusnewdemo.MainEventBusIndex']
}
}
参数的arguments含有一个键值对,key是eventBusIndex,值是:com.docwei.eventbusnewdemo.MainEventBusIndex,value作为新生成的文件全路径。在注解处理器中可以捕获到这个key和value值。
v@SupportedOptions(value = {"eventBusIndex"})
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class EventBusAnnotationProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
//判断有没有设置options
// arguments = [eventBusIndex: 'com.docwei.eventbusnewdemo.MainEventBusIndex']
//这个就是新生成的类的文件全路径
mIndex = mOptions.get("eventBusIndex");
9. 支持多线程操作的ArrayList和HashMap并发容器
EventBus使用了CopyOnWriteArrayList和ConcurrentHashMap.
CopyOnWriteArrayList 查询时不需要加锁,可直接访问,只有在写入/删除时,才会从原来的数据复制一个副本出来,然后修改这个副本,最后把原数据替换成当前的副本。修改操作的同时,读操作不会被阻塞,而是继续读取旧的数据。 可能存在内存占用问题,导致Gc。CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。
ConcurrentHashMap key和value都不能为null,采用了CAS和synchronized来保证并发安全.
10. 对象池的多处使用
PendingPost 和 FindState 都创建了对应的对象池。以后可以参考这种写法:
private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();
static PendingPost obtainPendingPost(Subscription subscription, Object event) {
synchronized (pendingPostPool) {
int size = pendingPostPool.size();
if (size > 0) {
PendingPost pendingPost = pendingPostPool.remove(size - 1);
pendingPost.event = event;
pendingPost.subscription = subscription;
pendingPost.next = null;
return pendingPost;
}
}
return new PendingPost(event, subscription);
}
static void releasePendingPost(PendingPost pendingPost) {
pendingPost.event = null;
pendingPost.subscription = null;
pendingPost.next = null;
synchronized (pendingPostPool) {
// Don't let the pool grow indefinitely
if (pendingPostPool.size() < 10000) {
pendingPostPool.add(pendingPost);
}
}
}
private static final int POOL_SIZE = 4;
private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
findState.recycle();
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
if (FIND_STATE_POOL[i] == null) {
FIND_STATE_POOL[i] = findState;
break;
}
}
}
return subscriberMethods;
}
private FindState prepareFindState() {
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
FindState state = FIND_STATE_POOL[i];
if (state != null) {
FIND_STATE_POOL[i] = null;
return state;
}
}
}
return new FindState();
}
三、基于EventBus源码,精简代码的demo奉上
看完源码后以完成EventBus的主体功能为目标进行精简重写。