Eventbus的使用及源码分析

前言: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_frist.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 = parameterTypes[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开头的四个方法,是不是。。。。哈哈! 你懂的!吐舌头
欢迎留言交流微笑


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值