EventBus 使用详解

EventBus 使用详解


概述

EventBus是一个Android事件发布/订阅框架,通过解耦发布者和订阅者简化Android事件传递,这里的事件可以理解为消息。事件传递既可以用于Android四大组件间通讯,也可以用于异步线程和主线程间通讯等。

传统的事件传递方式包括:Handler、BroadcastReceiver、Interface回调等
相比之下EventBus代码简洁,使用简单,并将事件发布和订阅充分解耦。


三个名词

  • **事件Event: **又可成为消息,其实就是一个对象,可以是网络请求返回的字符串,也可以是某个开关状态等等。事件类型EventType是指事件所属的Class。
    事件分为一般事件和Sticky事件,相对于一般事件,Sticky事件不同之处在于,当事件发布后,再有订阅者开始订阅该类型事件,依然能收到该类型事件的最近一个Sticky事件。

  • **订阅者Subscriber: **订阅某种事件类型的对象,当有发布者发布这类事件后,EventBus会执行订阅者的onEvent函数,这个函数叫事件响应函数。订阅者通过register接口订阅某个事件类型,unregister接口退订。订阅者存在优先级,优先级高的订阅者可以取消事件继续向优先级低的订阅者分发,默认所有订阅者优先级都为0。

  • **发布者Publisher: **发布某事件的对象,通过post接口发布事件。


基本使用

自定义一个事件类

public class AnyEventType {
     public AnyEventType(){}
 }

在要接受消息的页面注册

EventBus.getDefault().register(this);

接收消息的方法

@Subscribe
public void onEvent(AnyEventType event) {
    /* Do something */
};

发送消息

EventBus.getDefault().post(event);

取消注册

EventBus.getDefault().unregister(this);

例子

下面我们来实现一个具体的例子来介绍EventBus的基本使用。

在MainActivity中注册EventBus事件,并实现事件响应方法,当点击MainActivity中的按钮时跳转到SecondActivity,当点击SecondActivity中的按钮时向MainActivity发送Event事件,当MainActivity收到事件后,将事件内容显示在TextView中。

事件类Event

public class Event {

    private String messgae;

    public Event(String messgae) {
        this.messgae = messgae;
    }

    public String getMessgae() {
        return messgae;
    }
}

MainActivity

在OnCreate()函数中注册EventBus,在Ondestroy()函数中反注册。

public class MainActivity extends AppCompatActivity {

    @Bind(R.id.btn_open)
    Button mOpenBtn;

    @Bind(R.id.tv_showinfo)
    TextView mInfoTxt;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ButterKnife.bind(this);
        //注册
        EventBus.getDefault().register(this);

    }

    /**
     * 事件响应方法
     * 接收消息
     * @param event
     */
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent(Event event) {

        String msg = event.getMessgae();
        mInfoTxt.setText(msg);
    }

    //绑定点击事件
    @OnClick(R.id.btn_open)
    public void openSecondActivity(View view) {
            Intent intent = new Intent(MainActivity.this, SecondActivity.class);
            startActivity(intent);

    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        //反注册
        EventBus.getDefault().unregister(this);
    }
}

SecondActivity

public class SecondActivity extends AppCompatActivity {

    @Bind(R.id.btn_post)
    Button mPostBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        ButterKnife.bind(this);


        mPostBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //发送事件
                        EventBus.getDefault().post(new Event("Just do it"));
                    }
                }).start();

            }
        });
    }

}

EventBus的事件订阅函数

类别

