您的位置:首页 > 其它

Eventbus的使用及源码分析

2017-03-29 10:45 417 查看
前言:Eventbus出来已经很长时间了,相信大家运用的应该很熟悉了!下面就和大家在一起复习一下它的知识点,也为没有使用过Eventbus,或者不了解源码的哥们们,大家在一起学习回顾一下!共同进步!

1.1:Eventbus介绍:

        eventbus是针对Android的一款订阅、发布事件总线。说简单一点就是线程间消息的传递,说到这里你肯定会想,对线程消息的传递我们为什么不去使用Intent、Handler、BroastCast,其实这就涉及到了Eventbus的优点-有效地降低消息发布者和订阅者之间的耦合度,开销小,代码更加通俗易懂。(马上你就明白



1.2:Eventbus的基本使用:

        1.2.1:下载Eventbus的源码:https://github.com/greenrobot/EventBus

        1.2.2:AS直接gradle编译:compile 'de.greenrobot:eventbus:3.0.0-beta1'1.3:Eventbus的代码使用:
       1.3.1:基本步骤:

                  (1)首先定义一个消息类:

public class MessageEvent {
......
}                   (2)在需要接受的页面进行世家你注册:
EventBus.getDefault().register(this);
                    (3)事件的产生,即可发送消息
EventBus.getDefault().post(messageEvent);
                   (4)在需要接受消息的页面里面实现onEvent方法 - 消息处理(这里面涉及到4个方法,下面会一一进行介绍)

@Subscribe
public void onEvent(MessageEvent messageEvent) {
...
}
在3.0之前,Eventbus还没有注解的使用方式,在3.0之后,消息处理可以随便取名,但是需要添加一个注解(Subscribe)如上,并指定线程,注意:修饰权限必须public
                    (5)进行反注册 - 取消订阅
EventBus.getDefault().unregister(this);基本上的使用思路和步骤差不多就如上了,呵呵!为了让我的小伙伴们更好的去理解,下面就开始写一个案列在来说明下(重复就是力量


          1.3.2:Eventbus简单案列

需求:两个Activity之间的消息发送。

分析:FristActivity中设计一个按钮和一个显示的TextView,点击按钮跳转SecondActivity,SecondActivity中也有一个按钮,点击按钮返回FristActivity,并将数据显示到TextView

FristActivity中布局(activity_fr
4000
ist.xml)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/btn_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="FristActivity"/>
<TextView
android:id="@+id/text_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
</LinearLayout> SecondActivity中的布局(activity_second.xml)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.harvic.try_eventbus_1.SecondActivity" >
<Button
android:id="@+id/btn_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Second Event"/>
</LinearLayout>
FristActivity代码实现:
package com.example.tryeventbus_simple;

import com.harvic.other.FirstEvent;

import de.greenrobot.event.EventBus;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class FristActivity extends Activity {

Button btn_1;
TextView tv;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EventBus.getDefault().register(this); //首先实现需要接受消息的页面注册EventBus (还记得上面的步骤吗?)
btn_1 = (Button) findViewById(R.id.btn_try);
tv = (TextView)findViewById(R.id.tv);

btn_1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(), SecondActivity.class);
startActivity(intent);
}
});
}
@Override
protected void onDestroy(){
super.onDestroy();
EventBus.getDefault().unregister(this); //反注册EventBus
}
 } 创建一个消息类:造时传进去一个字符串,然后可以通过getMsg()获取出来
public class MessageEvent {
private String mMsg;
public MessageEventString msg) {
mMsg = msg;
}
public String getMsg(){
return mMsg;
}
} SeconeActivity用来发送消息:
import com.harvic.other.FirstEvent;

import de.greenrobot.event.EventBus;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class SecondActivity extends Activity {
private Button btn_2;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
btn_2 = (Button) findViewById(R.id.btn_first_event);
btn_2.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v) {
EventBus.getDefault().post(new MessageEvent("SeconeActivity btn clicked")); //发送消息
}
});
}
} 好,关键一步来了,上面的步骤实现了消息的发送,但是总要有东西去接收吧,接收消息时,我们使用EventBus中最常用的onEventMainThread()函数来接收消息
在MainActivity中重写onEventMainThread(MessageEvent event),参数就是我们自己定义的类.

