【Android實戰】EventBus 更少的代碼 更好的體驗

簡介

事件總線庫,極大地簡化了 Activities, Fragments, Threads, Services等各組件之間的通訊。更少的代碼,更好的體檢
這裏寫圖片描述
EventBus的github地址java

優勢

  • 簡化組件之間的通訊,對事件的發送者和接收者進行解耦;在Activity、Fragment、以及後臺線程中運轉良好;避免複雜和容易出錯的依賴關係以及生命週期問題
  • 使你的代碼更簡單,代碼可讀性更好
  • 小(~ 50K的jar包)
  • 在安裝量多達100,000,000+ 的應用中實踐,表現優異
  • 獨具特點的功能,如線程分發,優先級訂閱等。

    這裏寫圖片描述

項目實戰


這裏寫圖片描述

需求背景

  • 登陸、登出成功以後,涉及的相關頁面要即時刷新登陸數據,作出相應的調整
  • 多個頁面涉及比賽預定的狀態,一個頁面預定或者取消預定成功,要即時更新其它頁面的比賽預定狀態

步驟

部分概念相關的能夠參考下面的相關介紹穿插理解git

一、首先須要定義消息類,該類能夠不繼承任何基類也不須要實現任何接口github

這裏以LoginEvent(用戶登陸退出場景) 和AppointmentStateEvent (見註釋)爲例來介紹web

public class OnEventBusInterface {
    public interface OnLoginListener {
        void onEventMainThread(LoginEvent event);
    }

    public interface OnTopicRefreshListener {
        void onEventMainThread(FollowTopicRefreshEvent event);
    }

    public interface OnAppointmentStateListener {
        void onEventMainThread(AppointmentStateEvent event);
    }

    public static class LoginEvent {
        private boolean hasLoginSucc;

        public LoginEvent(boolean loginSucc) {
            this.hasLoginSucc = loginSucc;
        }

        public boolean hasLoginSucc() {
            return hasLoginSucc;
        }

        public void setHasLoginSucc(boolean hasLoginSucc) {
            this.hasLoginSucc = hasLoginSucc;
        }
    }

    //FollowFragment刷新事件
    public static class FollowTopicRefreshEvent {
    }

    /** * 在比賽詳情頁 * 比賽未開始 * 從首頁直播tab進入或者從球隊詳情頁的比賽tab進入 * 在比賽未開始時,能夠預定或者取消預定比賽,這個操做完成後再返回上面兩個入口時,須要刷新預定狀態 * 這裏傳入了matchId做爲參數,目前沒有用到,由於如今比賽的預定與否是存在本地數據庫的 * 若是之後預定比賽的狀態和用戶綁定在服務器,那麼可能就須要使用這個matchId了 * <p/> * 須要3個有關注狀態的地方 互相通知最新的關注狀態 * add By SuS */
    public static class AppointmentStateEvent {
        private String matchId;

        public AppointmentStateEvent(String matchId) {
            this.matchId = matchId;
        }

        public String getMatchId() {
            return matchId;
        }

        public void setMatchId(String matchId) {
            this.matchId = matchId;
        }
    }

二、在須要訂閱事件的地方註冊事件,在須要取消消息訂閱的地方取消消息訂閱數據庫

public class BaseLoginActivity extends BaseActivity implements OnEventBusInterface.OnLoginListener{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EventBus.getDefault().register(this);//註冊EventBus
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);//反註冊EventBus
    }

    protected void onLoginSuccess(){
    }
    protected void onLoginOut(){
    }

    public void onEventMainThread(OnEventBusInterface.LoginEvent event){
        if(event.hasLoginSucc()){
            onLoginSuccess();
        }else{
            onLoginOut();
        }
    }

}
public class BaseLoginFragment extends BaseFragment implements IHandlerMessage,OnEventBusInterface.OnLoginListener,OnEventBusInterface.OnTopicRefreshListener,OnEventBusInterface.OnAppointmentStateListener {
    protected Handler handler;

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        handler = new CommonHandler<BaseLoginFragment>(this);
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EventBus.getDefault().register(this);//註冊EventBus
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);//反註冊EventBus
    }

    @Override
    public void handlerCallback(Message msg) {
    }

    protected void showLoginWindow(){
        StormUtils2.startLoginActivity(getActivity());
    }

    protected void onLoginSuccess(){

    }

    protected void onLoginOut(){

    }

    protected void onFollowStatusChanged() {}

    protected void onAppointmentStateChanged(String matchId){

    }


    public void onEventMainThread(OnEventBusInterface.LoginEvent event){
        if(event.hasLoginSucc()){
            onLoginSuccess();
        }else{
            onLoginOut();
        }
    }

    public void onEventMainThread(OnEventBusInterface.FollowTopicRefreshEvent event){
        onFollowStatusChanged();
    }

    @Override
    public void onEventMainThread(OnEventBusInterface.AppointmentStateEvent event) {
        onAppointmentStateChanged(event.getMatchId());
    }
}

