EventBus使用详解

前言:EventBus出来已经有一段时间了,github上面也有很多开源项目中使用了EventBus。所以抽空学习顺便整理了一下。目前EventBus最新版本是3.0,所以本文是基于EventBus3.0的。

相关文章

EventBus使用详解

EventBus源码解析

概述

EventBus是针一款对Android的发布/订阅事件总线。它可以让我们很轻松的实现在Android各个组件之间传递消息,并且代码的可读性更好,耦合度更低。

如何使用

(1)首先需要定义一个消息类,该类可以不继承任何基类也不需要实现任何接口。如:

publicclassMessageEvent {    ......}

(2)在需要订阅事件的地方注册事件

EventBus.getDefault().register(this);

(3)产生事件,即发送消息

EventBus.getDefault().post(messageEvent);

(4)处理消息

@Subscribe(threadMode = ThreadMode.PostThread)publicvoidXXX(MessageEvent messageEvent){    ...}

在3.0之前,EventBus还没有使用注解方式。消息处理的方法也只能限定于onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,分别代表四种线程模型。而在3.0之后,消息处理的方法可以随便取名,但是需要添加一个注解@Subscribe,并且要指定线程模型(默认为PostThread),四种线程模型,下面会讲到。

注意,事件处理函数的访问权限必须为public,否则会报异常。

(5)取消消息订阅

EventBus.getDefault().unregister(this);

有何优点

采用消息发布/订阅的一个很大的优点就是代码的简洁性,并且能够有效地降低消息发布者和订阅者之间的耦合度。

举个例子,比如有两个界面,ActivityA和ActivityB,从ActivityA界面跳转到ActivityB界面后,ActivityB要给ActivityA发送一个消息,ActivityA收到消息后在界面上显示出来。我们最先想到的方法就是使用广播,使用广播实现此需求的代码如下:

首先需要在ActivityA中定义一个广播接收器:

publicclassMessageBroadcastReceiverextendsBroadcastReceiver{@Overridepublic void onReceive(Contextcontext,Intentintent) {        mMessageView.setText("Message from SecondActivity:"+ intent.getStringExtra("message"));    }}

还需要在onCreate()方法中注册广播接收器:

@OverrideprotectedvoidonCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);//注册事件EventBus.getDefault().register(this);//注册广播IntentFilter intentFilter =newIntentFilter("message_broadcast");    mBroadcastReceiver =newMessageBroadcastReceiver();    registerReceiver(mBroadcastReceiver, intentFilter);    ......}

然后在onDestory()方法中取消注册广播接收器:

@OverrideprotectedvoidonDestroy(){super.onDestroy();    ......//取消广播注册unregisterReceiver(mBroadcastReceiver);}

最后我们需要在ActivityB界面中发送广播消息:

findViewById(R.id.send_broadcast).setOnClickListener(newView.OnClickListener() {@OverridepublicvoidonClick(View v){        String message = mMessageET.getText().toString();if(TextUtils.isEmpty(message)) {            message ="defaule message";        }        Intent intent =newIntent();        intent.setAction("message_broadcast");        intent.putExtra("message", message);        sendBroadcast(intent);    }});

看着上面的实现代码,感觉也没什么不妥,挺好的!下面对比看下使用EventBus如何实现。

根据文章最前面所讲的EventBus使用步骤,首先我们需要定义一个消息事件类:

publicclassMessageEvent {privateStringmessage;publicMessageEvent(Stringmessage) {this.message = message;    }publicStringgetMessage() {returnmessage;    }publicvoidsetMessage(Stringmessage) {this.message = message;    }}

在ActivityA界面中我们首先需要注册订阅事件:

@OverrideprotectedvoidonCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);//注册事件EventBus.getDefault().register(this);    ......}

然后在onDestory()方法中取消订阅:

@OverrideprotectedvoidonDestroy(){super.onDestroy();//取消事件注册EventBus.getDefault().unregister(this);}

当然还要定义一个消息处理的方法:

@Subscribe(threadMode= ThreadMode.MainThread)public void onShowMessageEvent(MessageEvent messageEvent) {mMessageView.setText("MessagefromSecondActivity:"+messageEvent.getMessage());}

至此,消息订阅者我们已经定义好了,我们还需要在ActivityB中发布消息:

findViewById(R.id.send).setOnClickListener(newView.OnClickListener() {@OverridepublicvoidonClick(View v){        String message = mMessageET.getText().toString();if(TextUtils.isEmpty(message)) {            message ="defaule message";        }        EventBus.getDefault().post(newMessageEvent(message));    }});

对比代码一看,有人会说了,这尼玛有什么区别嘛!说好的简洁呢?哥们,别着急嘛!我这里只是举了个简单的例子,仅仅从该例子来看,EventBus的优势没有体现出来。现在我将需求稍微改一下,ActivityA收到消息后,需要从网络服务器获取数据并将数据展示出来。如果使用广播,ActivityA中广播接收器代码应该这么写:

publicclassMessageBroadcastReceiverextendsBroadcastReceiver{@OverridepublicvoidonReceive(Context context, Intent intent){newThread(newRunnable() {@Overridepublicvoidrun(){//从服务器上获取数据......                runOnUiThread(newRunnable() {@Overridepublicvoidrun(){//将获取的数据展示在界面上......                    }                });            }        }).start();    }}

看到这段代码,不知道你何感想,反正我是看着很不爽,嵌套层次太多,完全违反了Clean Code的原则。那使用EventBus来实现又是什么样呢?我们看一下。

@Subscribe(threadMode = ThreadMode.BackgroundThread)public void onGetDataEvent(MessageEvent messageEvent) {//从服务器上获取数据......EventBus.getDefault().post(new ShowMessageEvent());}@Subscribe(threadMode = ThreadMode.MainThread)public void onShowDataEvent(ShowMessageEvent showMessageEvent) {//将获取的数据展示在界面上......}

对比一下以上两段代码就能很明显的感觉到EventBus的优势,代码简洁、层次清晰,大大提高了代码的可读性和可维护性。我这只是简单的加了一个小需求而已,随着业务越来越复杂,使用EventBus的优势愈加明显。

常用API介绍

线程模型

在EventBus的事件处理函数中需要指定线程模型,即指定事件处理函数运行所在的想线程。在上面我们已经接触到了EventBus的四种线程模型。那他们有什么区别呢?

在EventBus中的观察者通常有四种线程模型,分别是PostThread(默认)、MainThread、BackgroundThread与Async。

PostThread:如果使用事件处理函数指定了线程模型为PostThread,那么该事件在哪个线程发布出来的,事件处理函数就会在这个线程中运行,也就是说发布事件和接收事件在同一个线程。在线程模型为PostThread的事件处理函数中尽量避免执行耗时操作,因为它会阻塞事件的传递,甚至有可能会引起ANR。

MainThread:如果使用事件处理函数指定了线程模型为MainThread,那么不论事件是在哪个线程中发布出来的,该事件处理函数都会在UI线程中执行。该方法可以用来更新UI,但是不能处理耗时操作。

BackgroundThread:如果使用事件处理函数指定了线程模型为BackgroundThread,那么如果事件是在UI线程中发布出来的,那么该事件处理函数就会在新的线程中运行,如果事件本来就是子线程中发布出来的,那么该事件处理函数直接在发布事件的线程中执行。在此事件处理函数中禁止进行UI更新操作。

Async:如果使用事件处理函数指定了线程模型为Async,那么无论事件在哪个线程发布,该事件处理函数都会在新建的子线程中执行。同样,此事件处理函数中禁止进行UI更新操作。

为了验证以上四个方法,我写了个小例子。

@Subscribe(threadMode= ThreadMode.PostThread)public void onMessageEventPostThread(MessageEvent messageEvent) {Log.e("PostThread",Thread.currentThread().getName());}@Subscribe(threadMode= ThreadMode.MainThread)public void onMessageEventMainThread(MessageEvent messageEvent) {Log.e("MainThread",Thread.currentThread().getName());}@Subscribe(threadMode= ThreadMode.BackgroundThread)public void onMessageEventBackgroundThread(MessageEvent messageEvent) {Log.e("BackgroundThread",Thread.currentThread().getName());}@Subscribe(threadMode= ThreadMode.Async)public void onMessageEventAsync(MessageEvent messageEvent) {Log.e("Async",Thread.currentThread().getName());}

分别使用上面四个方法订阅同一事件,打印他们运行所在的线程。首先我们在UI线程中发布一条MessageEvent的消息,看下日志打印结果是什么。

findViewById(R.id.send).setOnClickListener(newView.OnClickListener() {@OverridepublicvoidonClick(View v){            Log.e("postEvent", Thread.currentThread().getName());            EventBus.getDefault().post(newMessageEvent());        }    });

打印结果如下:

2689-2689/com.lling.eventbusdemoE/postEvent﹕ main2689-2689/com.lling.eventbusdemoE/PostThread﹕ main2689-3064/com.lling.eventbusdemoE/Async﹕ pool-1-thread-12689-2689/com.lling.eventbusdemoE/MainThread﹕ main2689-3065/com.lling.eventbusdemoE/BackgroundThread﹕ pool-1-thread-2

从日志打印结果可以看出,如果在UI线程中发布事件,则线程模型为PostThread的事件处理函数也执行在UI线程,与发布事件的线程一致。线程模型为Async的事件处理函数执行在名字叫做pool-1-thread-1的新的线程中。而MainThread的事件处理函数执行在UI线程,BackgroundThread的时间处理函数执行在名字叫做pool-1-thread-2的新的线程中。

我们再看看在子线程中发布一条MessageEvent的消息时,会有什么样的结果。

findViewById(R.id.send).setOnClickListener(newView.OnClickListener() {@OverridepublicvoidonClick(View v){newThread(newRunnable() {@Overridepublicvoidrun(){                    Log.e("postEvent", Thread.currentThread().getName());                    EventBus.getDefault().post(newMessageEvent());                }            }).start();        }    });

打印结果如下:

3468-3945/com.lling.eventbusdemo E/postEvent﹕ Thread-1253468-3945/com.lling.eventbusdemo E/PostThread﹕ Thread-1253468-3945/com.lling.eventbusdemo E/BackgroundThread﹕ Thread-1253468-3946/com.lling.eventbusdemo E/Async﹕ pool-1-thread-13468-3468/com.lling.eventbusdemo E/MainThread﹕ main

从日志打印结果可以看出,如果在子线程中发布事件,则线程模型为PostThread的事件处理函数也执行在子线程,与发布事件的线程一致(都是Thread-125)。BackgroundThread事件模型也与发布事件在同一线程执行。Async则在一个名叫pool-1-thread-1的新线程中执行。MainThread还是在UI线程中执行。

上面一个例子充分验证了指定不同线程模型的事件处理方法执行所在的线程。

黏性事件

除了上面讲的普通事件外,EventBus还支持发送黏性事件。何为黏性事件呢?简单讲,就是在发送事件之后再订阅该事件也能收到该事件,跟黏性广播类似。具体用法如下:

订阅黏性事件:

EventBus.getDefault().register(StickyModeActivity.this);

黏性事件处理函数:

@Subscribe(sticky =true)publicvoidXXX(MessageEvent messageEvent){    ......}

发送黏性事件:

EventBus.getDefault().postSticky(newMessageEvent("test"));

处理消息事件以及取消订阅和上面方式相同。

看个简单的黏性事件的例子,为了简单起见我这里就在一个Activity里演示了。

Activity代码:

publicclassStickyModeActivityextendsAppCompatActivity{intindex =0;@OverrideprotectedvoidonCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);        setContentView(R.layout.activity_sticky_mode);        findViewById(R.id.post).setOnClickListener(newView.OnClickListener() {@OverridepublicvoidonClick(View v){                EventBus.getDefault().postSticky(newMessageEvent("test"+ index++));            }        });        findViewById(R.id.regist).setOnClickListener(newView.OnClickListener() {@OverridepublicvoidonClick(View v){                EventBus.getDefault().registerSticky(StickyModeActivity.this);            }        });        findViewById(R.id.unregist).setOnClickListener(newView.OnClickListener() {@OverridepublicvoidonClick(View v){                EventBus.getDefault().unregister(StickyModeActivity.this);            }        });    }@Subscribe(threadMode = ThreadMode.PostThread, sticky =true)publicvoidonMessageEventPostThread(MessageEvent messageEvent){        Log.e("PostThread", messageEvent.getMessage());    }@Subscribe(threadMode = ThreadMode.MainThread, sticky =true)publicvoidonMessageEventMainThread(MessageEvent messageEvent){        Log.e("MainThread", messageEvent.getMessage());    }@Subscribe(threadMode = ThreadMode.BackgroundThread, sticky =true)publicvoidonMessageEventBackgroundThread(MessageEvent messageEvent){        Log.e("BackgroundThread", messageEvent.getMessage());    }@Subscribe(threadMode = ThreadMode.Async, sticky =true)publicvoidonMessageEventAsync(MessageEvent messageEvent){        Log.e("Async", messageEvent.getMessage());    }}

布局代码activity_sticky_mode.xml:

           

代码很简单,界面上三个按钮,一个用来发送黏性事件,一个用来订阅事件,还有一个用来取消订阅的。首先在未订阅的情况下点击发送按钮发送一个黏性事件,然后点击订阅,会看到日志打印结果如下:

15246-15246/com.lling.eventbusdemoE/PostThread﹕ test015246-15391/com.lling.eventbusdemoE/Async﹕ test015246-15246/com.lling.eventbusdemoE/MainThread﹕ test015246-15393/com.lling.eventbusdemoE/BackgroundThread﹕ test0

这就是粘性事件,能够收到订阅之前发送的消息。但是它只能收到最新的一次消息,比如说在未订阅之前已经发送了多条黏性消息了,然后再订阅只能收到最近的一条消息。这个我们可以验证一下,我们连续点击5次POST按钮发送5条黏性事件,然后再点击REGIST按钮订阅,打印结果如下:

6980-6980/com.lling.eventbusdemoE/PostThread﹕ test46980-6980/com.lling.eventbusdemoE/MainThread﹕ test46980-7049/com.lling.eventbusdemoE/Async﹕ test46980-7048/com.lling.eventbusdemoE/BackgroundThread﹕ test4

由打印结果可以看出,确实是只收到最近的一条黏性事件。

好了,EventBus的使用暂时分析到这里,例子代码从这里获取。下一讲将讲解EventBus的源码。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容

  • EventBus这个开源框架出来已经很久了,深的很多开发者青睐,由greenrobot组织贡献(该组织还贡献了gr...
    Scus阅读 2,230评论 0 0
  • 本文的EventBus,是指greenrobot的 EventBus, 主要以EventBus3.0 讲解; 什么...
    Simon_z阅读 5,147评论 2 2
  • 前言:EventBus出来已经有一段时间了,github上面也有很多开源项目中使用了EventBus。所以抽空学习...
    Lauren_Liuling阅读 48,427评论 23 155
  • 目录 1.概述 2.实战 1.基本框架搭建 2.新建一个类FirstEvent 3.在要接收消息的页面注册Even...
    慕涵盛华阅读 10,488评论 2 16
  • 前言 最近在公司做一个类似于手机工厂模式的一个项目,用来检测其他各个App是否正常工作,所以要求是尽可能的轻量级,...
    Luckily_Liu阅读 1,173评论 2 8