public void onEventMainThread(FirstEvent event) { //接受上面的发送的消息
String msg = "onEventMainThread收到了消息:" + event.getMsg();
tv.setText(msg);
}
小结:通过如上的步骤,外加一个小小的demo的演示,相信这会的你,肯定对于Eventbus有了很大的了解,呵呵!难度也不过如此嘛,但是你心里肯定有了很多问题,下面就来解决你内心的疑惑,来继续走起。。。。

1.4:上面的使用在接收消息的时候,为什么我们要在使用消息的页面写那个onEventMainEvent(),它到底有什么用?是否只可以写这一个方法呢?

         很显然, 当然不是了,要是这么简单的话, Eventbus还有可能会火起来吗?哈哈。。

          1.4.1:下面就来聊聊这个“四大金刚”:onEvent()、onEventMainThread()、onEventBackgroundThread()、onEventAsync()

               onEvent:如果是使用这个方法订阅事件, 那么该事件在那个线程里面发出来,onEvent()就会在这个线程里面运行,说简单一点就是发布线程和接受线程在一个     线程,还有一点需要注意的是,在onEvent()不能执行耗时的操作,会导致消息延迟。

                onEventMainThread:如果使用onEventMainThread作为订阅函数,那么不论事件是在哪个线程中发布出来的,onEventMainThread都会在UI线程中执行,接收事件就会在UI线程中运行,这个在Android中是非常有用的,因为在Android中只能在UI线程中跟新UI,所以在onEvnetMainThread方法中是不能执行耗时操作的

               
onEventBackground:如果使用onEventBackgrond作为订阅函数,那么如果事件是在UI线程中发布出来的,那么onEventBackground就会在子线程中运行,如果事件本来就是子线程中发布出来的,那么onEventBackground函数直接在该子线程中执行。

               
onEventAsync:使用这个函数作为订阅函数,那么无论事件在哪个线程发布,都会创建新的子线程在执行onEventAsync.

           1.4.2:上面的这些描述,百度的一大堆,但是我们必须要有丰衣足食的理念,哈哈!(当然是亲自实践一下)

既然有四个可以接收的, 那我们就实现这四个方法,看它怎么搞:

@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(new View.OnClickListener() {
@Override
public void onClick(View v) {
EventBus.getDefault().post(new MessageEvent());
}
});来,大家一起看日志:如下
2689-2689/com.eventbusdemo E/postEvent﹕ main
2689-2689/com.eventbusdemo E/PostThread﹕ main
2689-3064/com.eventbusdemo E/Async﹕ pool-1-thread-1
2689-2689/com.eventbusdemo E/MainThread﹕ main
2689-3065/com.eventbusdemo E/BackgroundThread﹕ pool-1-thread-2接下来我们在在子线程里发个看看:
findViewById(R.id.send).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
EventBus.getDefault().post(new MessageEvent());
}
}).start();
}
});啥都别讲了, 看日志:
3468-3945/com.eventbusdemo E/postEvent﹕ Thread-125
3468-3945/com.eventbusdemo E/PostThread﹕ Thread-125
3468-3945/com.eventbusdemo E/BackgroundThread﹕ Thread-125
3468-3946/com.eventbusdemo E/Async﹕ pool-1-thread-1
3468-3468/com.eventbusdemo E/MainThread﹕ main
So easy! 是吧!Eventbus的使用都在这里了,试想一下,我们如果在此基础上在去理解原理,那么的话,层次是不是就又上升了呢!哈哈!just do it。。。

1.5:Eventbus源码分析:

        1.5.1:就从register开始下手吧,


