Android 框架之EventBus(一)

EventBus 是一個(gè)Android端優(yōu)化的 publish/subscribe 消息總線,簡化了應(yīng)用程序各個(gè)組件間(Activity,Service,Fragment等),組件和后臺(tái)線程間的通信. 比如網(wǎng)絡(luò)請求 , 等網(wǎng)絡(luò)返回時(shí)通過Handler 或者 Broadcast 通知 UI , Fragment之間的通信 等功能都可以通過EventBus 實(shí)現(xiàn).

EventBus-Publish-Subscribe.png

相關(guān)鏈接 :

EventBus 基本使用

  • 在 build.gradle 中添加依賴,同步項(xiàng)目.
compile 'org.greenrobot:eventbus:3.0.0'
  • 定義傳遞小的消息實(shí)體類, 這個(gè)類的內(nèi)容可以自定義,比如我這里定義一個(gè)EventBusMsg類.
public class EventBusMsg {
    // 類的結(jié)構(gòu)是自定義的,我這類添加了一個(gè) String類型的 name 字段 方便測試.
    public String name;

    public EventBusMsg(String name) {
        this.name = name;
    }
}
  • 訂閱事件
    訂閱事件需要進(jìn)行三步設(shè)置,注冊,解注冊和定義事件接收函數(shù),比如我在MainActivity中接收事件進(jìn)行如下操作.

    • 在MainActivity 的onCreate()方法中注冊EventBus .
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          ...
          // 1. 注冊
          EventBus.getDefault().register(MainActivity.this);
      }
    
    • 在MainActivity 的onDestroy() 方法中 解注冊 EventBus . 一定不要忘記解注冊否則可能會(huì)造成內(nèi)存泄漏.
      @Override
      protected void onDestroy() {
          super.onDestroy();
          // 2. 解注冊
          EventBus.getDefault().unregister(MainActivity.this);
      }
    
    • 定義事件接收方法,方法名稱自定義,使用注解@Subscribe()標(biāo)示.
      @Subscribe(threadMode = ThreadMode.MAIN)
      public void onMessageEvent(EventBusMsg event) {
          Log.d(TAG, "接收到信息");
          tv_console.setText("EventBus 數(shù)據(jù) : " + event.name);
      }
    
    1. 使用 @Subscribe() 注解來表明這個(gè)方法是EventBus事件接收的方法.
    2. 事件接收方法的參數(shù)必須和EventBus發(fā)送的事件類是同一類型.比如我使用了自己定義的EventBusMsg類.
    3. 在 @Subscribe() 中可以設(shè)置threadMode 來設(shè)置不同的模式.關(guān)于threadMode下面會(huì)有講解,這里使用ThreadMode.MAIN表明這個(gè)方法運(yùn)行在主線程中.
  • 發(fā)布事件
    發(fā)布事件比較簡單調(diào)用EventBus的post方法即可, 我這里在EventBusSendActivity中發(fā)布事件因此我在EventBusSendActivity的Button的點(diǎn)擊事件中添加如下代碼.

public void sendMsg(View view) {
    // 發(fā)送消息
    EventBus.getDefault().post(new EventBusMsg("我是EventBus發(fā)送的數(shù)據(jù)"));
    // 銷毀當(dāng)前Activity
    finish();
}

到此我們就完成了EventBus的最基本使用 , 我在MainActivity中注冊了事件監(jiān)聽,然后跳轉(zhuǎn)到EventBusSendActivity 中發(fā)布事件,事件發(fā)布完成后銷毀EventBusSendActivity,此時(shí)跳轉(zhuǎn)到MainActivity中,使用一個(gè)TextView打印剛才發(fā)布的時(shí)間的name.
注意 : 上面這中用法一定要先訂閱事件在進(jìn)行發(fā)布事件才可以接收到事件,當(dāng)然也可以實(shí)現(xiàn)先發(fā)布在訂閱的模式,后面會(huì)介紹

EventBus 事件回調(diào)方法線程(Delivery Thread)

