EventBus使用
EventBus优点
-
简化组件之间的通信
- 解耦发送者和接收者
- 可以在Fragment、Activity和不同的线程之间使用
- 避免了复杂的依赖
-
简化代码
-
快且小,60K
-
已经有上亿的下载量
-
具有很多高级特性
使用方法
- EventBus.getDefault().register(this)注册当前对象
- 使用@Subscribe注解要接收Event的方法
- 定义事件(Event)类,本文里定义一个EventBusMessage类
- EventBus.getDefault().post(EventBusMessage())发送事件
class MainActivity : AppCompatActivity() {
val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val btn = findViewById<TextView>(R.id.helloEventBus)
btn.setOnClickListener {
for (i in 0 until 3){
Log.d(TAG, "post Thread: ${Thread.currentThread()}")
EventBus.getDefault().post(EventBusMessage())
}
}
}
// 注册
override fun onStart() {
EventBus.getDefault().register(this)
super.onStart()
}
// Event的类型和订阅的类都可以自定义,关键是Subscribe注解
@Subscribe(threadMode = ThreadMode.MAIN)
fun onMessageEvent(event: EventBusMessage?) {
Log.d(TAG, "onMessageEvent: ${event!!.name} Receiver Thread: ${Thread.currentThread()}")
}
override fun onStop() {
EventBus.getDefault().unregister(this)
super.onStop()
}
}
定义一个事件类:
class EventBusMessage(val i: Int) {
public val name = "cool!+${i}"
companion object {
private const val TAG = "EventBusMessage"
}
}
解释一下
注意事项
- 注册的EventBus对象和post的是一个对象才能成功,EventBus.getDefault()拿到的是默认的单例,不同model引用eventbus包名一致,所以EventBus.getDefault()拿到的是同一对象
- 订阅的方法必须是public的
- 不用的时候要调用EventBus.getDefault().unregister(this)
点击btn,onMessageEvent就会打印方法
虽然这个例子并没有实际意义,但是可以很好的解释用法
@Subscribe 注解介绍
完整版如下:
@Subscribe(threadMode = ThreadMode.MAIN,sticky = true,priority = 1)
threadMode
先看下简单的一段源码,应该更容易理解
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:
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
这是EventBus的默认模式,表示post事件是什么线程,onReceiveMsg接收事件方法就在同样的线程环境中执行代码。更改代码如下
findViewById.setOnClickListener {
Thread{
for (i in 0 until 3){
Log.d(TAG, "post Thread: ${Thread.currentThread()}")
EventBus.getDefault().post(EventBusMessage())
}
}.start()
}
@Subscribe(threadMode = ThreadMode.POSTING)
fun onMessageEvent(event: EventBusMessage?) {
Log.d(TAG, "Receiver Thread: ${Thread.currentThread()} onMessageEvent: ${event!!.name} ")
}
从源码里可以看出,当ThreadMode是Posting,则直接执行invokeSubscriber(subscription, event),
void invokeSubscriber(Subscription subscription, Object event) {
try {
// 通过反射调用,所以是串行
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
handleSubscriberException(subscription, event, e.getCause());
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
}
}
ps:【Thread-5, 5, main】:Thread-5是线程名,5:优先级,main: 线程所属线程组
适合计算量小的耗时少的、不涉及UI的代码
MAIN
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
subscribe都会在主线程中执行
- 如果post是在主线程中,则串行执行invokeSubscriber,因此会依次执行post和subscribe
- 如果post在子线程中,则通过enqueue加到主线程的执行队列里面,subscribe会保留相对执行顺序。
修改对应的代码如下:
@Subscribe(threadMode = ThreadMode.MAIN)
fun onMessageEvent(event: EventBusMessage?) {
Log.d(TAG, "Receiver Thread: ${Thread.currentThread()} onMessageEvent: ${event!!.name} ")
}
findViewById.setOnClickListener {
// Thread{
// for (i in 0 until 3){
// Log.d(TAG, "post Thread: ${Thread.currentThread()}")
// EventBus.getDefault().post(EventBusMessage(i))
// }
// }.start()
for (i in 0 until 3){
Log.d(TAG, "post Thread: ${Thread.currentThread()}")
EventBus.getDefault().post(EventBusMessage(i))
}
}
post在子线程子线程:
post在主线程:依次执行
MAIN_ORDERED
case MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
// temporary: technically not correct as poster not decoupled from subscriber
invokeSubscriber(subscription, event);
}
无论事件发布者post在什么线程环境,都会将subscribe加到主线程的执行队列,(ps. mainThreadPoster感觉上一直是非null的?)
BACKGROUND
该模式下的时间发布者post线程环境与事件接收onReceiveMsg方法的线程环境关系如下:
case BACKGROUND:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
- post在主线程,subscribe在子线程执行
- post在子线程,subscribe在同一子线程执行
ASYNC:
case ASYNC:
asyncPoster.enqueue(subscription, event);
该模式表示,无论post环境是什么线程,事件接收处理环境都是子线程。
ASYNC和BACKGROUND有什么区别?
backgroundPoster在 Executor的execute()方法 上添加了 synchronized关键字 并设立了控制标记flag,保证任一时间只且仅能有一个任务会被线程池执行,而ASYNC没有限制,可以同时在不同线程执行不同任务,但是应该是同一线程池。 执行结果如下所示:
由于篇幅,完整的代码占篇幅就不贴了,相信认真的读者可以理解。
sticky
sticky默认的设置为false
将sticky设置为true允许先post后subscribe,更改代码如下,注意不再是
EventBus.getDefault().post(EventBusMessage(i))
而是:EventBus.getDefault().postSticky(EventBusMessage(i))
for (i in 0 until 3){
Log.d(TAG, "post Thread: ${Thread.currentThread()}")
EventBus.getDefault().postSticky(EventBusMessage(i))
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onMessageEvent(event: EventBusMessage?) {
Log.d(TAG, "Receiver Thread: ${Thread.currentThread()} onMessageEvent: ${event!!.name} ")
}
我们先post然后再注册结果如下:可以看出,只接收到了一个事件,后注册应该只能接收注册之前的最后一个post事件
priority
该参数是int型,默认值是0,优先级值越高,越先接收到事件,不过这里要注意一个问题,那就是优先级的比较前提是在post事件发布,onReceiveMsg事件接收处理这两方的线程环境相同的前提下,才有意义。
同是与priority相配合使用的一个方法是cancelEventDelivery.示例如下,如果满足某些事件,取消继续传播
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onMessageEvent(event: EventBusMessage?) {
if(condition()){
EventBus.getDefault().cancelEventDelivery(event)
}
}