EventBus3.x源码分析之发送事件

##EventBus3.x源码分析之发送事件

发送事件时,最长使用的就是EventBus.getDefault().post(Object object)方法。
这里只分析非粘性事件发送,因为粘性事件已经再EventBus3.x源码分析之注册(一)中提到过,后续的逻辑同post。

EventBus#
public void post(Object event) {
    PostingThreadState postingState = currentPostingThreadState.get();
    List<Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);

是否已经发送了
    if (!postingState.isPosting) {
        //当前线程是否是主线程
        postingState.isMainThread = isMainThread();
        postingState.isPosting = true;
        if (postingState.canceled) {
            throw new EventBusException("Internal error. Abort state was not reset");
        }
        try {
            while (!eventQueue.isEmpty()) {
                //将发送的事件从事件队列中移除
                postSingleEvent(eventQueue.remove(0), postingState);
            }
        } finally {
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}
//发送事件的状态
final static class PostingThreadState {
    final List<Object> eventQueue = new ArrayList<>();//发送事件列表
    boolean isPosting;//是否已经发送了
    boolean isMainThread;//是否主线程
    Subscription subscription;
    Object event;
    boolean canceled;//是否需要终止发送
}

1、发送事件的入口函数,通过ThreadLocal的方式获取PostingThreadState实例,好处就是将每个需要它的线程分配一份实例,保证数据的安全及速度。
2、并将事件存入发送事件列表eventQueue。判断当前事件是否已经被发送了,进行一系列发送事件状态封装。
3、然后轮询获取事件列表中的事件,直到取完为止。最后将PostingThreadState实例重置。

EventBus#
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    Class<?> eventClass = event.getClass();
    //是否找到了对应的订阅信息
    boolean subscriptionFound = false;
    //是否需要查找发送的事件的父类以及接口
    if (eventInheritance) {
        List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
        int countTypes = eventTypes.size();
        for (int h = 0; h < countTypes; h++) {
            Class<?> clazz = eventTypes.get(h);
            subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
        }
    } else {
        subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
    }
    ...
}
private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
    synchronized (eventTypesCache) {
        List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
        if (eventTypes == null) {
            eventTypes = new ArrayList<>();
            Class<?> clazz = eventClass;
            while (clazz != null) {
                eventTypes.add(clazz);
                //添加所有的接口
                addInterfaces(eventTypes, clazz.getInterfaces());
                //添加所有的父类
                clazz = clazz.getSuperclass();
            }
            eventTypesCache.put(eventClass, eventTypes);
        }
        return eventTypes;
    }
}

发送事件前,找到对应的事件类型。

EventBus#
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
        //根据发送的事件(也就是之前订阅方法中的参数)获取订阅信息
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    if (subscriptions != null && !subscriptions.isEmpty()) {
        for (Subscription subscription : subscriptions) {
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted = false;
            try {
                postToSubscription(subscription, event, postingState.isMainThread);
                //正常情况下者里的postingState.canceled返回一定是false,不会终止,但是如果有中途中断比如#cancelEventDelivery(Object event)会终止发送事件。
                aborted = postingState.canceled;
            } finally {
                postingState.event = null;
                postingState.subscription = null;
                postingState.canceled = false;
            }
//如果调用的中断方法,终止后续的发送事件
            if (aborted) {
                break;
            }
        }
        return true;
    }
    return false;
}
//发送事件的状态
final static class PostingThreadState {
    final List<Object> eventQueue = new ArrayList<>();//发送事件列表
    boolean isPosting;//是否已经发送了
    boolean isMainThread;//是否主线程
    Subscription subscription;
    Object event;
    boolean canceled;//是否需要终止发送
}

根据订阅类型,将所有之前订阅过的订阅信息取出,循环发送事件。

