Android EventBus 使用詳解


概述

EventBus是一個Android端優化的publish/subscribe消息總線,它簡化了應用程序內各組件間、組件與后台線程間的通信。比如請求網絡,等網絡返回時通過Handler或Broadcast通知UI,兩個Fragment之間需要通過Listener通信,這些需求都可以通過 EventBus 實現,最常見的就是它可以代替Intent,Handler,BroadCase在Activity,Service,Fragment間傳遞消息

Eventbus作為事件總線,它主要有三個元素:

  1. Event:事件
  2. Subscriber:事件訂閱者,接收特定的事件
  3. Publisher:事件發布者,用於通知Subscriber有事件發生

介紹

首先我們來看一張圖,圖來自github(https://github.com/greenrobot/EventBus/blob/master/EventBus-Publish-Subscribe.png

這里寫圖片描述

我們可以看到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’

Eclipse用戶請下載EventBus的源碼導入(源碼地址:https://github.com/greenrobot/EventBus

實戰

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


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com