EventBus會(huì)處理事件接收線程的問題 , 比如可以將消息發(fā)送到另外一個(gè)線程中,比如 : 在 線程A 中發(fā)布事件. 事件傳遞到線程B中執(zhí)行. 這個(gè)功能主要用在處理更新UI的問題上,我們都知道在Android中只有在UI線程中才可以更新UI,否則會(huì)引發(fā)異常.但是一下耗時(shí)操作比如網(wǎng)絡(luò)請求等又必須在子線程中執(zhí)行, 此時(shí) EventBus就可以幫助我們處理UI和后臺(tái)任務(wù)的同步問題 . 就好像Android自帶的Handler 和 Broadcast機(jī)制一樣.
EventBus有四種線程模式 , 可以通過@Subscribe()注解的threadMode參數(shù)進(jìn)行指定.

  • ThreadMode : POSTING
    POSTING是 threadMode的默認(rèn)參數(shù),如果不設(shè)置該參數(shù)就是POSTING模式. 在這種模式下接收事件的方法和發(fā)布消息的方法運(yùn)行在同一個(gè)線程中.
    優(yōu)點(diǎn) : 這種模式下開銷最小,因?yàn)椴恍枰袚Q線程.
// 運(yùn)行在發(fā)布消息的線程中 (default)
// ThreadMode 是可選設(shè)置
@Subscribe(threadMode = ThreadMode.POSTING)
public void onMessage(MessageEvent event) {
    log(event.message);
}
  • ThreadMode : MAIN
    在 MAIN 模式下事件接收方法將會(huì)運(yùn)行在主線程中(有時(shí)也稱為UI線程). 如果發(fā)布事件的線程是主線程那么事件就會(huì)直接發(fā)布給訂閱者,不會(huì)進(jìn)行線程切換, 也就相當(dāng)于使用了POSTING模式.
    使用注意 : 在此模式不要在事件接收方法中進(jìn)行耗時(shí)操作,防止UI線程阻塞
// 運(yùn)行在 UI 線程中(主線程)
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessage(MessageEvent event) {
textField.setText(event.message);
}
  • ThreadMode : BACKGROUND
    事件接收方法將會(huì)運(yùn)行在一個(gè)后臺(tái)線程中,但是事件不可以并發(fā)執(zhí)行.
    • 如果發(fā)布事件本身就在后臺(tái)線程中,那么事件接收方法就運(yùn)行在這個(gè)線程中,不在另外開啟新的線程.
    • 如果發(fā)布事件在主線程中,那么EventBus會(huì)開一個(gè)后臺(tái)線程(只有一個(gè))然后在該線程中依次處理所有事件.
      使用注意 : 事件處理方法應(yīng)當(dāng)盡快返回,避免阻塞后臺(tái)線程.
// 運(yùn)行在后臺(tái)線程中.
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onMessage(MessageEvent event){
    saveToDisk(event.message);
}
  • ThreadMode : ASYNC
    在ASYNC模式下事件接收方法總是運(yùn)行在一個(gè)發(fā)布事件線程和主線程之外獨(dú)立的線程中,發(fā)布事件不必依賴訂閱事件處理結(jié)果. 因此此模式適合于在事件處理中有比較耗時(shí)的操作的情況.為了避免同一時(shí)間有過多的運(yùn)行線程.EventBus使用線程池和事件處理完成通知來限制最大的線程并發(fā)數(shù).
// 運(yùn)行在一個(gè)獨(dú)立的線程中.
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onMessage(MessageEvent event){
    backend.send(event.message);
}

EventBus 高級一點(diǎn)的用法 : 粘性事件(Stick Events)

在EventBus的基本使用中我們說過那種模式必須要先訂閱事件然后再發(fā)布事件. 這次我們來嘗試一下EventBus另一種用法 粘性事件,在這種模式允許先發(fā)布事件再訂閱事件 . EventBus會(huì)將粘性事件保存在內(nèi)存中然后發(fā)送給訂閱者,訂閱者也可以主動(dòng)查詢粘性事件.

  • 定義事件實(shí)體類
    同樣事件實(shí)體類是可以自定義的我這類定義了EventBusStickyMsg實(shí)體類.
public class EventBusStickyMsg {
    public String name;
    public EventBusStickyMsg(String name) {
        this.name = name;
    }
}
  • 發(fā)布粘性事件
    發(fā)布粘性事件使用 postSticky() 方法, 我在MainActivity中添加如下代碼.
public void sendStickyEvent(View view) {
    EventBus.getDefault().postSticky(new EventBusStickyMsg("我是Sticky消息"));
    // 啟動(dòng) EventBusSendActivity
    Intent intent = new Intent(MainActivity.this, EventBusSendActivity.class);
    MainActivity.this.startActivity(intent);
}
  • 訂閱粘性事件
    訂閱粘性事件和訂閱普通事件是一樣的.都需要進(jìn)行EventBus的注冊,解注冊以及事件接收函數(shù)
    • 注冊
     public void getStickyMsg(View view) {
          // 注冊,注意不要進(jìn)行多次注冊,否則程序可能崩潰.可以設(shè)置一個(gè)標(biāo)志.
          EventBus.getDefault().register(EventBusSendActivity.this);
      }
    

