Android开源框架EventBus事件总线详解
2016-02-17 16:45
447 查看
一、 什么是EventBus
EventBus是一款针对Android优化的发布/订阅事件总线。主要功能是替代Intent,Handler,BroadCast在Fragment,Activity,Service,线程之间传递消息.。其实就是观察者模式的实现。EventBus牛逼的优点:是开销小,代码更优雅。以及将发送者和接收者解耦。是一个屌炸天的Android开源框架。
EventBus有如下三个要素:
1、Event:事件
2、Subscriber:事件订阅者,接收特定的事件。
3、Publisher:事件发布者,用于通知Subscriber有事件发生。
其中,Event可以使任意类型对象。Subscriber都是以约定的onEvent开头的函数,具体是 onEvent,onEventMainThread,onEventBackgroundThread,onEventAsync这四个(注意:从3.0.x开始不是必须的。而是用@Subscriber注解来指明执行的线程的。)。 Publisher可以通过post(Object)在任意线程任意位置发送事件。
二、 使用EventBus
首先需要在工程中添加依赖:(目前最新版本是3.0.0)compile 'org.greenrobot:eventbus:3.0.0'
3步使用EventBus:
1、 定义事件
public class MessageEvent {/*
添加需要的成员变量*/ }
2、 创建订阅者:
在onCreate或者构造方法中注册订阅者
eventBus.register(this);
实现订阅方法:
@Subscribe
public voidonEvent(AnyEventType event) {/* Do something */};
3、 发送事件
eventBus.post(event);
三、 第一个EventBus案例
案例:MainActivity中有一个Button和一个TextView,当点击Button的时候跳转到第二个Activity,第二个Activity中只有一个按钮,点击按钮给MainActivity发送信息,当MainActivity接收到信息之后,把收到的信息内容在TextView中展示。MainActivity布局:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="startActivity" android:text="打开第二个Activity" /> <TextView android:id="@+id/content" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
SecondActivity布局:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="postMsg" android:text="给第一个Activity发送信息" /> </LinearLayout>
重点来了:
1、创建消息事件类
package com.qianfeng.eventbusdemo; /** * 事件是简单的java对象(POJO---plain old Java object), * 没有任何特殊需求 */ public class MessageEvent{ private String message; public MessageEvent(String message){ this.message = message; } public String getMessage(){ return message; } }
2、在接收消息的页面中注册EventBus。因为我们本例是在Activity中接收消息,所以在Activity中注册。
@Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //获取默认的EventBus对象(单例),并把当前对象注册为Subscriber。 //注意:当销毁当前实例的时候必须注销这个Subscriber。一般与Activity的生命周期绑定 EventBus.getDefault().register(this);
content = (TextView) findViewById(R.id.content); }
@Override protected void onDestroy(){ super.onDestroy(); //注销当前Subscriber EventBus.getDefault().unregister(this); }
3、在SecondActivity中发送消息。
/** * 给第一个Activity发送信息 * @param view */ public void postMsg(View view){ EventBus.getDefault().post(new MessageEvent("这个是第二个Activity发出的消息")); }
4、在MainActivity中接收消息
/* 如果发送的消息类型是MessageEvent类型,则此方法会被回调。 这种方法被称为订阅方法或者订阅函数。 注意:从3.0.0版本开始,订阅方法必须添加注解@Subscribe 该方法的参数类型是MessageEvent类型,所以只要发送的消息的类型是MessageEvent 则不管方法名是什么都会回调。 换句话说:订阅方法的回调只和参数类型有关,与方法名无关。 但是出于可读性考虑,订阅方法一般用onXxxEvent来命名。 */ @Subscribe public void onEvent(MessageEvent event){ Log.d("MainActivity", "onEvent...."); content.setText(event.getMessage() + "hahahahah"); }
四、 EventBus深入
4.1 线程模式
考虑一个问题:我们的订阅方法是在哪个线程中运行的?能不能指定订阅方法的线程。很多时候我们需要一些代码在UI线程中执行,有些时候我们又需要一些代码在非UI线程中执行。这些问题,EventBus都做了考虑,使用EventBus可以非常优雅的满足我们的需要。如果实现?这就用到了一个注解:@Subscribe
先看@Subscribe这个注解的定义:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Subscribe { ThreadMode threadMode() default ThreadMode.POSTING; /** * If true, delivers the most recent sticky event (posted with * {@link EventBus#postSticky(Object)}) to this subscriber (if event available). */ boolean sticky() default false; /** 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! */ int priority() default 0; }
分析:
第一个属性是ThreadMode类型,ThreadMode是一个枚举类型,这个属性就决定了订阅方法到底应该允许在什么样的线程中。
ThreadMode共有4个值:
public enum ThreadMode { POSTING, MAIN, BACKGROUND, ASYNC }
四个值的含义如下:
POSTING: 表示订阅方法在发送消息所在的线程中运行。并且他也是默认值。
MAIN:表示订阅放在在主线程也就是UI线程中执行。
BACKGROUND:表示订阅方法会在子线程中运行。补充:如果在主线程中发送消息,则EventBus会开启一个新的线程去执行订阅方法。如果发送消息本身已经是在某个子线程中,则EventBus不会开启新的线程,直接在发送消息的线程中执行订阅方法。
ASYNC:异步模式。总是开启一个新的线程去回调订阅方法。
注意:如果发送线程和订阅方法所在线程在同一个线程的时候,当消息发送结束(即:post方法执行完),立即执行订阅方法,订阅方法执行结束后再去执行发送方其余的代码。 所以在涉及到UI线程的时候,务必小心不要阻塞UI线程。
再思考一个问题:如果有两个(或多个)订阅方法,并且他们的参数是一样的(接收同样的消息)。那么这两个订阅方法怎么执行?是都执行还是执行其中一个?如果都执行执行顺序是怎样的?
1、只要参数能够对应上,则两个或多个订阅方法都会执行。
2、如果多个订阅方法需要在同一个线程中执行(同步执行),则执行顺序是按照方法名的字母顺序来执行。
例如下面两个方法,则一定是先执行a方法,再执行b方法。
@Subscribe(threadMode= ThreadMode.POSTING)
public void a(MessageEvent event){
}
@Subscribe(threadMode = ThreadMode.POSTING)
public void b(MessageEvent event){
}
4.1 Sticky Event(粘性事件)
我们用post()发送的信息是一种实时的,也就是说只有发送信息前注册的Subscriber的订阅方法才可以收到,发送之后再注册Subscriber的订阅方法是无法收到的。但是,有时候我们需要保存最后一个信息,比如手机的当前位置信息。如果我们自己手动保存相对比较麻烦,EventBus的Sticky Event则帮助我们解决了这个问题。
使用Sticky Event则可以非常简介的帮助我们保存最后一个信息(某个类型),并且方便的获取。而且我们也快手动获取最后一个粘性事件(不用在订阅方法中获取)
1、发送Sticky Event
EventBus.getDefault().postSticky(new MessageEvent("这个是发送的粘性事件" + i++));
2、接收Sticky Event
回头看注解@Subscribe注解,第二个属性sticky就表明这个订阅方法是否处理sticky event。默认是false不处理。
@Subscribe(threadMode = ThreadMode.POSTING,sticky = true) public void onEvent(MessageEvent event){ Toast.makeText(this, "虽说我启动的晚,但是我也能收到信息:" + event.getMessage(), Toast.LENGTH_SHORT).show(); }
注意:如果fasle并不是代表如果发送的是粘性事件这个订阅方法就收不到这个事件,只是表示如果发送粘性服务前没有注册,那么这个订阅方法就再也不会收到这次这个事件了。换句话说:false的时候,对这个订阅方法来说,sticky event和普通的event的没有任何区别。
代码片段:
public void postMsg(View view){ EventBus.getDefault().postSticky(new MessageEvent("这个是发送的粘性事件" + i++)); Log.d("SecondActivity", "发送方所在线程:" + Thread.currentThread().getName()); Intent intent = new Intent(this, ThirdActivity.class); startActivity(intent); }
另外:我们可以在任何地方手动获取某个类型的最后一个sticky event
public void get(View view){ MessageEvent stickyEvent =
EventBus.getDefault().getStickyEvent(MessageEvent.class); Toast.makeText(this, "我是手动获取的粘性事件" + stickyEvent.getMessage(),
Toast.LENGTH_SHORT).show(); }
也可以删除某个类型的粘性事件,删除的同时会返回这个粘性事件。
MessageEvent stickyEvent =EventBus.getDefault().removeStickyEvent(MessageEvent.class);
// Better check that an event was actually posted before
if(stickyEvent !=null) {
// Now do something with it
}
注意:如果没有粘性事件,上面两个方法都会返回null。所以使用的时候最好做非空判断。
4.3 Event优先级和取消Event
参考官方文档:http://greenrobot.org/eventbus/documentation/priorities-and-event-cancellation/
其他一些不太核心的特点均可以参考官方文档。
相关文章推荐
- android滚轮式时间选择器
- Android Studio快捷键
- Android横竖屏切换小结
- Android的存储系统—Vold与MountService分析(三)
- 使用android新特性:Material Design
- android-Ultra-Pull-To-Refresh 源码解析
- Android命名趣事儿
- Android源码分析二:Android AIDL使用详解
- Android Studio中gradle使用解析
- Android Studio 中 .gitignore的模版
- android 的事件分发从源码分析
- MAC中设置android adb环境变量和用wifi真机调试
- Android 使用android-support-multidex解决Dex超出方法数的限制问题,让你的应用不再爆棚
- Android Context完全解析,你所不知道的Context的各种细节
- Android源码分析一:语言切换机制
- 【iOS-Android开发对照】之 数据存储
- 如何打log 检查 Android CTS failure
- android 动画
- 提高android应用的效率--主要讲解listview的优化
- Android Studio引用Github类库