EventBus的使用和原理在網上有很多的博客了,其中泓洋大哥和啟艦寫的非常非常棒,我也是跟着他們的博客學會的EventBus,因為是第一次接觸並使用EventBus,所以我寫的更多是如何使用,源碼解析就不怎么樣啦!
如需轉載請注明出處:Android EventBus 3.0 實例使用詳解
一、概述
compile 'org.greenrobot:eventbus:3.0.0'
(2)在相關Activity中的onCreat()、onDestory()注冊和解注EventBus
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); EventBus.getDefault().register(this); } @Override protected void onDestroy() { super.onDestroy(); EventBus.getDefault().unregister(this); }
(3)事件發布者如何發布事件
EventBus.getDefault().post(實參);
我們到.getDefault()來看一個這個方法:
/** Convenience singleton for apps using a process-wide EventBus instance. */ public static EventBus getDefault() { if (defaultInstance == null) { synchronized (EventBus.class) { if (defaultInstance == null) { defaultInstance = new EventBus(); } } } return defaultInstance; }
這里的.getDefault()方法其實就是一個單例,獲取到EventBus實例后調用post方法開始發布事件
post():
/** Posts the given event to the event bus. */ public void post(Object event) { PostingThreadState postingState = currentPostingThreadState.get(); List<Object> eventQueue = postingState.eventQueue; eventQueue.add(event); if (!postingState.isPosting) { postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper(); 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; } } }
通過源碼可以看得出來,方法的實參是一個對象類型的參數,參數會統一存儲在eventQueue這個隊列中,然后循環隊列,將隊列中的消息逐一發布,這里大家可能會想,每次post都會去調用整個隊列么,那么不會造成方法多次調用么?
答案是不會的,因為在最外層加了一個判斷,判斷event是否被發布過,如果被發布過,則不會進入到這個方法內。
(4)事件訂閱者訂閱事件
@Subscribe public void onEventMainThread(實參){ //接收到發布者發布的事件后,進行相應的處理操作 }
這里要注意的是:EventBus在 3.0 版本后,事件訂閱監聽的方法名可以隨意起,不同於舊版本,名字是特定的。
public void onEventMainThread(param) {
//如果使用onEventMainThread作為訂閱函數,那么不論事件是在哪個線程中發布出來的,onEventMainThread都會在UI線程中執行,接收事件就會在UI線程中運行,
//這個在Android中是非常有用的,因為在Android中只能在UI線程中跟新UI,所以在onEvnetMainThread方法中是不能執行耗時操作的。 } public void onEventPostThread(param) { //如果使用onEvent作為訂閱函數,那么該事件在哪個線程發布出來的,onEvent就會在這個線程中運行,也就是說發布事件和接收事件線程在同一個線程。
//使用這個方法時,在onEvent方法中不能執行耗時操作,如果執行耗時操作容易導致事件分發延遲。 } public void onEventBackgroundThread(param) { //如果使用onEventBackgrond作為訂閱函數,那么如果事件是在UI線程中發布出來的,那么onEventBackground就會在子線程中運行,如果事件本來就是子線程中發布出來的,那么onEventBackground函數直接在該子線程中執行。 } public void onEventAsync(param) { //使用這個函數作為訂閱函數,那么無論事件在哪個線程發布,都會創建新的子線程在執行onEventAsync. }
但是新版本需要手動的添加注解@Subscribe(這是必不可少的)。既然名字可以隨意起,那么又怎么控制在什么線程內進行處理呢?
@Subscribe(threadMode = ThreadMode.MAIN)
給注解設置ThreadMode就可以了。
(5)事件發布者和訂閱者如何對應上
可以看到發布事件和訂閱事件,都需要傳入一個實參,而且在post方法中我們也看到了,這個實參是對象類型的,大家猜想的話也可以知道,發布和訂閱事件是通過一個對象實參來進行關聯的。
public class TestEvent { private int mMsg; public TestEvent(int msg) { mMsg = msg; } public int getMsg(){ return mMsg; } }
這個類很簡單,只有一個變量和一個構造方法、get方法。具體內容根據項目需求來定。
基本上了解這些就可以搞明白EventBus的使用了,下面上一下我寫的Demo,功能很簡單,就是模仿下載的進度條,因為現在用的最多的是通過handler來進行處理的,而EventBus的出現,可以完美的代替handler,
而且實現了解耦。
好,上代碼!
MainActivity.class
package com.example.wgh.eventbusdemo; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.ProgressBar; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; public class MainActivity extends Activity { public ProgressBar progressBar = null; public int time = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new Thread(new Runnable() { @Override public void run() { while (time<100){ time += 15; EventBus.getDefault().post(new TestEvent(time)); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } }); progressBar = (ProgressBar) findViewById(R.id.progressbar); EventBus.getDefault().register(this); } @Override protected void onDestroy() { super.onDestroy(); EventBus.getDefault().unregister(this); } @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(TestEvent event){ progressBar.setProgress(event.getMsg()); } }
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:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.wgh.eventbusdemo.MainActivity" android:orientation="vertical"> <ProgressBar android:id="@+id/progressbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="150dp" android:max="100" style="@style/Widget.AppCompat.ProgressBar.Horizontal"/> <Button android:id="@+id/button" android:layout_marginTop="10dp" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="開始下載"/> </LinearLayout>
TestEvent.class
public class TestEvent { private int mMsg; public TestEvent(int msg) { mMsg = msg; } public int getMsg(){ return mMsg; } }
補充知識點:粘性事件
public class SecondActivity extends Activity { private TextView textView = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); textView = (TextView) findViewById(R.id.test); EventBus.getDefault().register(this); } @Override protected void onDestroy() { super.onDestroy(); EventBus.getDefault().unregister(this); } @Subscribe(threadMode = ThreadMode.MAIN) public void wgh2(TestEvent event){ textView.setText("同樣接收到了msg"+event.getMsg()); } }
發現SecondActivity中沒有訂閱到發布的事件,查了下資料才知道,在MainActivity中發布了事件,但是到了SecondActivity中沒有訂閱到,這里就需要用到粘性事件了
所謂粘性事件指的就是事件發布之后再訂閱該事件,仍然可以收到該事件,這部分與普通事件的區別是:普通事件是先注冊再綁定
所以代碼方面就需要做一下調整
MainActivity:
EventBus.getDefault().postSticky(new TestEvent(time));
SecondActivity:
@Subscribe(threadMode = ThreadMode.MAIN,sticky = true) public void wgh2(TestEvent event){ textView.setText("同樣接收到了msg"+event.getMsg()); }
經過試驗之后發現就生效了!!!