在上面的例子中,我们再注解@Subscribe(threadMode = ThreadMode.MAIN)中使用了ThreadMode.MAIN这个模式,表示该函数在主线程即UI线程中执行,实际上EventBus总共有四种线程模式,分别是:

  • ThreadMode.MAIN:表示无论事件是在哪个线程发布出来的,该事件订阅方法onEvent都会在UI线程中执行,这个在Android中是非常有用的,因为在Android中只能在UI线程中更新UI,所有在此模式下的方法是不能执行耗时操作的。

  • ThreadMode.POSTING:表示事件在哪个线程中发布出来的,事件订阅函数onEvent就会在这个线程中运行,也就是说发布事件和接收事件在同一个线程。使用这个方法时,在onEvent方法中不能执行耗时操作,如果执行耗时操作容易导致事件分发延迟。

  • ThreadMode.BACKGROUND:表示如果事件在UI线程中发布出来的,那么订阅函数onEvent就会在子线程中运行,如果事件本来就是在子线程中发布出来的,那么订阅函数直接在该子线程中执行。

  • ThreadMode.AYSNC:使用这个模式的订阅函数,那么无论事件在哪个线程发布,都会创建新的子线程来执行订阅函数。

例子

如何调用不同的订阅函数

我们在基本使用章节的例子上进行扩展,首先建立四个类:FirstEvent、SecondEvent、ThirdEvent、FourthEvent。

FirstEvent

public class FirstEvent {

    private String messgae;

    public FirstEvent(String messgae) {
        this.messgae = messgae;
    }

    public String getMessgae() {
        return messgae;
    }
}

SecondEvent

public class SecondEvent {

    private String messgae;

    public SecondEvent(String messgae) {
        this.messgae = messgae;
    }

    public String getMessgae() {
        return messgae;
    }
}

ThirdEvent

public class ThirdEvent{

    private String messgae;

    public ThirdEvent(String messgae) {
        this.messgae = messgae;
    }

    public String getMessgae() {
        return messgae;
    }
}

FourthEvent

public class FourthEvent{

    private String messgae;

    public FourthEvent(String messgae) {
        this.messgae = messgae;
    }

    public String getMessgae() {
        return messgae;
    }
}

然后在MainActivity中,增加四种模式的订阅函数

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    @Bind(R.id.btn_open)
    Button mOpenBtn;

    @Bind(R.id.tv_showinfo)
    TextView mInfoTxt;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ButterKnife.bind(this);
        //注册
        EventBus.getDefault().register(this);

    }

    /**
     * 事件响应方法
     * @param event
     */
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEventMain(FirstEvent event) {

        String msg = event.getMessgae();
        Log.i(TAG, "onEventMain: " + event.getMessgae());
    }

    @Subscribe(threadMode = ThreadMode.POSTING)
    public void onEventPosting(SecondEvent event) {

        String msg = event.getMessgae();
        Log.i(TAG, "onEventPosting: "+ event.getMessgae());
    }

    @Subscribe(threadMode = ThreadMode.BACKGROUND)
    public void onEventBackgroud(ThirdEvent event) {

        String msg = event.getMessgae();
        Log.i(TAG, "onEventBackgroud: " + event.getMessgae());
    }

    @Subscribe(threadMode = ThreadMode.ASYNC)
    public void onEventAsync(FourthEvent event) {

        String msg = event.getMessgae();
        Log.i(TAG, "onEventAsync: " + event.getMessgae());
    }

    //绑定点击事件
    @OnClick(R.id.btn_open)
    public void openSecondActivity(View view) {
            Intent intent = new Intent(MainActivity.this, SecondActivity.class);
            startActivity(intent);

    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        //反注册
        EventBus.getDefault().unregister(this);
    }
}

接下来在SecondActivity中增加四个按钮,分别发送不同类别的事件

public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        ButterKnife.bind(this);
    }

    @OnClick(R.id.btn_post)
    public void onPostA() {
        EventBus.getDefault().post(new FirstEvent("FirstEvent"));
    }

    @OnClick(R.id.btn_post2)
    public void onPostB() {

        EventBus.getDefault().post(new SecondEvent("SecondEvent"));
    }

    @OnClick(R.id.btn_post3)
    public void onPostC() {

        EventBus.getDefault().post(new ThirdEvent("ThirdEvent"));
    }

    @OnClick(R.id.btn_post4)
    public void onPostD() {

        EventBus.getDefault().post(new FourthEvent("FourthEvent"));
    }
}

运行后,分别顺序点击SecondActivity的四个按钮,打印信息如下:

12-25 20:00:45.950 4111-4111/com.github.sadaharusong.eventbusdemo I/MainActivity: onEventMain: FirstEvent
12-25 20:00:47.528 4111-4111/com.github.sadaharusong.eventbusdemo I/MainActivity: onEventPosting: SecondEvent
12-25 20:00:48.882 4111-4352/com.github.sadaharusong.eventbusdemo I/MainActivity: onEventBackgroud: ThirdEvent
12-25 20:00:50.462 4111-4352/com.github.sadaharusong.eventbusdemo I/MainActivity: onEventAsync: FourthEvent

由此可见,通过发布不同的事件类的实例,EventBus根据类的实例分别调用了不同的订阅函数来处理事件。

那么,当同一个类的实例有多个函数订阅时,结果会是怎样呢?答案是,这些函数都会执行。下面我们来验证一下,将MainActivity中订阅函数的参数都改为FirstEvent,代码如下

/**
     * 事件响应方法
     * @param event
     */
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEventMain(FirstEvent event) {

        String msg = event.getMessgae();
        Log.i(TAG, "onEventMain: " + event.getMessgae());
    }

    @Subscribe(threadMode = ThreadMode.POSTING)
    public void onEventPosting(FirstEvent event) {

        String msg = event.getMessgae();
        Log.i(TAG, "onEventPosting: "+ event.getMessgae());
    }

    @Subscribe(threadMode = ThreadMode.BACKGROUND)
    public void onEventBackgroud(FirstEvent event) {

        String msg = event.getMessgae();
        Log.i(TAG, "onEventBackgroud: " + event.getMessgae());
    }

    @Subscribe(threadMode = ThreadMode.ASYNC)
    public void onEventAsync(FirstEvent event) {

        String msg = event.getMessgae();
        Log.i(TAG, "onEventAsync: " + event.getMessgae());
    }

运行程序,点击SecondActivity的FirstEvent按钮,打印信息如下:

12-25 20:02:45.025 24779-24779/com.github.sadaharusong.eventbusdemo I/MainActivity: onEventAsync: FirstEvent
12-25 20:02:45.652 24779-24779/com.github.sadaharusong.eventbusdemo I/MainActivity: onEventMain: FirstEvent
12-25 20:02:46.156 24779-24963/com.github.sadaharusong.eventbusdemoI/MainActivity: onEventPosting: FirstEvent
12-25 20:02:46.999 24779-24963/com.github.sadaharusong.eventbusdemo I/MainActivity: onEventBackgroud: FirstEvent

分析可知,当SecondActivity发送FirstEvent事件过来的时候,这个四个订阅函数会同时接收到这个事件并执行。

**总结: **订阅函数的执行是根据参数中的事件类的类名来决定的。


PS

优先级

如果在两个类中订阅同一个Event,不加优先级,是都会收到消息么?如果加了优先级,优先级高的先收到,那优先级低的还会收到么?

/** Subscriber priority to influence the order of event delivery.
* Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
* others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
* delivery among subscribers with different {@link ThreadMode}s! */

根据源码的注释信息 订阅了就都会收到

如何设置订阅者的优先级呢?

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

推荐阅读更多精彩内容

  • 概述 EventBus是一个Android事件发布/订阅框架,通过解耦发布者和订阅者简化Android事件传递,这...
    刘涤生阅读 78,337评论 6 57
  • 前言:EventBus出来已经有一段时间了,github上面也有很多开源项目中使用了EventBus。所以抽空学习...
    Kerry202阅读 1,282评论 1 2
  • 目录 1.概述 2.实战 1.基本框架搭建 2.新建一个类FirstEvent 3.在要接收消息的页面注册Even...
    慕涵盛华阅读 10,488评论 2 16
  • EventBus这个开源框架出来已经很久了,深的很多开发者青睐,由greenrobot组织贡献(该组织还贡献了gr...
    Scus阅读 2,230评论 0 0
  • 概述 EventBus是Android和Java的发布/订阅事件总线。 简化了组件之间的通信;将事件发送者和接收者...
    有没有口罩给我一个阅读 770评论 0 0