ThreadMode#
public enum ThreadMode {
	//默认模式,事件的处理在和事件的发送在相同的进程,所以事件处理时间不应太长,不然影响事件的发送线程,而这个线程可能是UI线程.
	/**
	 * 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,

	//同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
}

这里就是为何要在定义订阅方法的时候要指定@Subscribe(threadMode = ThreadMode.xxx)。当然不指定默认为POSTTING,下面会做详细分析。

EventBus#
//事件入列,或处理
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 MAIN_ORDERED:
            //如果是在Android当中
            if (mainThreadPoster != null) {
                //这种模式将事件添加到分发队列
                mainThreadPoster.enqueue(subscription, event);
            } else {
                //不是在安卓中,直接在当前线程调用订阅方法(该模式可视为无效)
                // temporary: technically not correct as poster not decoupled from subscriber
                invokeSubscriber(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);
    }
}

这里是正式开始事件分发的入口,挨个分析:
####POSTING:
当前线程分发,当前线程处理,也不需要队列。
####MAIN:
1、如果主线程分发,处理也在主线程,也不需要队列。
2、如果子线程分发,需要先入列。
分析代码mainThreadPoster是HandlerPoster的实例

HandlerPoster#
@Override
public void enqueue(Subscription subscription, Object event) {
	PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
	synchronized (this) {
		queue.enqueue(pendingPost);
		if (!handlerActive) {
			handlerActive = true;
			//试图发送空消息,检测是否支持发送消息
			if (!sendMessage(obtainMessage())) {
				throw new EventBusException("Could not send handler message");
			}
		}
	}
}

首先通过obtainPendingPost获取PendingPost实例。

//待分发的事件封装体
final class PendingPost {
	//对象池——所有分发器公用
	private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();
	//事件链表
	PendingPost next;
	...
	//从对象池中获取分发事件
	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);
			}
		}
	}
}

这里维护了一个公所有分发器使用的待分发事件对象池,取出一个删除一个,释放时候添加一个。上限10000

//待分发事件管理队列
final class PendingPostQueue {
	private PendingPost head;
	private PendingPost tail;

	synchronized void enqueue(PendingPost pendingPost) {
		if (pendingPost == null) {
			throw new NullPointerException("null cannot be enqueued");
		}
		//后面的事件来了
		if (tail != null) {
			tail.next = pendingPost;//(将后来的事件添加给上一个事件)构建一个链表
			tail = pendingPost;//并将后来的事件添加到尾部属性,等待构建下一个事件的引用
		} else if (head == null) {
			//第一个事件刚入列:
			//事件队列中只有此一个
			head = tail = pendingPost;
		} else {
			throw new IllegalStateException("Head present, but no tail");
		}
		notifyAll();
	}
	...
}

拿到带分发事件实例后,进行入列。
1、PendingPostQueue这个对象有两个成员全部是PendingPost类型,但顾名思义是“首”“尾”意思,而PendingPost中也有一个自身类型的成员为next,这就构成了事件链表。
2、第一个事件刚入列,首尾都是该事件,后面来的,添加给尾部事件的成员next上,这就构成链表。

HandlerPoster#
@Override
public void handleMessage(Message msg) {
	boolean rescheduled = false;
	try {
		long started = SystemClock.uptimeMillis();
		while (true) {
			PendingPost pendingPost = queue.poll();//去取事件
			//事件取完后就会将handlerActive置为false。
			if (pendingPost == null) {
				synchronized (this) {
					// Check again, this time in synchronized
					pendingPost = queue.poll();
					if (pendingPost == null) {
						handlerActive = false;
						return;
					}
				}
			}
			//将事件拉到主线程进行处理
			eventBus.invokeSubscriber(pendingPost);
			long timeInMethod = SystemClock.uptimeMillis() - started;
			//如果处理事件(循环)的时间>10ms,终止此队列的消息处理。
			if (timeInMethod >= maxMillisInsideHandleMessage) {
				//并再次发送消息,执行handlemessage,继续之前队列中事件的处理,保证主线程阻塞时间不超过10ms
				if (!sendMessage(obtainMessage())) {
					throw new EventBusException("Could not send handler message");
				}
				//并将handlerActive置为true,防止多次发送消息。
				rescheduled = true;
				return;
			}
		}
	} finally {
		handlerActive = rescheduled;
	}
}

在事件入列发送空消息后,会回调HandlerPoster的handleMessage方法

//待分发事件管理队列
final class PendingPostQueue {
	...
	synchronized PendingPost poll() {
		PendingPost pendingPost = head;
		//取出第一个事件,并准备下一个事件
		if (head != null) {
			head = head.next;
			if (head == null) {
				tail = null;
			}
		}
		return pendingPost;
	}
	...
}

1、然后循环从队列中取出事件,并通过eventBus.invokeSubscriber(pendingPost);将事件拉到主线程进行处理。
2、这里维护了一个时间差,因为循环处理事件会造成线程堵塞,所以这里实行超过maxMillisInsideHandleMessage这个时间,终止此队列消息处理,再次发送空消息,继续上次队列中的时间处理。

####MAIN_ORDERED:
分析这里,mainThreadPoster是否为空,取决于mainThreadSupport=builder.getMainThreadSupport();是否返回null;
最终会走到

Object getAndroidMainLooperOrNull() {
	try {
		return Looper.getMainLooper();
	} catch (RuntimeException e) {
		// Not really a functional Android (e.g. "Stub!" maven dependencies)
		return null;
	}
}

所以这里通过mainThreadPoster != null可以判断当前试运行在Android系统中。
这种模式会将事件添加到分发队列

针对MAIN和MAIN_ORDERED:这两个分发不会阻塞因为有队列,但是处理事件在主线程有可能造成堵塞,所以建议处理的事件不要耗时。

####TBACKGROUND:
1、如果分发在子线程,当即处理事件。
2、如果分发在主线程
分析代码backgroundPoster是BackgroundPoster的实例

BackgroundPoster#
//子线程事件分发器
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);
		}
	}
}
@Override
public void run() {
	try {
		try {
			while (true) {
				PendingPost pendingPost = queue.poll(1000);
				//取完后终止
				if (pendingPost == null) {
					synchronized (this) {
						// Check again, this time in synchronized
						pendingPost = queue.poll();
						if (pendingPost == null) {
							executorRunning = false;
							return;
						}
					}
				}
				//直接在子线程处理事件
				eventBus.invokeSubscriber(pendingPost);
			}
		} catch (InterruptedException e) {
			eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
		}
	} finally {
		executorRunning = false;
	}
}

队列的逻辑与先前分析的无差别,但是事件处理不同,这里使用了线程池,而且是只是用了一条线程。之前讲过为了保证处理事件的有序。

####ASYNC
无论分发在哪个线程,将事件添加到分发队列,通过线程池,并发处理这些事件
分析代码asyncPoster是AsyncPoster的实例

class AsyncPoster implements Runnable, Poster {

	private final PendingPostQueue queue;
	private final EventBus eventBus;

	AsyncPoster(EventBus eventBus) {
		this.eventBus = eventBus;
		queue = new PendingPostQueue();
	}

	public void enqueue(Subscription subscription, Object event) {
		PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
		queue.enqueue(pendingPost);
		//将所有的事件并发处理
		eventBus.getExecutorService().execute(this);
	}

	@Override
	public void run() {
		PendingPost pendingPost = queue.poll();
		if(pendingPost == null) {
			throw new IllegalStateException("No pending post available");
		}
		eventBus.invokeSubscriber(pendingPost);
	}
}

这里的逻辑是入列一个事件开一条线程取处理个事件。并发执行。这也必然会导致@Subscribe(pority = 1)这个优先级失效!

线程池使用的是无限大可缓存的线程池。
//使用系统默认线程池
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();

以上就是分发事件流程。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值