前言
最近在公司做一個類似于手機工廠模式的一個項目锐峭,用來檢測其他各個App是否正常工作中鼠,所以要求是盡可能的輕量級可婶,因為是檢測其他App的工作,所以整個項目都是信息之間的頻繁交互援雇。在項目中存在很多Fragment和Fragment之間的信息交互矛渴,之前一直在用廣播來實現但是廣播使用起來較為麻煩且效率不高。在同事的建議下學習了EventBus惫搏,在此做一下學習記錄具温,侵權請告知一定及時刪除。
EventBus概述
EventBus是針一款對Android的發(fā)布/訂閱事件總線筐赔。它可以讓我們很輕松的實現在Android各個組件之間傳遞消息铣猩,并且代碼的可讀性更好,耦合度更低茴丰。
1.基本用法##
示例:
當擊StartSecondActivity按鈕的時候达皿,跳到第二個Activity,當點擊第二個activity上面的SendMessage按鈕的時候向第一個Activity發(fā)送消息贿肩,當第一個Activity收到消息后將消息放入textView中顯示峦椰。
用法:
(1)先定義一個類
public class MessageEvent {
.......
}
(2)在需要接收消息的頁面注冊事件(類似于動態(tài)注冊廣播)
EventBus.getDefault().register(this);
(3)發(fā)送消息
EventBus.getDefault().post(new MessageEvent());
(4)處理消息( 在3.0之后,消息處理的方法可以隨便取名汰规,但是需要添加一個注解 @Subscribe汤功,并且要指定線程模型(默認為PostThread),四種線程模型溜哮,下面會講到滔金。)
@Subscribe(threadMode = ThreadMode.PostThread)
public void XXX(MessageEvent messageEvent) {
...
}
(5)解除注冊( 類似于動態(tài)廣播的解除注冊)
EventBus.getDefault().post(new MessageEvent());
整體的流程就是這樣色解,注意:事件處理函數的訪問權限必須為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>
新建一個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>
(2)創(chuàng)建一個MessageEvent類
public class MessageEvent {
private String message;
public MessageEvent(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
這個類很簡單冒签,只需要我們在構造方法內傳入一個值,然后通過 getMessage()來獲取出來钟病。
(3)在要接收消息的界面注冊事件(別忘記在onDestory()中解除注冊)
EventBus.getDefault().register(MainActivity.this);
(4)處理消息
@Subscribe(threadMode = ThreadMode.POSTING)
public void showMessageMainEvent(MessageEvent messageEvent){
show_message.setText("Message from SecondActivity" + "---" +messageEvent.getMessage());
}
(5)發(fā)送消息
EventBus.getDefault().post(new MessageEvent(message));
完整代碼
(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);
}
}
(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));
}
});
}
}
這就是基本的使用了萧恕,上文中我們使用的是POSTING線程模型,下面介紹一下EventBus的四種線程模型肠阱。
2.四種線程模型(ThreadMode)##
在寫消息處理的時候票唆,方法名字可以隨意命名,但是需要通過@Subscribe注解一下當前使用的線程模型屹徘。EventBus的縣城模型一共有四種:
- POSTING(默認)
如果使用事件處理函數指定了線程模型為PostThread走趋,那么該事件在哪個線程發(fā)布出來的,事件處理函數就會在這個線程中運行噪伊,也就是說發(fā)布事件和接收事件在同一個線程簿煌。在線程模型為PostThread的事件處理函數中盡量避免執(zhí)行耗時操作,因為它會阻塞事件的傳遞鉴吹,甚至有可能會引起ANR姨伟。 - MAIN
不論事件是在哪個線程中發(fā)布出來的,該事件處理函數都會在UI線程中執(zhí)行豆励。該方法可以用來更新UI夺荒,但是不能處理耗時操作。 - BACKGROUND
如果事件是在UI線程中發(fā)布出來的良蒸,那么該事件處理函數就會在新的線程中運行技扼,如果事件本來就是子線程中發(fā)布出來的,那么該事件處理函數直接在發(fā)布事件的線程中執(zhí)行嫩痰。在此事件處理函數中禁止進行UI更新操作剿吻。 - ASYNC
無論事件在哪個線程發(fā)布,該事件處理函數都會在新建的子線程中執(zhí)行串纺。同樣丽旅,此事件處理函數中禁止進行UI更新操作。
示例:
1.將上面的例子加上三個處理消息的方法造垛,分別指定為其余的三種線程模型魔招,我們現在UI線程線程發(fā)送一條消息看看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);
}
}
(2)Log如下:
D/MAIN: main
D/POSTING: main
D/ASYNC: pool-1-thread-1
D/BACKGROUND: pool-1-thread-2
從日志打印結果可以看出五辽,如果在UI線程中發(fā)布事件办斑,則線程模型為POSTING的事件處理函數也執(zhí)行在UI線程,與發(fā)布事件的線程一致。線程模型為ASYNC的事件處理函數執(zhí)行在名字叫做pool-1-thread-1的新的線程中乡翅。而MAIN的事件處理函數執(zhí)行在UI線程鳞疲,BACKGROUND的時間處理函數執(zhí)行在名字叫做pool-1-thread-2的新的線程中。
2.我們在子線程發(fā)送一條消息看看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();
}
});
Log如下:
D/post: Thread-153
D/BACKGROUND: Thread-153
D/POSTING: Thread-153
D/MAIN: main
D/ASYNC: pool-1-thread-1
從日志打印結果不難看出蠕蚜,如果在子線程中發(fā)布事件尚洽,則線程模型為POSTING的事件處理函數也執(zhí)行在子線程,與發(fā)布事件的線程一致靶累。BACKGROUND事件模型也與發(fā)布事件在同一線程執(zhí)行腺毫。ASYNC則在一個名叫pool-1-thread-1的新線程中執(zhí)行。MAIN還是在UI線程中執(zhí)行挣柬。
3.黏性事件##
黏性事件和我們的粘性廣播很類似潮酒,就是在發(fā)送事件之后再訂閱該事件也能收到該事件。
用法
(1)注冊粘性事件
EventBus.getDefault().register(MainActivity.this);
(2)處理粘性事件
@Subscribe(sticky = true)
public void XXX(MessageEvent messageEvent) {
......
}
(3)發(fā)送粘性事件
EventBus.getDefault().postSticky(new MessageEvent("message"));
(4)解除粘性事件
EventBus.getDefault().unregister(MainActivity.this);
示例:
當點擊Post Message按鈕發(fā)送粘性消息邪蛔,此時因為沒有注冊事件所以并沒有收到任何東西急黎,當點擊Regist EventBus按鈕進行注冊時就會收到之前發(fā)送的粘性消息,?并且此時我們每次點擊發(fā)送都會收到消息侧到,當我們點擊解除注冊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());
}
}
粘性事件就是這樣可以收到注冊之前發(fā)送的消息匠抗,但是它只能收到最新的一次消息故源,比如說在未注冊之前已經發(fā)送了多條黏性消息了,然后再注冊只能收到最近的一條消息戈咳。我們多次點擊發(fā)送消息之后點擊注冊來看一下Log心软。
D/MAIN: message---12
D/POSTING: message---12
D/BACKGROUND: message---12
D/ASYNC: message---12
可以看到我們只收到了最后一次點擊發(fā)送的消息。
尾語
學習到最后發(fā)現EventBus其實并不難著蛙,和我們之前用的廣播很類似,同樣需要注冊和解除注冊耳贬,處理消息的方法就類似于廣播接收者的onReceive()踏堡,同樣也可以選擇粘性性質。
參考資料
http://www.reibang.com/p/da9e193e8b03
http://blog.csdn.net/harvic880925/article/details/40660137