Android EventBus 使用詳解
概述
EventBus是一個Android端優化的publish/subscribe訊息匯流排,它簡化了應用程式內各元件間、元件與後臺執行緒間的通訊。比如請求網路,等網路返回時通過Handler或Broadcast通知UI,兩個Fragment之間需要通過Listener通訊,這些需求都可以通過 EventBus 實現,最常見的就是它可以代替Intent,Handler,BroadCase在Activity,Service,Fragment間傳遞訊息
Eventbus作為事件匯流排,它主要有三個元素:
- Event:事件
- Subscriber:事件訂閱者,接收特定的事件
- Publisher:事件釋出者,用於通知Subscriber有事件發生
介紹
我們可以看到Publisher(事件釋出)通過post方法將訊息發出去,Event事件在接受到會通知事件釋出者,然後會在事件釋出者中,查詢OnEvent函式來執行事件。
說到這裡,你一定會想到,這不就是觀察者模式。感興趣的同學可以閱讀EventBus的原始碼,由於本人功力不夠,這裡了暫且不對原始碼做分析了。關於EventBus有4個訂閱函式:
onEvent:
如果使用onEvent作為訂閱函式,那麼該事件在哪個執行緒釋出出來的,onEvent就會在這個執行緒中執行,也就是說釋出事件和接收事件執行緒在同一個執行緒。使用這個方法時,在onEvent方法中不能執行耗時操作,如果執行耗時操作容易導致事件分發延遲。
onEventMainThread:
如果使用onEventMainThread作為訂閱函式,那麼不論事件是在哪個執行緒中釋出出來的,onEventMainThread都會在UI執行緒中執行,接收事件就會在UI執行緒中執行,這個在Android中是非常有用的,因為在Android中只能在UI執行緒中跟新UI,所以在onEvnetMainThread方法中是不能執行耗時操作的。
onEventBackground:
如果使用onEventBackgrond作為訂閱函式,那麼如果事件是在UI執行緒中釋出出來的,那麼onEventBackground就會在子執行緒中執行,如果事件本來就是子執行緒中釋出出來的,那麼onEventBackground函式直接在該子執行緒中執行。
onEventAsync:
使用這個函式作為訂閱函式,那麼無論事件在哪個執行緒釋出,都會建立新的子執行緒在執行onEventAsync。
基本用法
AS使用者請新增依賴庫
compile ‘org.greenrobot:eventbus:2.4.0’
實戰
1.使用EventBus簡化AdApter,首先來看效果
我們在ListView點選了某一首音樂,在底部播放條需要顯示正在播放的節目節目資訊,需求就是這麼簡單。但是由於當前頁面是用Listview+gridview佈局,點選的是gridview子項的Item。一提到Item,你可能會說這不就是個點選事件嘛,太簡單了,寫個介面回撥不就行了。是的確實如此,但是今天我們將用更簡便的方法來實現它。
定義Event事件
import java.util.Map;
public class PlayEventMsg {
//這裡post的資訊為Map型別
Map<String, String> map;
public PlayEventMsg(Map<String, String> Map) {
map=Map;
}
public Map<String, String> getMsg(){
return map;
}
}
註冊訂閱者
//註冊訂閱者
EventBus.getDefault().register(this);
向訂閱者傳送訊息
//這是對應的Adapter的程式碼
viewholder.gridView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//返回listview的下標
int currpisition=(int) viewholder.gridView.getTag();
Map<String, String> Map=new HashMap<String,String>();
Map.put("currpisition", currpisition+"");
Map.put("position", position+"");
//把訊息psot出去
EventBus.getDefault().post(new PlayEventMsg(Map));
}
});
定義OnEvent()方法來接受訊息
// 處理點選事件
public void onEvent(PlayEventMsg msg) {
map = msg.getMsg();
currpisition = Integer.parseInt(map.get("currpisition"));
position = Integer.parseInt(map.get("position"));
}
最後我們根據Listview下標和gridview下標取出自己實體類中的物件在OnEvent事件中改變底部播放條的資訊就行了,效果如上,哦,提醒一下,別忘記取消事件
//取消註冊訂閱者
EventBus.getDefault().unregister(this);
用EventBus代替Intent傳值
老規矩先貼下效果圖,然後再介紹:
這裡的需求是在主頁面顯示歌曲資訊然後進入播放器的頁面,需要在播放器頁面顯示正在播放的音樂的資訊,然後在播放器頁面改變正在播放的音樂的資訊,同時回到主頁面底部播放條按照要求同樣也應該改變播放器的資訊。
1.定義Event事件,和上面步驟一樣
import java.util.Map;
public class OnBackEventMsg {
Map<String, String> map;
public OnBackEventMsg(Map<String, String> Map) {
map=Map;
}
public Map<String, String> getMsg(){
return map;
}
}
2.註冊觀察者
//註冊訂閱者
EventBus.getDefault().register(this);
3.向訂閱者傳送訊息,這裡主要看在從播放器頁面回到主頁面首先我從主頁面進入播放器頁面,這裡很簡單直接攜帶物件到播放器頁面就行我們在播放器頁面改變的歌曲資訊回到主頁面,注意回到主頁面,我們有兩種方法回去,一是直接按back鍵,二是自己定義的返回按鈕。好,接下來我們和上面步驟一樣向註冊者傳送訊息
EventBus.getDefault().post(new OnBackEventMsg(BackMap));
然而這卻出現了一個問題,我在自己寫的返回按鈕的點選事件中傳送了訊息,在主頁面卻收不到,這樣直接導致了播放資訊沒有改變,通過錯誤日誌,這和Activity的生命週期有關,播放器頁面是處於棧頂,按下返回鍵已經把當前的Activity銷燬了,然後定義返回按鈕我也是直接finish,為了是防止這頁面重新載入一遍,耗費流量。於是我修改了下程式碼:
我直接把post訊息寫在OnDestory裡 了
@Override
protected void onDestroy() {
Map<String, String> BackMap = new HashMap<String, String>();
BackMap.put("Name", MusicName);
BackMap.put("Duration", MusicDuration);
BackMap.put("imgUrl", MusicImgUrl);
SharedPreferenceUtil.set(GaiyaMusic.this, "PlayState", "State", isplay + "");
EventBus.getDefault().post(new OnBackEventMsg(BackMap));
super.onDestroy();
}
4.在主頁面的Event函式接受訊息
public void onEventMainThread(OnBackEventMsg msg) {
Map<String, String> bcakmap = msg.getMsg();
controlName = bcakmap.get("Name");
controlDuration = bcakmap.get("Duration");
controlimgUrl = bcakmap.get("imgUrl");
music_durations.setText(TimeSizeChange.generateTime(Long.parseLong(controlDuration)));
}
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
music_durations.setVisibility(View.GONE);
main_progress.setProgress(0);
currentime.setText("正在載入中...");
}
}, 100);
music_names.setText(controlName);
imgTools.getImgFromNetByUrl(Check.getImg_300_300(controlimgUrl), img_plays, R.drawable.gai);
}
}
5.取消訂閱者
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
後話
現在EventBus升級到了3.0.0了,相比2.4.0版本可以不用約定OnEvent方法開頭了,我們為其可以添加註解:
//傳送訊息
Map<String, String> BackMap = new HashMap<String, String>();
BackMap.put("Name", MusicName);
BackMap.put("Duration", MusicDuration);
BackMap.put("imgUrl", MusicImgUrl);
EventBus.getDefault().post(new OnBackEventMsg(BackMap));
//接受訊息
註解@Subscribe,翻譯過來為訂閱者,在內部傳入了threadMode,我們定義為ThreadMode.MainThread,譯為該方法在UI執行緒完成
@Subscribe(threadMode = ThreadMode.MainThread)
public void MyEventBus(OnBackEventMsg msg){
Map<String, String> bcakmap = msg.getMsg();
controlName = bcakmap.get("Name");
controlDuration = bcakmap.get("Duration");
controlimgUrl = bcakmap.get("imgUrl");
music_durations.setText(TimeSizeChange.generateTime(Long.parseLong(controlDuration)));
}
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
music_durations.setVisibility(View.GONE);
main_progress.setProgress(0);
currentime.setText("正在載入中...");
}
}, 100);
music_names.setText(controlName);
imgTools.getImgFromNetByUrl(Check.getImg_300_300(controlimgUrl), img_plays, R.drawable.gai);
}
}
}
好了,EventBus的基本使用就到這裡了,由於當中部分內容涉及到公司專案這裡就不貼程式碼了,更多的高階的用法可以閱讀EventBus的原始碼:https://github.com/greenrobot/EventBus