public void register(Object subscriber) { //register源码
Class<?> subscriberClass = subscriber.getClass();
// @Subscribe in anonymous classes is invisible to annotation processing, always fall back to reflection
boolean forceReflection = subscriberClass.isAnonymousClass();
List<SubscriberMethod> subscriberMethods =subscriberMethodFinder.findSubscriberMethods(subscriberClass, forceReflection);
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}相信一眼就可以看出,其实最核心的方法就是finidSubscriberMethods()找到所有的订阅方法,返回一个集合,下面我们来研究一下下:
        1.5.2:findSubscriberMethods()深入

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass, boolean forceReflection) {
String key = subscriberClass.getName(); //获取订阅者名称,在此基础上创建一个key
List<SubscriberMethod> subscriberMethods;
synchronized (METHOD_CACHE) {
subscriberMethods = METHOD_CACHE.get(key); //判断是否有缓存,有缓存则返回缓存
}
if (subscriberMethods != null) { //判断是否是第一次进来的
return subscriberMethods;
}
if (INDEX != null && !forceReflection) {
subscriberMethods = findSubscriberMethodsWithIndex(subscriberClass); //方法在下面
if (subscriberMethods.isEmpty()) {
subscriberMethods = findSubscriberMethodsWithReflection(subscriberClass); //方法在下面
}
} else {
subscriberMethods = findSubscriberMethodsWithReflection(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
synchronized (METHOD_CACHE) {
METHOD_CACHE.put(key, subscriberMethods);
}
return subscriberMethods;
}
}
private List<SubscriberMethod> findSubscriberMethodsWithIndex(Class<?> subscriberClass) {
Class<?> clazz = subscriberClass;                                   //反射的原理
while (clazz != null) {
SubscriberMethod[] array = INDEX.getSubscribersFor(clazz);
if (array != null && array.length > 0) {
List<SubscriberMethod> subscriberMethods = new ArrayList<SubscriberMethod>();
for (SubscriberMethod subscriberMethod : array) {            //创建一个新的订阅事件列表, 将之前的订阅事件存放进去
subscriberMethods.add(subscriberMethod);
}
return subscriberMethods;
} else {
String name = clazz.getName();
if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {//将系统的一些文件进行清除
// Skip system classes, this just degrades performance
break;
}
clazz = clazz.getSuperclass();
}
}
return Collections.EMPTY_LIST;
}

private List<SubscriberMethod> findSubscriberMethodsWithReflection(Class<?> subscriberClass) {
List<SubscriberMethod> subscriberMethods = new ArrayList<SubscriberMethod>();
Class<?> clazz = subscriberClass;
HashSet<String> eventTypesFound = new HashSet<String>();
StringBuilder methodKeyBuilder = new StringBuilder();
while (clazz != null) {
String name = clazz.getName();
if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {//清除系统文件
// Skip system classes, this just degrades performance
break;
}

// Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
Method[] methods = clazz.getDeclaredMethods();                                       //通过反射的方法拿到订阅者所有的方法
for (Method method : methods) {
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { //判断订阅者是否为public修饰,所以这是一个注意的
Class<?>[] parameterTypes = method.getParameterTypes();                      //获取订阅方法的参数
if (parameterTypes.length == 1) {                                            //参数的个数只能为1
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);   //获取所有的订阅事件
if (subscribeAnnotation != null) {
String methodName = method.getName();
Class<?> eventType = paramete
ca78
rTypes[0];                              //获取参数的类型,其实就是获取接受事件的类型
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(methodName);
methodKeyBuilder.append('>').append(eventType.getName());

String methodKey = methodKeyBuilder.toString();
if (eventTypesFound.add(methodKey)) { //封装一个订阅方法对象,这个对象包含Method对象eventtype // Only add if not already found in a sub class ThreadMode threadMode = subscribeAnnotation.threadMode(); subscriberMethods.add(new
SubscriberMethod(method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky())); } } } else if (strictMethodVerification) { if (method.isAnnotationPresent(Subscribe.class)) { String methodName = name + "." + method.getName();
throw new EventBusException("@Subscribe method " + methodName + "must have exactly 1 parameter but has " + parameterTypes.length); } } } else if (strictMethodVerification) { if (method.isAnnotationPresent(Subscribe.class)) { String methodName = name + "."
+ method.getName(); throw new EventBusException(methodName + " is a illegal @Subscribe method: must be public, non-static, and non-abstract"); } } } clazz = clazz.getSuperclass(); //遍历父类的订阅函数 } return subscriberMethods; }

1.5.3:对于subscriberMethod在注释里面都有,自己可以看一下即可!

1.5.4:可以发现每一个订阅的方法,对其调用sunscribe(),下面在看看研究下它:

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
subscribed = true;
Class<?> eventType = subscriberMethod.eventType; //从订阅方法中拿到订阅事件的类型
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); //找到所有的订阅(Subscription),订阅中包含了订阅者,订阅方法

Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority); //创建一个新的订阅

