进程内部消息处理之EventBus

EventBus简介:

说到进程内部的消息通信,第一时间就会想到Intent,Handler,BroadCast。
EventBus是一款针对Android优化的发布/订阅事件总线。
主要功能是替代Intent,Handler,BroadCast在Fragment,Activity,Service,线程之间传递消息.优点是开销小,代码更优雅。以及将发送者和接收者解耦。
要说明的是EventBus是一个进程范围内的发布/订阅时间总线,所以只能在一个进程内进行订阅和发布,如果你在新的进程里给Service/Activity发送消息,就无法收到消息了。
EventBus与LocalBroadcast 和观察者模式很类似,都可以实现进程内一对多的通信。

EventBus、广播和观察者的区别:

广播:
本地广播是三种方式中消耗时间、空间最多的一种方式,但也是同 android 相性最好的方式。因为广播属于 android 四大组件之一,在 BroadcastReceiver 中的 onReceive 方法中可以获得 Context、Intent 参数。持有这两个参数便可以调用许多 android sdk 中的方法。EventBus 和观察者,需要获得 Context 的话,往往都需要进行复杂的参数传递或者是依赖注入。
本地广播另外的一个优点是,许多系统级的事件都是使用广播来进行通知的,像常用的电量变化、网络状态变化、短信发送接收的状态等等。这就使得与 android 系统相关的通知,广播往往成了唯一的选择。
广播是重量级的、消耗资源较多的方式。广播的优势体现在它与 android sdk 链接的更紧密。对于不需要同 android 交互或是只做很少的交互的时候,就不推荐使用广播了。
并且在广播中有一个常见的坑:在 android 的 Application、Activity、Service、ContentProvider、BroadcastReceiver 中都可以获得对应的 Context,但它们并不完全相同。Activity 的 Context 所能做的事是最全的,而其它组件中的 Context 都或多或少的有着功能残缺。

Application和各个组件获取Context各自的功能

EventBus:
EventBus 作为 Android 开发中常用的框架,拥有着许多优点:调度灵活。不依赖于 Context,使用时无需像广播一样关注 Context 的注入与传递。父类对于通知的监听和处理可以继承给子类,这对于简化代码至关重要;通知的优先级,能够保证 Subscriber 关注最重要的通知;粘滞事件(sticky events)能够保证通知不会因 Subscriber 的不在场而忽略。可继承、优先级、粘滞,是 EventBus 比之于广播、观察者等方式最大的优点,它们使得创建结构良好组织紧密的通知系统成为可能。
使用简单。EventBus 的 Subscriber 注册非常简单,调用 eventBus 对象的 register 方法即可,如果不想创建 eventBus 还可以直接调用静态方法 EventBus.getDefault() 获取默认实例,Subscriber 接收到通知之后的操作放在 onEvent 方法里就行了。成为 Publisher 的过程就更简单了,只需要调用合适的 eventBus(自己创建的或是默认的)的 post 方法即可。

观察者模式:
由于观察者的实现比较简单,因此性能上是三者中最好的,但观察者难以控制通知的优先度,另外观察者模式要求观察者在事件发生时在场才能收到通知,这就使得观察者有可能遗漏事件也就是缺少Eventbus 优先级、粘滞事件的优点。
但有一个缺点是观察者独有的,那就是观察者可能会造成接口的膨胀。特别是当程序要求大量形式各异的通知,而程序员有没有做出良好的抽象时,代码中会包含大量的接口,接口数量的增长又会带来命名、注释等等一大堆问题。

Event的使用:

1.首先,在接受信息的Activity的OnCreate()和OnDestroy()方法注册和注销EventBus

EventBus.getDefault().register(this);/在OnCreate()中注册
EventBus.getDefault().unregister(this);//在OnDestroy()中注销

2.编写自定义类,里面封装好要发送和接受的信息

public class MainEvent{
 
         public String post = "";
     }

3.在接受信息的类中实现以下四个函数,各功能不同。EventBus3.0通过注解的方式,告知订阅函数运行在哪个线程中。

