(一)介绍
1,EvenetBus是一种发布-订阅事件总线.代码简洁,开销小,并很好的实现了发送者和接收者的解耦.(是一种观察者模式)
优点:
代码简单,速度快;
解耦;
体积小,jar大概50k;
稳定,大概有1亿+的应用中使用
2,三要素:
A,Event:事件,
B,Publisher:发布者,可以在任意线程发布事件
C,Subscrible:订阅者,
3,通常情况下安卓下数据的传递有下面几种方法:
3.1.通过intent传递,包括显式意图和隐式意图,广播(Broadcast)和服务都能通过Intent传递
传递的数据类型包括8大基本数据类型 实现Parcelable或Serializable接口的类型 以及集合数组类型
3.2.静态变量传递 在工具类下 声明一个Object类型的静态变量 在A中将要传递的值,在B中通过这个静态变量取出来
3.3.通过handle在不同的线程中传递Object类型的数据
3.4.通过构造方法传递Object类型的数据
3.5.通过SharedPreferences传递八大基本数据类型
3.6.通过ContentProvider在进程间共享数据
3.7.通过aidl在进程进程传递数据
3.8.通过流(本地文件)传递八大基本数据类型和实现Serializable接口的数据类型
3.9.通过基类中的属性或者方法
属性: 基类公有属性 在某个子类中赋值 其他子类中都能使用
方法: 子类调用父类的某个方法给父类某个属性赋值 另外一个子类通过父类的另一个公有方法获取这个值(这个方法把值返回)
(二)基本使用
##先订阅,后发布
- 1,添加依赖
compile 'org.greenrobot:eventbus:3.1.1'
- 2,注册事件
public class MessageEvent {
private String message;
public MessageEvent(String message){
this.message = message;
}
public String getMessage(){
return message;
}
}
- 3,在接受消息的代码
//注册成为订阅者
EventBus.getDefault().register(this);
@Override
protected void onDestroy() {
super.onDestroy();
//解除注册
EventBus.getDefault().unregister(this);
}
//订阅方法,当接收到事件的时候,会调用该方法
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent messageEvent){
Log.e("date","receive it");
Toast.makeText(ViewPageStep1Activity.this, messageEvent.getMessage(), Toast.LENGTH_SHORT).show();
}
- 4,在发送消息的地方
EventBus.getDefault().post(new MessageEvent("从fragment将数据传递到activity22222222"));
###先发布,再订阅,黏性事件
- 5,在接受消息的代码
//注册成为订阅者
EventBus.getDefault().register(this);
@Override
protected void onDestroy() {
super.onDestroy();
//解除注册
EventBus.getDefault().unregister(this);
}
//订阅方法,当接收到事件的时候,会调用该方法
@Subscribe(threadMode = ThreadMode.MAIN,stick = true)
public void onEvent(MessageEvent messageEvent){
Log.e("date","receive it");
Toast.makeText(ViewPageStep1Activity.this, messageEvent.getMessage(), Toast.LENGTH_SHORT).show();
}
- 6,在发送消息的地方
EventBus.getDefault().postSticky(new MessageEvent("从fragment将数据传递到activity22222222"));
如果使用onEvent作为订阅函数,那么该事件在哪个线程发布出来的,onEvent就会在这个线程中运行,也就是说发布事件和接收事件线程在同一个线程。使用这个方法时,在onEvent方法中不能执行耗时操作,如果执行耗时操作容易导致事件分发延迟。
如果使用onEventMainThread作为订阅函数,那么不论事件是在哪个线程中发布出来的,onEventMainThread都会在UI线程中执行,接收事件就会在UI线程中运行,这个在Android中是非常有用的,因为在Android中只能在UI线程中跟新UI,所以在onEvnetMainThread方法中是不能执行耗时操作的。
如果使用onEventBackgrond作为订阅函数,那么如果事件是在UI线程中发布出来的,那么onEventBackground就会在子线程中运行,如果事件本来就是子线程中发布出来的,那么onEventBackground函数直接在该子线程中执行。
使用这个函数作为订阅函数,那么无论事件在哪个线程发布,都会创建新的子线程在执行onEventAsync。
Subscribe注解
RetentionPolicy.SOUCE(编译时)
如果是RetentionPolicy.RUNTIME(运行时),那就可以通过反射拿到它的方法。
可以看到,发布者(Publisher
)使用post()
方法将Event
发送到Event Bus
,而后Event Bus
自动将Event
发送到多个订阅者(Subcriber
)。这里需要注意两个地方:
(1)一个发布者可以对应多个订阅者。
(2)3.0以前订阅者的订阅方法为onEvent()
、onEventMainThread()
、onEventBackgroundThread()
和onEventAsync()
。在Event Bus3.0
之后统一采用注解@Subscribe
的形式,具体实现方式见下文。
// 1主线程调用
@Subscribe(threadMode = ThreadMode.MAIN)
public void eventBusMain(String str){
Log.i("TAG", "MAIN:"+str+" Thread="+Thread.currentThread().getId());
}
// 2.发布线程为主线程,新开线程调用
// 2.发布线程为子线程,发布线程调用
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void eventBusBg(String str){
Log.i("TAG", "BACKGROUND:"+str+" Thread="+Thread.currentThread().getId());
}
//3,哪个线程发布,就在哪个线程调用
@Subscribe(threadMode = ThreadMode.POSTING)
public void eventBusPosting(String str){
Log.i("TAG", "POSTING:"+str+" Thread="+Thread.currentThread().getId());
}
// 4,每次都新开线程调用
@Subscribe(threadMode = ThreadMode.ASYNC)
public void eventBusAsync(String str){
Log.i("TAG", "ASYNC:"+str+" Thread="+Thread.currentThread().getId());
}
1,getDefault()
里面采用单利双重锁模式创建Eventbus对象
static volatile EventBus defaultInstance;
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
2,构造方法
2.1,粘性事件,保存到ConCurrenHashMap集合,(在构造方法中实现),
HashMap效率高,但线程不安全,在多线程的情况下,尽量用ConcurrentHashMap,避免多线程并发异常
EventBus(EventBusBuilder builder) {
logger = builder.getLogger();
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>(); //线程安全,
mainThreadSupport = builder.getMainThreadSupport();
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
logSubscriberExceptions = builder.logSubscriberExceptions;
logNoSubscriberMessages = builder.logNoSubscriberMessages;
sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
throwSubscriberException = builder.throwSubscriberException;
eventInheritance = builder.eventInheritance;
executorService = builder.executorService;
}
3,register()
注册,主要是为了获取目标activity或者fragment里,接收你传递信息的方法。比如你点击B页面按钮,将信息传递到A页面,你是在B页面注册,那注册时,就是为了获取A页面接收你信息的那个方法。
register()方法主要做了3件事(过程):
3.1,从缓存中获取订阅方法列表,
3.2,如果缓存中不存在,则通过反射获取到订阅者所有的函数,
3.3,遍历再通过权限修饰符.参数长度(只允许一个参数).注解(@Subscribe)来判断是否是具备成为订阅函数的前提
3.4,具备则构建一个SubscriberMethod(订阅方法,其相当于一个数据实体类,包含方法,threadmode,参数类型,优先级,是否粘性事件这些参数),循环结束订阅函数列表构建完成添加进入缓存
在RunTime期间使用反射对程序运行的性能有较大影响,EventBus3.0中增加了一个新特性:通过在编译期创建索引(SubscriberInfoIndex)就不会跑用反射获取订阅者方法,而是直接从getSubscriberInfo这个方法里面获取,提高程序运行性能,自己添加索引进行优化。
public void register(Object subscriber) {
Class> subscriberClass = subscriber.getClass();
//找到订阅者的方法.找出传进来的订阅者的所有订阅方法,然后遍历订阅者的方法.
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
//订阅者的注册
subscribe(subscriber, subscriberMethod);
}
}
}
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// 通过反射来获取订阅者中所有的方法
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
//
,并根据方法的类型,参数和注解找到订阅方法.
methods = findState.clazz.getMethods();
findState.skipSuperClasses = true;}
//通过CopyOnWriteArrayList保存Subscription,Subscription是一个封装类,封装了订阅者、订阅方法这两个类。
//Arraylist效率高,但线程不安全,在多线程的情况下,使用CopyOnWriteArrayList,避免多线程并发异常
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
3.5,如果是粘性事件,是先接收消息,再发送。
它就会获取接收消息,判断接收的消息是不是发送的消息。
isAssignableFrom()方法是判断是否为某个类的父类,是从类继承的角度去判断;
isAssignableFrom()方法的调用者和参数都是Class对象,调用者为父类,参数为本身或者其子类。(instanceof关键字是判断是否某个类的子类,是从实例继承的角度去判断)
stickyEvents是发送粘性事件时,报错的粘性事件
4.1,发送事件:从缓存中获取目标页面接收信息的方法,循环遍历,找到对应的方法,就将信息传递过去.
4.2,post调用后先使用ThreadLocal来存储事件,他可以隔离多个线程对数据的访问冲突(ThreadLocal是每个线程独享的,其数据别的线程是不能访问的,因此是线程安全的。)
4.3,,根据事件类型(也就是Post参数的类型)为key从subscriptionsByEventType获取订阅方法和订阅者
此时,我们已经具备了订阅者.订阅方法.待发送事件.post线程.threadmode等信息
,4.4,,根据线程和threadmode来判断
public class EventBus {
private final ThreadLocal currentPostingThreadState = new
ThreadLocal() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
/** For ThreadLocal, much faster to set (and get multiple values). */
final static class PostingThreadState {
final List
public void post(Object event) {
//PostingThreadState 保存着事件队列和线程状态信息
PostingThreadState postingState = currentPostingThreadState.get();
//获取事假队列,并将当期事件插入事件队列
List
5,unregister
取消事件订阅,移除缓存中的事件
public synchronized void unregister(Object subscriber) {
////根据当前的订阅者来获取它所订阅的所有事件
//typesBySubscriber是一个map集合,
//通过subscriber找到 subscribedTypes (事件类型集合),
List> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
//将subscriber对应的eventType从typesBySubscriber移除
typesBySubscriber.remove(subscriber);
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
private void unsubscribeByEventType(Object subscriber, Class> eventType) {
//根据事件类型从subscriptionsByEventType中获取相应的 subscriptions 集合
List subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {
int size = subscriptions.size();
//遍历所有的subscriptions,满足条件就删除
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
if (subscription.subscriber == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
借鉴:刘望舒先生的进阶之光
Android EventBus3.1.1从使用到源码解析
Android EventBus 使用详解
EventBus的使用,数据传递
EventBus 3.0进阶:源码及其设计模式 完全解析