if (subscriptions == null) { //将新建的订阅加入到这个事件类型对应的所有订阅列表
subscriptions = new CopyOnWriteArrayList<Subscription>(); //判断该事件目前没有订阅列表,那么创建并加入该订阅
subscriptionsByEventType.put(eventType, subscriptions);
} else {
for (Subscription subscription : subscriptions) { //判断有订阅列表,检查是否已经加入过
if (subscription.equals(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
}
int size = subscriptions.size(); //根据优先级插入订阅
for (int i = 0; i <= size; i++) {
if (i == size || newSubscription.priority > subscriptions.get(i).priority) {
subscriptions.add(i, newSubscription);
break;
}
}
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber); //将这个订阅事件加入到订阅者的订阅事件列表中
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<Class<?>>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
if (sticky) { //粘性事件
Object stickyEvent;
synchronized (stickyEvents) {
stickyEvent = stickyEvents.get(eventType);
}
if (stickyEvent != null) {
postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());
}
}
} 其实到这里register的差不多就说完了:在来回顾一下,其实步骤很简单
第一:从注册的事件中找到所有的订阅方法

第二:遍历订阅方法,找到Eventtype对应的订阅列表,然后根据订阅者和订阅列表在创建一个新的订阅,在将他们全部都添加到这个新的订阅列表中

第三:找到Eventbus中sunscriber订阅的事件列表,将eventType加入事件列表

1.6:分析完了register(),下面我们在来分析一下post()是不是赶脚很给力....jia 油!

        1.6.1中 - post () - 方法中其实就是将事件放入队列,最核心的就是分发事件的这个方法postSingleEvent(eventQueue.remove(0), postingState); 

        1.6.2:下面我们就来研究一下,postSingleEvent这个方法

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<? extends Object> eventClass = event.getClass();

List<Class<?>> eventTypes = findEventTypes(eventClass); //获取eventClass对应事件,包含父类对应的事件和接口对应的事件
boolean subscriptionFound = false;
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(clazz); //获取订阅事件对应的订阅,这个是通过register加入的(即使之前注册的.)
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
postToSubscription(subscription, event, postingState.isMainThread); //对每个订阅调用该方法
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
subscriptionFound = true;
}
}
if (!subscriptionFound) { //如果没有订阅发现,所做的处理
Log.d(TAG, "No subscribers registered for event " + eventClass);
if (eventClass != NoSubscriberEvent.class && eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}         1.6.3:核心方法:postToSubscription(),please!


private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
//参数含义: 一:传入的订阅,二:分发事件,三:是否在主线程
switch (subscription.subscriberMethod.threadMode) {
case PostThread: //threadMode其实就是根据onEvent,onEventMainThread,onEventBackground,onEventAsync
invokeSubscriber(subscription, event); //本线程中调用订阅方法
break;
case MainThread:
if (isMainThread) {
invokeSubscriber(subscription, event); //主线程,直接在本现场中调用订阅方法
} else {
mainThreadPoster.enqueue(subscription, event); //不在主线程,那么通过handler实现在主线程中执行
}
break;
case BackgroundThread:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event); //主线程创建一个runnable放入线程池
} else {
invokeSubscriber(subscription, event); //子线程,则直接调用
}
break;
case Async:
asyncPoster.enqueue(subscription, event); //其它线程,直接放入线程池
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
} 其实如上的操作,也就对应起了之前的以OnEvent开头的四个方法,是不是。。。。哈哈! 你懂的!


欢迎留言交流
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: