基于Jar包方式实现EventBus
https://blog.csdn.net/weixin_37730482/article/details/72780281
基于Gradle依赖实现的EventBus
一.EventBus GitHub地址
https://github.com/greenrobot/EventBus
二.Gradle依赖
implementation 'org.greenrobot:eventbus:3.1.1'
三.图解
四.常用方法
<1> 注册
EventBus.getDefault().register(this);
<2> 注销
EventBus.getDefault().unregister(this);
<3> 发送
EventBus.getDefault().post(new AnyEventType event);
可以看出 注册,注销,发送 三个方法和Jar包的方式一致。唯独接收的方式不一样。
<4> 接收&线程
根据官方文档来看,这种方式接收的方法名不固定,但是必须要在方法名上添加 @Subscribe。方法名不固定,那么怎么区分接收方所在的线程呢。
答案就是 枚举类 ThreadMode 共有四种常用取值 取值以及对应线程。
[1] POSTING:默认,表示事件处理函数的线程跟发布事件的线程在同一个线程。
相当于Jar包方式的 onEvent方法。
[2] MAIN:表示事件处理函数的线程在主线程(UI)线程,因此在这里不能进行耗时操作。
相当于Jar包方式的 onEventMainThread方法。
[3] BACKGROUND:表示事件处理函数的线程在后台线程,因此不能进行UI操作。如果发布事件的线程是主线程(UI线程),那么事件处理函数将会开启一个后台线程,如果果发布事件的线程是在后台线程,那么事件处理函数就使用该线程。
相当于Jar包方式的 onEventBackgroundThread方法。
[4] ASYNC:表示无论事件发布的线程是哪一个,事件处理函数始终会新建一个子线程运行,同样不能进行UI操作。
相当于Jar包方式的 onEventAsync方法。
五.示例demo
接收方
package com.example.myapplication.eventbus;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.example.myapplication.R;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
/**
* 模拟EventBus接收方
*/
public class EventBusActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_eventbus);
//注册EventBus
EventBus.getDefault().register(this);
//先发送数据
TextView textView = findViewById(R.id.acivity_eventbus_textview1);
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(EventBusActivity.this, EventBusOtherActivity.class);
startActivity(intent);
}
});
}
/**
* EventBus接收方法
* ThreadMode.POSTING
*/
@Subscribe(threadMode = ThreadMode.POSTING)
public void onMessageDefaultEvent(MsgBean msgBean) {
if (msgBean == null) {
return;
}
String result = "msgType:" + msgBean.getMsgType() + " msgContent:" + msgBean.getMsgContent() + " msgId:" + msgBean.getMsgId();
Log.d("EventBusActivity", "EventBus接收方法 ThreadMode.POSTING result----:" + result);
Log.d("EventBusActivity", "EventBus接收方法 ThreadMode.POSTING 线程----:" + Thread.currentThread().getName());
}
/**
* EventBus接收方法
* ThreadMode.MAIN
*/
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageMainEvent(MsgBean msgBean) {
if (msgBean == null) {
return;
}
String result = "msgType:" + msgBean.getMsgType() + " msgContent:" + msgBean.getMsgContent() + " msgId:" + msgBean.getMsgId();
Log.d("EventBusActivity", "EventBus接收方法 ThreadMode.MAIN result----:" + result);
Log.d("EventBusActivity", "EventBus接收方法 ThreadMode.MAIN 线程----:" + Thread.currentThread().getName());
}
/**
* EventBus接收方法
* ThreadMode.BACKGROUND
*/
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onMessageBackgroundEvent(MsgBean msgBean) {
if (msgBean == null) {
return;
}
String result = "msgType:" + msgBean.getMsgType() + " msgContent:" + msgBean.getMsgContent() + " msgId:" + msgBean.getMsgId();
Log.d("EventBusActivity", "EventBus接收方法 ThreadMode.BACKGROUND result----:" + result);
Log.d("EventBusActivity", "EventBus接收方法 ThreadMode.BACKGROUND 线程----:" + Thread.currentThread().getName());
}
/**
* EventBus接收方法
* ThreadMode.ASYNC
*/
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onMessageAsyncEvent(MsgBean msgBean) {
if (msgBean == null) {
return;
}
String result = "msgType:" + msgBean.getMsgType() + " msgContent:" + msgBean.getMsgContent() + " msgId:" + msgBean.getMsgId();
Log.d("EventBusActivity", "EventBus接收方法 ThreadMode.ASYNC result----:" + result);
Log.d("EventBusActivity", "EventBus接收方法 ThreadMode.ASYNC 线程----:" + Thread.currentThread().getName());
}
/**
* onDestroy方法
*/
@Override
protected void onDestroy() {
super.onDestroy();
//EventBus注销
EventBus.getDefault().unregister(this);
}
}
发送方(UI线程发送)
package com.example.myapplication.eventbus;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.example.myapplication.R;
import org.greenrobot.eventbus.EventBus;
/**
* 模拟EventBus发送方
*/
public class EventBusOtherActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_eventbusother);
//UI线程发送EventBus
TextView textView1 = findViewById(R.id.acivity_eventbusother_textview1);
textView1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MsgBean msgBean = new MsgBean();
msgBean.setMsgType("MainUI");
msgBean.setMsgContent("我是从UI线程发出的EventBus消息");
msgBean.setMsgId("123");
EventBus.getDefault().post(msgBean);
Log.d("EventBusActivity", "UI线程发送EventBus 线程----:" + Thread.currentThread().getName());
}
});
//子线程发送EventBus
TextView textView2 = findViewById(R.id.acivity_eventbusother_textview2);
textView2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
MsgBean msgBean = new MsgBean();
msgBean.setMsgType("NewThreaUI");
msgBean.setMsgContent("我是从子线程线程发出的EventBus消息");
msgBean.setMsgId("456");
EventBus.getDefault().post(msgBean);
Log.d("EventBusActivity", "子线程发送EventBus 线程----:" + Thread.currentThread().getName());
}
}).start();
}
});
}
}
结果
D/EventBusActivity: EventBus接收方法 ThreadMode.POSTING result----:msgType:MainUI msgContent:我是从UI线程发出的EventBus消息 msgId:123
D/EventBusActivity: EventBus接收方法 ThreadMode.POSTING 线程----:main
*****************************************************************************
D/EventBusActivity: EventBus接收方法 ThreadMode.MAIN result----:msgType:MainUI msgContent:我是从UI线程发出的EventBus消息 msgId:123
D/EventBusActivity: EventBus接收方法 ThreadMode.MAIN 线程----:main
******************************************************************************
D/EventBusActivity: EventBus接收方法 ThreadMode.BACKGROUND result----:msgType:MainUI msgContent:我是从UI线程发出的EventBus消息 msgId:123
D/EventBusActivity: EventBus接收方法 ThreadMode.BACKGROUND 线程----:pool-1-thread-2
*********************************************************************************
D/EventBusActivity: EventBus接收方法 ThreadMode.ASYNC result----:msgType:MainUI msgContent:我是从UI线程发出的EventBus消息 msgId:123
D/EventBusActivity: EventBus接收方法 ThreadMode.ASYNC 线程----:pool-1-thread-1
************************************************************************************
D/EventBusActivity: UI线程发送EventBus 线程----:main
这样能准确说明四个取值对应的接收方所在的线程。
发送方(子线程发放) 接收方不变 结果
D/EventBusActivity: EventBus接收方法 ThreadMode.POSTING result----:msgType:NewThreaUI msgContent:我是从子线程线程发出的EventBus消息 msgId:456
D/EventBusActivity: EventBus接收方法 ThreadMode.POSTING 线程----:Thread-6
*************************************************************************************
D/EventBusActivity: EventBus接收方法 ThreadMode.MAIN result----:msgType:NewThreaUI msgContent:我是从子线程线程发出的EventBus消息 msgId:456
D/EventBusActivity: EventBus接收方法 ThreadMode.MAIN 线程----:main
*************************************************************************************
D/EventBusActivity: EventBus接收方法 ThreadMode.BACKGROUND result----:msgType:NewThreaUI msgContent:我是从子线程线程发出的EventBus消息 msgId:456
D/EventBusActivity: EventBus接收方法 ThreadMode.BACKGROUND 线程----:Thread-6
**************************************************************************************
D/EventBusActivity: EventBus接收方法 ThreadMode.ASYNC result----:msgType:NewThreaUI msgContent:我是从子线程线程发出的EventBus消息 msgId:456
D/EventBusActivity: EventBus接收方法 ThreadMode.ASYNC 线程----:pool-1-thread-1
**************************************************************************************
D/EventBusActivity: 子线程发送EventBus 线程----:Thread-6
这样能准确说明四个取值对应的接收方所在的线程。
六.黏性事件
<1> 简介
所谓的黏性事件,就是指发送了该事件之后再订阅者依然能够接收到的事件。
使用黏性事件的时候有两个地方需要做些修改。
一个是订阅事件的地方。即添加 sticky 属性
@Subscribe(threadMode = ThreadMode.MAIN ,sticky = true)
另一个是发布事件的地方。即post方法改成postSticky方法
EventBus.getDefault().postSticky(msgBean);
<2> 代码
package com.example.myapplication.eventbus;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.example.myapplication.R;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
/**
* 模拟EventBus接收方
*/
public class EventBusActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_eventbus);
//先发送数据
TextView textView1 = findViewById(R.id.acivity_eventbus_textview1);
textView1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MsgBean msgBean = new MsgBean();
msgBean.setMsgType("onClick");
msgBean.setMsgContent("我是从onClick发出的EventBus消息");
msgBean.setMsgId("123");
EventBus.getDefault().postSticky(msgBean);
Log.d("EventBusActivity", "onClick发送EventBus 线程----:" + Thread.currentThread().getName());
}
});
TextView textView2 = findViewById(R.id.acivity_eventbus_textview2);
textView2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//注册EventBus
EventBus.getDefault().register(EventBusActivity.this);
}
});
}
/**
* EventBus接收方法
* ThreadMode.POSTING
*/
@Subscribe(threadMode = ThreadMode.POSTING, sticky = true)
public void onMessageDefaultEvent(MsgBean msgBean) {
if (msgBean == null) {
return;
}
String result = "msgType:" + msgBean.getMsgType() + " msgContent:" + msgBean.getMsgContent() + " msgId:" + msgBean.getMsgId();
Log.d("EventBusActivity", "EventBus接收方法 ThreadMode.POSTING result----:" + result);
Log.d("EventBusActivity", "EventBus接收方法 ThreadMode.POSTING 线程----:" + Thread.currentThread().getName());
}
/**
* EventBus接收方法
* ThreadMode.MAIN
*/
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onMessageMainEvent(MsgBean msgBean) {
if (msgBean == null) {
return;
}
String result = "msgType:" + msgBean.getMsgType() + " msgContent:" + msgBean.getMsgContent() + " msgId:" + msgBean.getMsgId();
Log.d("EventBusActivity", "EventBus接收方法 ThreadMode.MAIN result----:" + result);
Log.d("EventBusActivity", "EventBus接收方法 ThreadMode.MAIN 线程----:" + Thread.currentThread().getName());
}
/**
* EventBus接收方法
* ThreadMode.BACKGROUND
*/
@Subscribe(threadMode = ThreadMode.BACKGROUND, sticky = false)
public void onMessageBackgroundEvent(MsgBean msgBean) {
if (msgBean == null) {
return;
}
String result = "msgType:" + msgBean.getMsgType() + " msgContent:" + msgBean.getMsgContent() + " msgId:" + msgBean.getMsgId();
Log.d("EventBusActivity", "EventBus接收方法 ThreadMode.BACKGROUND result----:" + result);
Log.d("EventBusActivity", "EventBus接收方法 ThreadMode.BACKGROUND 线程----:" + Thread.currentThread().getName());
}
/**
* EventBus接收方法
* ThreadMode.ASYNC
*/
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onMessageAsyncEvent(MsgBean msgBean) {
if (msgBean == null) {
return;
}
String result = "msgType:" + msgBean.getMsgType() + " msgContent:" + msgBean.getMsgContent() + " msgId:" + msgBean.getMsgId();
Log.d("EventBusActivity", "EventBus接收方法 ThreadMode.ASYNC result----:" + result);
Log.d("EventBusActivity", "EventBus接收方法 ThreadMode.ASYNC 线程----:" + Thread.currentThread().getName());
}
/**
* onDestroy方法
*/
@Override
protected void onDestroy() {
super.onDestroy();
//EventBus注销
EventBus.getDefault().unregister(EventBusActivity.this);
}
}
<3> 结果
D/EventBusActivity: onClick发送EventBus 线程----:main
********************************************************************************
D/EventBusActivity: EventBus接收方法 ThreadMode.POSTING result----:msgType:onClick msgContent:我是从onClick发出的EventBus消息 msgId:123
D/EventBusActivity: EventBus接收方法 ThreadMode.POSTING 线程----:main
********************************************************************************
D/EventBusActivity: EventBus接收方法 ThreadMode.MAIN result----:msgType:onClick msgContent:我是从onClick发出的EventBus消息 msgId:123
D/EventBusActivity: EventBus接收方法 ThreadMode.MAIN 线程----:main
<4> 说明
[1] 代码中
ThreadMode.POSTING和ThreadMode.MAIN属性对应的方法添加了sticky = true 两者都收到了粘性事件。
ThreadMode.BACKGROUND属性对应的方法添加了sticky = false 没有收到粘性事件。
ThreadMode.ASYNC属性对应的方法没有添加 sticky 没有收到粘性事件。
也就是说设置false和不设置是一样的都收不到粘性事件。
[2] 此方式接收方的方法命名有一定的规范
方法必须是public修饰符修饰,不能用static关键字修饰,不能是抽象的(abstract)。
方法需要用@Subscribe注解进行修饰。
[3] @Subscribe注解 其实有三个参数
参数1:threadMode 决定接收方处于哪个线程中接收。
参数2:sticky 是否具有粘性事件。
参数3:priority 优先级 是一个整数类型的值,默认是0,值越大表示优先级越大。在某个事件被发布出来的时候,优先级较高的订阅方法会首先接受到事件。
前两个参数都说了 下面讲解第三个参数
七.事件接收优先级
<1> 代码
package com.example.myapplication.eventbus;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.example.myapplication.R;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
/**
* 模拟EventBus接收方
*/
public class EventBusActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_eventbus);
//先发送数据
TextView textView1 = findViewById(R.id.acivity_eventbus_textview1);
textView1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MsgBean msgBean = new MsgBean();
msgBean.setMsgType("onClick");
msgBean.setMsgContent("我是从onClick发出的EventBus消息");
msgBean.setMsgId("123");
EventBus.getDefault().postSticky(msgBean);
Log.d("EventBusActivity", "onClick发送EventBus 线程----:" + Thread.currentThread().getName());
}
});
TextView textView2 = findViewById(R.id.acivity_eventbus_textview2);
textView2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//注册EventBus
EventBus.getDefault().register(EventBusActivity.this);
}
});
}
/**
* EventBus接收方法
* ThreadMode.POSTING
*/
@Subscribe(threadMode = ThreadMode.POSTING, sticky = true, priority = 10)
public void onMessageDefaultEvent(MsgBean msgBean) {
if (msgBean == null) {
return;
}
String result = "msgType:" + msgBean.getMsgType() + " msgContent:" + msgBean.getMsgContent() + " msgId:" + msgBean.getMsgId();
Log.d("EventBusActivity", "EventBus接收方法 ThreadMode.POSTING result----:" + result);
Log.d("EventBusActivity", "EventBus接收方法 ThreadMode.POSTING 线程----:" + Thread.currentThread().getName());
}
/**
* EventBus接收方法
* ThreadMode.MAIN
*/
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true, priority = 1000)
public void onMessageMainEvent(MsgBean msgBean) {
if (msgBean == null) {
return;
}
String result = "msgType:" + msgBean.getMsgType() + " msgContent:" + msgBean.getMsgContent() + " msgId:" + msgBean.getMsgId();
Log.d("EventBusActivity", "EventBus接收方法 ThreadMode.MAIN result----:" + result);
Log.d("EventBusActivity", "EventBus接收方法 ThreadMode.MAIN 线程----:" + Thread.currentThread().getName());
}
/**
* onDestroy方法
*/
@Override
protected void onDestroy() {
super.onDestroy();
//EventBus注销
EventBus.getDefault().unregister(EventBusActivity.this);
}
}
<2> 结果
D/EventBusActivity: EventBus接收方法 ThreadMode.MAIN result----:msgType:onClick msgContent:我是从onClick发出的EventBus消息 msgId:123
D/EventBusActivity: EventBus接收方法 ThreadMode.MAIN 线程----:main
*******************************************************************************
D/EventBusActivity: EventBus接收方法 ThreadMode.POSTING result----:msgType:onClick msgContent:我是从onClick发出的EventBus消息 msgId:123
D/EventBusActivity: EventBus接收方法 ThreadMode.POSTING 线程----:main
...
说明
即 priority值越高 优先级越高 最先收到消息。
八.Gradle依赖方式需配置代码混淆
依照官方文档需要添加混淆,否则打包后的APK会有收不到消息的现象。
-keepattributes *Annotation*
-keepclassmembers class * {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# And if you use AsyncExecutor:
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
<init>(java.lang.Throwable);
}
九.EventBus的线程切换原理
由上可知 接收方的方法配置 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);
}
}
private final Poster mainThreadPoster;
private final BackgroundPoster backgroundPoster;
private final AsyncPoster asyncPoster;
final class BackgroundPoster implements Runnable, Poster {
private final PendingPostQueue queue;
private final EventBus eventBus;
private volatile boolean executorRunning;
BackgroundPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
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;
}
}
}
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);
}
}