三、分發事件,即觸發消息服務器

這裏寫圖片描述

這裏對BaseActivity小作解釋:當經過插件化的方式加載暴風體育的時候,啓動登陸,經過startActivityForResult的方式調用主版的登陸(之因此調用主版的登陸是由於主應用和以插件化方式加載的暴風體育第三方登陸(QQ,微信)的簽名不一樣),當登陸成功獲取用戶信息後,分發用戶登陸成功的消息微信

代碼很簡潔,具體相關操做以下:ide

EventBus.getDefault().post(new OnEventBusInterface.LoginEvent(true));//登陸成功
EventBus.getDefault().post(new OnEventBusInterface.LoginEvent(false));//登出成功
EventBus.getDefault().post(new OnEventBusInterface.AppointmentStateEvent(String.valueOf(matchInfo.getId())));//比賽預定狀態改變成功

四、消息處理svg

這裏使用了最普通的方式,沒有使用EventBus的註解模式,並且考慮到收到消息後的處理都是在主線程中完成,因此採用了onEventMainThread方法。函數

繼承了BaseLoginActivity的Activity的相關處理:
這裏寫圖片描述

繼承了BaseLoginFragment的Fragment的相關處理:
這裏寫圖片描述

注意

  • 在3.0以前,EventBus尚未使用註解方式。消息處理的方法也只能限定於onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,分別表明四種線程模型。而在3.0以後,消息處理的方法能夠隨便取名,可是須要添加一個註解@Subscribe,而且要指定線程模型(默認爲PostThread)
  • 事件處理函數的訪問權限必須爲public,不然會報異常

相關代碼以下:

public void onEventMainThread(OnEventBusInterface.LoginEvent event){
        if(event.hasLoginSucc()){
            onLoginSuccess();
        }else{
            onLoginOut();
        }
    }

    public void onEventMainThread(OnEventBusInterface.FollowTopicRefreshEvent event){
        onFollowStatusChanged();
    }

    @Override
    public void onEventMainThread(OnEventBusInterface.AppointmentStateEvent event) {
        onAppointmentStateChanged(event.getMatchId());
    }


這裏寫圖片描述

相關介紹

線程模型

主要包括以下四種:
這裏寫圖片描述

POSTING

  • 發佈事件和接收事件在同一個線程
  • 避免執行耗時操做,不然會阻塞事件的傳遞,有可能會引發ANR
@Subscribe(threadMode = ThreadMode.POSTING) // ThreadMode is optional here
public void onMessage(MessageEvent event) {
    log(event.message);
}

MAIN

  • 不管事件從哪裏發佈,接收事件都在UI線程
  • 能夠用來更新UI,可是不能處理耗時操做
// Called in Android UI's main thread
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessage(MessageEvent event) {
    textField.setText(event.message);
}

BACKGROUND:

  • 發佈事件來自主線程,接收事件則在新的線程中運行;發佈事件來自子線 程,接收事件也在該子線程完成
  • 禁止進行UI更新操做
// Called in the background thread
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onMessage(MessageEvent event){
    saveToDisk(event.message);
}

ASYNC

  • 不管事件從哪裏發佈,接收事件都在新的子線程
  • 禁止進行UI更新操做
// Called in a separate thread
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onMessage(MessageEvent event){
    backend.send(event.message);
}

訂閱優先級以及事件取消

儘管大多數狀況下eventbus是不須要設置訂閱的優先級和事件取消,可是某些特殊的場景可能派上用場。例如,當應用程序在前臺,存在一個事件可能會觸發一些用戶界面相關邏輯,但當應用不可見時應該有不一樣的反應

優先級設置:

  • 默認優先級是0,一樣的線程分發模式,優先級更高的訂閱者會先收到消息
  • 不一樣線程模式的訂閱者接收消息的順序呢不受優先級影響
@Subscribe(priority = 1);
public void onEvent(MessageEvent event) {
…
}

事件取消

  • 事件取消通常都是被高優先級的訂閱者調用
  • 嚴格限制在線程模式爲POSTING的消息處理方法中
// Called in the same thread (default)
@Subscribe
public void onEvent(MessageEvent event){
// Process the event
…

EventBus.getDefault().cancelEventDelivery(event) ;
}

粘性事件

EventBus還支持發送黏性事件,就是在發送事件以後再訂閱該事件也能收到該事件,可以收到訂閱以前發送的消息。可是它只能收到最新的一次消息。

這裏不作過多介紹,詳情請參考Sticky Events

總結

  • 簡單強大
  • 實戰驗證
  • 高性能
  • 基於API的簡潔註解
  • 主線程和子線程都可進行消息發佈和訂閱
  • 事件以及訂閱者繼承特色
  • 零配置且可配

碼字!排版!畫圖!終於寫完了!


這裏寫圖片描述