EventBus使用详解 android
前言
最近在公司做一个类似于手机工厂模式的一个项目,用来检测其他各个App是否正常工作,所以要求是尽可能的轻量级,因为是检测其他App的工作,所以整个项目都是信息之间的频繁交互。在项目中存在很多Fragment和Fragment之间的信息交互,之前一直在用广播来实现但是广播使用起来较为麻烦且效率不高。在同事的建议下学习了EventBus,在此做一下学习记录,侵权请告知一定及时删除。
EventBus概述
EventBus是针一款对Android的发布/订阅事件总线。它可以让我们很轻松的实现在Android各个组件之间传递消息,并且代码的可读性更好,耦合度更低。
1.基本用法
示例:
当击StartSecondActivity按钮的时候,跳到第二个Activity,当点击第二个activity上面的SendMessage按钮的时候向第一个Activity发送消息,当第一个Activity收到消息后将消息放入textView中显示。
用法:
(1)先定义一个类
public class MessageEvent {
.......
}
- 1
- 2
- 3
- 4
(2)在需要接收消息的页面注册事件(类似于动态注册广播)
EventBus.getDefault().register(this);
- 1
(3)发送消息
EventBus.getDefault().post(new MessageEvent());
- 1
(4)处理消息( 在3.0之后,消息处理的方法可以随便取名,但是需要添加一个注解 @Subscribe,并且要指定线程模型(默认为PostThread),四种线程模型,下面会讲到。)
@Subscribe(threadMode = ThreadMode.PostThread)
public void XXX(MessageEvent messageEvent) {
...
}
- 1
- 2
- 3
- 4
(5)解除注册( 类似于动态广播的解除注册)
EventBus.getDefault().post(new MessageEvent());
- 1
整体的流程就是这样,注意:事件处理函数的访问权限必须为public,否则会报异常。
示例代码:
下面我将示例代码描述一下:
(1)布局
MainActivity布局(activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.liuhe.eventbusdemo.MainActivity">
<TextView
android:id="@+id/show_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="25sp"
android:text="Hello World" />
<Button
android:id="@+id/start_second"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start SecondActivity"/>
</LinearLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
新建一个SecondActivity(second.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="25sp"
android:text="This is SecondaAtivity's message"/>
<Button
android:id="@+id/send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Send Message"/>
</LinearLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
(2)创建一个MessageEvent类
public class MessageEvent {
private String message;
public MessageEvent(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
这个类很简单,只需要我们在构造方法内传入一个值,然后通过 getMessage()来获取出来。
(3)在要接受的消息的注册事件(别忘记在onDestroy()中解除注册)
@Subscribe(threadMode = ThreadMode.POSTING)
public void showMessageMainEvent(MessageEvent messageEvent){
show_message.setText("Message from SecondActivity" + "---" +messageEvent.getMessage());
}
- 1
- 2
- 3
- 4
(4)发送消息
EventBus.getDefault().post(new MessageEvent(message));
- 1
完整代码
(1)MainActivity
public class MainActivity extends AppCompatActivity {
private TextView show_message;
private Button start_second;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
show_message = (TextView) findViewById(R.id.show_message);
start_second = (Button) findViewById(R.id.start_second);
start_second.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
}
});
EventBus.getDefault().register(this);
}
@Subscribe(threadMode = ThreadMode.POSTING)
public void showMessagePOSTINGEvent(MessageEvent messageEvent){
show_message.setText("Message from SecondActivity" + "---" +messageEvent.getMessage());
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
(2)SecondActivity
public class SecondActivity extends Activity{
private Button button;
private TextView textView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second);
button = (Button) findViewById(R.id.send);
textView = (TextView) findViewById(R.id.message);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String message = textView.getText().toString();
if (TextUtils.isEmpty(message)){
message = "defaule message";
}
EventBus.getDefault().post(new MessageEvent(message));
}
});
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
这就是基本的使用了,上文中我们使用的是POSTING线程模型,下面介绍一下EventBus的四种线程模型。
2.四种线程模型(ThreadMode)
在写消息处理的时候,方法名字可以随意命名,但是需要通过@Subscribe注解一下当前使用的线程模型。EventBus的县城模型一共有四种:
- POSTING(默认)
如果使用事件处理函数指定了线程模型为PostThread,那么该事件在哪个线程发布出来的,事件处理函数就会在这个线程中运行,也就是说发布事件和接收事件在同一个线程。在线程模型为PostThread的事件处理函数中尽量避免执行耗时操作,因为它会阻塞事件的传递,甚至有可能会引起ANR。 - MAIN
不论事件是在哪个线程中发布出来的,该事件处理函数都会在UI线程中执行。该方法可以用来更新UI,但是不能处理耗时操作。 - BACKGROUND
如果事件是在UI线程中发布出来的,那么该事件处理函数就会在新的线程中运行,如果事件本来就是子线程中发布出来的,那么该事件处理函数直接在发布事件的线程中执行。在此事件处理函数中禁止进行UI更新操作。 - ASYNC
无论事件在哪个线程发布,该事件处理函数都会在新建的子线程中执行。同样,此事件处理函数中禁止进行UI更新操作。
示例:
1.将上面的例子加上三个处理消息的方法,分别指定为其余的三种线程模型,我们现在UI线程线程发送一条消息看看Log打印结果。
(1)MainActivity代码如下:
public class MainActivity extends AppCompatActivity {
private TextView show_message;
private Button start_second;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
show_message = (TextView) findViewById(R.id.show_message);
start_second = (Button) findViewById(R.id.start_second);
start_second.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
}
});
EventBus.getDefault().register(this);
}
@Subscribe(threadMode = ThreadMode.POSTING)
public void showMessagePOSTINGEvent(MessageEvent messageEvent){
show_message.setText("Message from SecondActivity" + "---" +messageEvent.getMessage());
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void showMessageMainEvent(MessageEvent messageEvent){
show_message.setText("Message from SecondActivity" + "---" +messageEvent.getMessage());
}
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void showMessageBACKGROUNDEvent(MessageEvent messageEvent){
show_message.setText("Message from SecondActivity" + "---" +messageEvent.getMessage());
}
@Subscribe(threadMode = ThreadMode.ASYNC)
public void showMessageASYNCEvent(MessageEvent messageEvent){
show_message.setText("Message from SecondActivity" + "---" +messageEvent.getMessage());
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
(2)Log如下:
D/MAIN: main
D/POSTING: main
D/ASYNC: pool-1-thread-1
D/BACKGROUND: pool-1-thread-2
- 1
- 2
- 3
- 4
- 5
从日志打印结果可以看出,如果在UI线程中发布事件,则线程模型为POSTING的事件处理函数也执行在UI线程,与发布事件的线程一致。线程模型为ASYNC的事件处理函数执行在名字叫做pool-1-thread-1的新的线程中。而MAIN的事件处理函数执行在UI线程,BACKGROUND的时间处理函数执行在名字叫做pool-1-thread-2的新的线程中。
2.我们在子线程发送一条消息看看Log情况
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
Log.d("post", Thread.currentThread().getName());
EventBus.getDefault().post(new MessageEvent("NewMesage"));
}
}).start();
}
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
Log如下:
D/post: Thread-153
D/BACKGROUND: Thread-153
D/POSTING: Thread-153
D/MAIN: main
D/ASYNC: pool-1-thread-1
- 1
- 2
- 3
- 4
- 5
从日志打印结果不难看出,如果在子线程中发布事件,则线程模型为POSTING的事件处理函数也执行在子线程,与发布事件的线程一致。BACKGROUND事件模型也与发布事件在同一线程执行。ASYNC则在一个名叫pool-1-thread-1的新线程中执行。MAIN还是在UI线程中执行。
3.黏性事件
黏性事件和我们的粘性广播很类似,就是在发送事件之后再订阅该事件也能收到该事件。
用法
(1)注册粘性事件
EventBus.getDefault().register(MainActivity.this);
- 1
(2)处理粘性事件
@Subscribe(sticky = true)
public void XXX(MessageEvent messageEvent) {
......
}
- 1
- 2
- 3
- 4
(3)发送粘性事件
EventBus.getDefault().postSticky(new MessageEvent("message"));
- 1
(4)解除粘性事件
EventBus.getDefault().unregister(MainActivity.this);
- 1
示例:
当点击Post Message按钮发送粘性消息,此时因为没有注册事件所以并没有收到任何东西,当点击Regist EventBus按钮进行注册时就会收到之前发送的粘性消息,并且此时我们每次点击发送都会收到消息,当我们点击解除注册Unregist EventBus按钮的时候,再次点击Post Message也不会收到任何消息了。
代码:
public class MainActivity extends AppCompatActivity {
private Button post_btn, regist_btn, unregist_btn;
private int index = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
post_btn = (Button) findViewById(R.id.post_message);
regist_btn = (Button) findViewById(R.id.regist_seventbus);
unregist_btn = (Button) findViewById(R.id.unregist_seventbus);
post_btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EventBus.getDefault().postSticky(new MessageEvent("message" +
"---" +index++));
}
});
regist_btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EventBus.getDefault().register(MainActivity.this);
}
});
unregist_btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EventBus.getDefault().unregister(MainActivity.this);
}
});
}
@Subscribe(threadMode = ThreadMode.POSTING, sticky = true)
public void showMessagePOSTINGEvent(MessageEvent messageEvent){
Log.d("POSTING",messageEvent.getMessage());
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void showMessageMainEvent(MessageEvent messageEvent){
Log.d("MAIN",messageEvent.getMessage());
}
@Subscribe(threadMode = ThreadMode.BACKGROUND, sticky = true)
public void showMessageBACKGROUNDEvent(MessageEvent messageEvent){
Log.d("BACKGROUND",messageEvent.getMessage());
}
@Subscribe(threadMode = ThreadMode.ASYNC, sticky = true)
public void showMessageASYNCEvent(MessageEvent messageEvent){
Log.d("ASYNC",messageEvent.getMessage());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
粘性事件就是这样可以收到注册之前发送的消息,但是它只能收到最新的一次消息,比如说在未注册之前已经发送了多条黏性消息了,然后再注册只能收到最近的一条消息。我们多次点击发送消息之后点击注册来看一下Log。
D/MAIN: message---12
D/POSTING: message---12
D/BACKGROUND: message---12
D/ASYNC: message---12
- 1
- 2
- 3
- 4
可以看到我们只收到了最后一次点击发送的消息。
尾语
学习到最后发现EventBus其实并不难,和我们之前用的广播很类似,同样需要注册和解除注册,处理消息的方法就类似于广播接收者的onReceive(),同样也可以选择粘性性质。