/**
     * 无论从那个线程发布的事件都会在UI线程中执行
     * ThreadMode.MAIN
     * @param event
     * 对应低版本的onEventMainThread方法
     */
    @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
    public void onEventMain(MainEvent event) {
        if(event != null){
            String frome = event.post;
            myTextView.setText(frome);
            Log.e("Test", "onEventMainThread = " + frome);
        }
    }

    /**
     * 无论从那个线程发布的事件,都在该线程中执行。
     * 所以需要注意,不能执行耗时操作,避免ANR
     * ThreadMode.POSTING
     * @param event
     * 对应低版本的onEvent
     */
    @Subscribe(threadMode = ThreadMode.POSTING, sticky = true)
    public void onEventPost(MainEvent event) {
        if(event != null){
            String frome = event.post;
            Log.e("Test", "onEventPostThread = " + frome);
        }
    }

    /**
     * 如果事件是从UI线程中发布出来,则在子线程中执行
     * 如果事件本身是从子线程中出来,则仍然在该子线程中执行
     * ThreadMode.BACKGROUND
     * @param event
     * 对应低版本的onEventBackgroundThread方法
     */
    @Subscribe(threadMode = ThreadMode.BACKGROUND, sticky = true)
    public void onEventBackground(MainEvent event) {
        if(event != null){
            String frome = event.post;
            Log.e("Test", "onEventBackgroundThread = " + frome);
        }
    }

    /**
     * 无论事件是从那个线程发布,都会另开一个线程执行
     * 所以该方法永远不会在UI线程中被执行
     * ThreadMode.ASYNC
     * 对应低版本的onEventAsync
     * @param event
     */
    @Subscribe(threadMode = ThreadMode.ASYNC, sticky = true)
    public void onEventAsync(MainEvent event) {
        if(event != null){
            String frome = event.post;
            Log.e("Test", "onEventAsync = " + frome);
        }

四个函数的区别:

  • ThreadMode.MAIN:无论从那个线程发布的事件都会在UI线程中执行。所以不能执行耗时操作。
  • ThreadMode.POSTING:无论从那个线程发布的事件,都在该线程中执行,不能执行耗时操作,避免ANR。
  • ThreadMode.BACKGROUND:在子线程中执行,如果发布事件的线程是子线程就在该线程中执行。如果发布事件的线程不是子线程就创建一个子线程执行。
  • ThreadMode.ASYNC:另开一个线程执行。

5.发布事件

EventBus.getDefault().post(MainEvent);

6.添加依赖

compile 'org.greenrobot:eventbus:3.0.0'

另外,EventBus3.0可以使用优先级,取消事件和滞留事件

订阅事件的优先级:
@Subscribe(threadMode = ThreadMode.MAIN,priority = 100) //在ui线程执行 优先级100
    public void onDataSynEvent(DataSynEvent event) {
        Log.e(TAG, "event---->" + event.getCount());
    }

在注解中添加priority, 事件的优先级类似广播的优先级,优先级越高优先获得消息。

终止事件往下传递:

EventBus也有类似有序广播中的终止广播往下传递的操作。只需在订阅事件中添加语句:

EventBus.getDefault().cancelEventDelivery(event) ;//优先级高的订阅者可以终止事件往下传递
粘性事件:

类似广播分类中的粘性广播。
1.EventBus粘性事件的注册和注销与普通事件的注册和注销是一样的。
2.粘性事件需要添加注解sticky = true:

@Subscribe(threadMode = ThreadMode.MAIN,sticky = true) //在ui线程执行
    public void onDataSynEvent(DataSynEvent event) {
        Log.e(TAG, "event---->" + event.getCount());
    }

3.发布粘性事件

EventBus.getDefault().postSticky(new DataSynEvent());

4.如果不再需要该粘性事件我们可以移除

EventBus.getDefault().removeStickyEvents(new DataSynEvent());

5.如果要取消全部的粘性事件

EventBus.getDefault().removeAllStickyEvents(new DataSynEvent());
EventBus processor使用:

EventBus提供了一个EventBusAnnotationProcessor注解处理器来在编译期通过读取@Subscribe()注解并解析,
处理其中所包含的信息,然后生成java类来保存所有订阅者关于订阅的信息,这样就比在运行时使用反射来获得这些订阅者的信息速度要快.
简单来说就是Event Bus的加速器。

1.在build.gradle中添加如下配置

buildscript {
    dependencies {
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
}
apply plugin: 'com.neenbedankt.android-apt'

dependencies {
    compile 'org.greenrobot:eventbus:3.0.0'
    apt 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
apt {
    arguments {
        eventBusIndex "com.whoislcj.eventbus.MyEventBusIndex"
    }
}

2.此时编译一次,自动生成生成索引类。在\build\generated\source\apt\PakageName\下看到通过注解分析生成的索引类。
3.在Application中启用加速模式,这样可以保证之后所有的EventBus都默认使用了加速模式。

EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();

具体一点:

public class MyApplication extends Application{
        private static Context context;
        @Override
        public void OnCreate(){
        EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();//在Application启动时加速
        context = getApplicationContext();
        }
        public static Context getContext(){
        return this.context;
        } 
}

参考文章:Android消息传递之EventBus 3.0使用详解

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

推荐阅读更多精彩内容