注意 : 不要進(jìn)行重復(fù)注冊,可能造成程序崩潰

  • 解注冊
  @Override
  protected void onDestroy() {
      super.onDestroy();
      // 移除所有的粘性事件.
      EventBus.getDefault().removeAllStickyEvents();
      // 解注冊
      EventBus.getDefault().unregister(EventBusSendActivity.this);
  }
  • 定義事件接收方法
  @Subscribe(sticky = true , threadMode = ThreadMode.MAIN)
  public void onStickyEvent(EventBusStickyMsg event) {
      tv_console.setText("Sticky 數(shù)據(jù) : " + event.name);
  }

粘性事件接收方法需要在@Subscribe()注解中添加sticky = true 參數(shù).

到此粘性事件的使用就完成了. 程序流程大概如下 :

  1. 在MainActivity中發(fā)布粘性事件.
  2. 在EventBusSendActivity中注冊事件.
  3. 在TextView上顯示事件.

上面我們使用的是訂閱方式獲取事件. 我們也可以手動(dòng)獲取粘性事件,我們添加一個(gè)Button在點(diǎn)擊事件中添加如下代碼

public void getStickyMsgManually(View view) {
    // 手動(dòng)獲取粘性事件
    EventBusStickyMsg msg = EventBus.getDefault().getStickyEvent(EventBusStickyMsg.class);
    if (msg != null) {
        tv_console.setText(tv_console.getText().toString() + msg.name);
        // 刪除事件
        EventBus.getDefault().removeStickyEvent(msg);
    }
}

我們首先使用getStickyEvent()方法獲取粘性事件 , 但是我們獲取了該粘性事件后 , EventBus并不會(huì)主動(dòng)刪除該粘性事件,(訂閱方式也不會(huì)刪除), 所以我們主動(dòng)調(diào)用了removeStickyEvent() 方法刪除粘性事件.

同時(shí)我們通過查看removeStickyEvent()方法返回值可以發(fā)現(xiàn) , 他會(huì)將刪除的事件返回,因此我們修改代碼如下.

public void getStickyMsgRemove(View view) {
    // 手動(dòng)獲取粘性事件
    EventBusStickyMsg msg = EventBus.getDefault().removeStickyEvent(EventBusStickyMsg.class);
    if (msg != null) {
        tv_console.setText(tv_console.getText().toString() + msg.name);
    }
}

OK 到此關(guān)于EventBus的基本使用已經(jīng)介紹完畢,我們主要介紹了一下三點(diǎn)內(nèi)容 :

  • EventBus普通事件的使用方法.
  • EvnetBus粘性事件的使用方法.
  • EventBus訂閱事件線程模式.

Demo 下載地址 : http://download.csdn.net/detail/qq_16188829/9766568

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末抡句,一起剝皮案震驚了整個(gè)濱河市吕漂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌身辨,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盒蟆,死亡現(xiàn)場離奇詭異接奈,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)兼蜈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門攘残,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人为狸,你說我怎么就攤上這事歼郭。” “怎么了钥平?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵实撒,是天一觀的道長。 經(jīng)常有香客問我涉瘾,道長知态,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任立叛,我火速辦了婚禮负敏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘秘蛇。我一直安慰自己其做,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布赁还。 她就那樣靜靜地躺著妖泄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪艘策。 梳的紋絲不亂的頭發(fā)上蹈胡,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機(jī)與錄音朋蔫,去河邊找鬼罚渐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛驯妄,可吹牛的內(nèi)容都是我干的荷并。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼青扔,長吁一口氣:“原來是場噩夢啊……” “哼源织!你這毒婦竟也來了翩伪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤雀鹃,失蹤者是張志新(化名)和其女友劉穎幻工,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體黎茎,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡囊颅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了傅瞻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片踢代。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖嗅骄,靈堂內(nèi)的尸體忽然破棺而出胳挎,到底是詐尸還是另有隱情,我是刑警寧澤溺森,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布慕爬,位于F島的核電站,受9級特大地震影響屏积,放射性物質(zhì)發(fā)生泄漏医窿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一炊林、第九天 我趴在偏房一處隱蔽的房頂上張望姥卢。 院中可真熱鬧,春花似錦渣聚、人聲如沸独榴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽棺榔。三九已至,卻和暖如春隘道,著一層夾襖步出監(jiān)牢的瞬間掷豺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工薄声, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人题画。 一個(gè)月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓默辨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親苍息。 傳聞我的和親對象是個(gè)殘疾皇子缩幸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容