android 消息传递机制EventBus的深入探究

以前,对于activity和fragment之间的通信可以使用接口的方式,定义起来比较麻烦费事,偶然间发现可以使用EventBus,发现很好用,查看了一下官方说明:EventBus是针一款对Android的发布/订阅事件总线。它可以让我们很轻松的实现在Android各个组件之间传递消息,并且代码的可读性更好,耦合度更低。但是在用的过程中总会出现一些问题,下面就将出现的问题详细记录一下,顺便具体学习EventBus(GreenRobot)这个第三方开源库,了解它内部的实现原理,以至于出了问题可以快速定位修复。

官网: http://greenrobot.org/eventbus/documentation/

github: https://github.com/greenrobot/EventBus

以下使用都是基于EventBus3.0。

EventBus3.0使用

对于EventBus的原理呢,可以参照一下官网的这张图:

具体的使用方法可以看官网,很简单,简单罗列一下:

Step 1: Add Gradle

compile ‘org.greenrobot:eventbus:3.0.0‘

Step 2: Define events

public class MessageEvent {

    public final String message;

    public MessageEvent(String message) {
        this.message = message;
    }
}

Step 3: Prepare subscribers

Subscribers implement event handling methods (also called “subscriber methods”) that will be called when an event is posted. These are defined with the @Subscribe annotation.Note that with EventBus 3 the method name can be chosen freely (no naming conventions like in EventBus 2).

// This method will be called when a MessageEvent is posted (in the UI thread for Toast)
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
    Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}

Subscribers also need to register themselves to and unregister from the bus. Only while subscribers are registered, they will receive events. In Android, in activities and fragments you should usually register according to their life cycle:

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);
    super.onStop();
}

Step 4: Post events

EventBus.getDefault().post(new MessageEvent("Hello everyone!"));

以上便是官网给出的简单使用步骤,也不是很难,所以就不需要翻译了。接下来我们针对使用过程中出现的问题来进行一步一步的深入探究。

踩坑

我们先来使用一个简单的例子来总结。这个例子主要有三个activity,MainActivity、SecondActivity、ThirdActivity以及一个MessageEvent对象。我们在MainActivity、SecondActivity中分别注册了MessageEvent事件,在ThirdActivity中post MessageEvent事件,这样我们在MainActivity、SecondActivity中应该都能接受到该事件。下面是具体的代码。

第一个MainActivity

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        EventBus.getDefault().register(this);
        Button btn = (Button) findViewById(R.id.button2);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(MainActivity.this, SecondActivity.class));
            }
        });
    }

    //接收事件
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void fresh(MessageEvent messageEvent) {
        M2Log.d("MessageEvent -----------------> MainActivity");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        M2Log.d("MainActivity -----------------> onDestroy");
        EventBus.getDefault().unregister(this);
    }
}

第二个SecondActivity

public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        EventBus.getDefault().register(this);
        Button btn = (Button) findViewById(R.id.btn2);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(SecondActivity.this, ThirdActivity.class));
            }
        });
    }

    //接收事件
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void fresh(MessageEvent messageEvent) {
        M2Log.d("MessageEvent -----------------> SecondActivity");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        M2Log.d("SecondActivity -----------------> onDestroy");
        EventBus.getDefault().unregister(this);
    }
}

第三个ThirdActivity

public class ThirdActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);
        Button btn = (Button) findViewById(R.id.btn3);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //发送消息
                EventBus.getDefault().post(new MessageEvent(""));
                finish();
            }
        });
    }
}

打印输出结果

很显然,MainActivity和SecondActivity都接收到了MessageEvent事件。

细心的会发现,我们

EventBus.getDefault().register(this);

EventBus.getDefault().unregister(this);

注册生命周期是放在onCreate()和onDestroy()中的,如果我们按照官网上来,放在onStart()和onStop()中,你就会发现,我们接收不到MessageEvent事件,可以验证一下

 @Override
    protected void onStart() {
        super.onStart();
        M2Log.d("SecondActivity -----------------> onStart");
        EventBus.getDefault().register(this);
    }

    @Override
    protected void onStop() {
        super.onStop();
        M2Log.d("SecondActivity -----------------> onStop");
        EventBus.getDefault().unregister(this);
    }

结果是什么都不会打印,所以我们一般会将注册生命周期放到onCreate()和onDestroy()中去。

我们在开发过程中,你会发现有的时候会出现问题:

1、没有注册该事件

出现这种情况,大多数是没有注册该事件,什么意思呢?就是下面的类似代码没有写。

 @Subscribe(threadMode = ThreadMode.MAIN)
 public void fresh(MessageEvent messageEvent) {
        M2Log.d("MessageEvent -----------------> MainActivity");
 }

有的人写了类似的注册代码,但还是会报这个错误,那就涉及到注册的生命周期了。

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);
    super.onStop();
}

2、多次注册

这是我们在生命周期中注册该事件时多次注册造成的。解决方法很简单,可以判断一下

        //没有注册时再进行注册操作
        if (!EventBus.getDefault().isRegistered(this)){
            EventBus.getDefault().register(this);
        }

粘性事件Sticky Events

粘性事件类似于粘性广播,就是一次注册永久使用。

如何使用呢?类似于前面的,只不过加了一个sticky = true,发送时采用postSticky而已

  //发布事件
  EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!"));

在接收的时候添加一个sticky = true即可。

  //注册接收
  @Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
    public void fresh(MessageEvent messageEvent) {
        M2Log.d("MessageEvent -----------------> SecondActivity");
    }

    @Override
    protected void onStart() {
        super.onStart();
        M2Log.d("SecondActivity -----------------> onStart");
        EventBus.getDefault().register(this);
    }

    @Override
    protected void onStop() {
        super.onStop();
        M2Log.d("SecondActivity -----------------> onStop");
        EventBus.getDefault().unregister(this);
    }

我们前面出现过一个问题,那就是我们在onStart和onStop中注册,接收不到EventMessage,通过粘性事件,就可以解决这个问题。不过当你使用粘性事件时你会发现,每次进入注册该事件的activity中都会主动接收到该事件。

下面是我发送了一个粘性事件,我们在MainActivity 和 SecondActivity中会接收到该事件,我们退出APP后,再次进入,则又会接收到该事件。

清除粘性事件

MessageEvent stickyEvent = EventBus.getDefault().getStickyEvent(MessageEvent.class);
// Better check that an event was actually posted before
if(stickyEvent != null) {
   // "Consume" the sticky event
   EventBus.getDefault().removeStickyEvent(stickyEvent);
   // Now do something with it
}
/**
 * threadMode
 * 表示方法在什么线程执行 (Android更新UI只能在主线程, 所以如果需要操作UI, 需要设置ThreadMode.MainThread)
 *
 * sticky
 * 表示是否是一个粘性事件 (如果你使用postSticky发送一个事件,那么需要设置为true才能接受到事件)
 *
 * priority
 * 优先级 (如果有多个对象同时订阅了相同的事件, 那么优先级越高,会优先被调用.)
 * */
@Subscribe(threadMode = ThreadMode.MainThread, sticky = true, priority = 100)
public void onEvent(MsgEvent event){
}

上面便是EventBus3.0的常规用法,我们在知道了常规用法后还不行,必须深入了解一下它的内部实现原理,否则到时候出了问题后不知道该如何解决,要知其然而之所以然。下面我们便来分析一下它的源码。

源码解析(EventBus3.0)

源码解析部分主要从register、post、以及unregisger这三部分进行分析。

register分析

我们首先从注册入手,先分析

EventBus.getDefault()

进入源码:


static volatile EventBus defaultInstance;

/** Convenience singleton for apps using a process-wide EventBus instance. */
    public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }

EventBus是单例模式存在的,使用了双重判断的方式,防止并发的问题,还能极大的提高效率。接着进入register(this)进行分析

//**EventBus.class ---> register**

 /**
     * Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
     * are no longer interested in receiving events.
     * <p/>
     * Subscribers have event handling methods that must be annotated by {@link Subscribe}.
     * The {@link Subscribe} annotation also allows configuration like {@link
     * ThreadMode} and priority.
     */
    public void register(Object subscriber) {
        //反射调用,获取订阅者的类对象
        Class<?> subscriberClass = subscriber.getClass();

        //获取订阅者所有的订阅方法以@Subscribe为注解的一些public方法
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);

        //依次注册这些订阅方法
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                //对订阅方法进行注册
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

其中有获取订阅方法的代码

List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);

我们进入,分析一下,如何获取订阅方法。首先来看一下订阅方法的类

//**SubscriberMethod.class**

/** Used internally by EventBus and generated subscriber indexes. */
public class SubscriberMethod {
    final Method method; //方法
    final ThreadMode threadMode; //执行线程
    final Class<?> eventType; //接收的事件类型
    final int priority; //优先级
    final boolean sticky; //粘性事件
    /** Used for efficient comparison */
    String methodString;

    //...省略部分代码

    }

SubscriberMethod是一个订阅方法的实体类,里面存储了订阅方法的一些基本信息,订阅方法就是在类中以@Subscribe为注解的一些public方法,注意是public方法否则会报错,为什么是public方法我们下面会分析,给出原因,然后进入subscriberMethodFinder.findSubscriberMethods(subscriberClass),该代码的作用主要是获取当前类中所有的订阅方法。我们来看看是如何获取一个订阅者所有的订阅方法的:

//**SubscriberMethodFinder.class**

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {

        //从缓存中获取订阅方法
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);

        if (subscriberMethods != null) {
            return subscriberMethods;
        }

        //是否忽略注解器生成的MyEventBusIndex类
        if (ignoreGeneratedIndex) {
            //利用反射来获取订阅类中的订阅方法信息
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            //从注解器生成的MyEventBusIndex类中获得订阅类的订阅方法信息
            subscriberMethods = findUsingInfo(subscriberClass);
        }

        //当前类中没有订阅方法
        if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass
                    + " and its super classes have no public methods with the @Subscribe annotation");
        } else {
            //保存进缓存
            METHOD_CACHE.put(subscriberClass, subscriberMethods);
            return subscriberMethods;
        }
    }

对于获取我们注册的订阅方法,首先就是通过缓存来获取,如果没有的话则通过以下两种方式进行获取:

  1. EventBusAnnotationProcessor注解生成器在编译期通过读取@Subscribe()注解并解析,处理其中所包含的信息,然后生成java类来保存所有订阅者关于订阅的信息。
  2. 运行时使用反射来获得这些订阅者的信息

对于这两种方式的分析,可以参考http://www.jianshu.com/p/f057c460c77e这里面相关的内容。

对于第一种方法没什么好说的,我们来分析一下通过反射来获取这些订阅方法的方式,接下来分析通过反射获取当前类中的订阅方法

//**SubscriberMethodFinder.class**

private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {

        //FindState其实就是一个里面保存了订阅者和订阅方法信息的一个实体类,包括订阅类中所有订阅的事件类型和所有的订阅方法等。
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {

            //获取订阅方法
            findUsingReflectionInSingleClass(findState);
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }

代码不是太多,里面涉及到一个类FindState,我们来看下,这是什么东西,

static class FindState {
        //订阅方法
        final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
        //以event为key,以method为value
        final Map<Class, Object> anyMethodByEventType = new HashMap<>();
        //以method的名字生成一个methodKey为key,该method的类(订阅者)为value
        final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
        final StringBuilder methodKeyBuilder = new StringBuilder(128);

        Class<?> subscriberClass;
        Class<?> clazz;
        boolean skipSuperClasses;
        SubscriberInfo subscriberInfo;
        //...省略部分代码
}

这个FindState其实就是一个里面保存了订阅者和订阅方法信息的一个实体类,包括订阅类中所有订阅的事件类型和所有的订阅方法等。我们接着分析下面的代码。

findUsingReflectionInSingleClass(findState)

这行代码便是获取订阅方法列表的重要代码,我们进入查看一下:

//**SubscriberMethodFinder.class**

private void findUsingReflectionInSingleClass(FindState findState) {
        //方法
        Method[] methods;
        try {

            //获取当前类中所有的方法
            // This is faster than getMethods, especially when subscribers are fat classes like Activities
            methods = findState.clazz.getDeclaredMethods();
        } catch (Throwable th) {
            // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
            methods = findState.clazz.getMethods();
            findState.skipSuperClasses = true;
        }

        //遍历所有方法
        for (Method method : methods) {

            //获取方法的访问修饰权
            int modifiers = method.getModifiers();

            //订阅方法必须是must be public, non-static, and non-abstract

                //获取订阅方法参数类型
                Class<?>[] parameterTypes = method.getParameterTypes();

                //注解方法必须只有一个参数
                if (parameterTypes.length == 1) {

                    //获取订阅方法的注解
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);

                    //该订阅方法包含Subscribe注解
                    if (subscribeAnnotation != null) {
                        Class<?> eventType = parameterTypes[0];

                        //添加该注解方法
                        if (findState.checkAdd(method, eventType)) {
                            //该注解方法的线程模式
                            ThreadMode threadMode = subscribeAnnotation.threadMode();

                            //添加该注解方法
                            findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                    subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                        }
                    }
                } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                    String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                    throw new EventBusException("@Subscribe method " + methodName +
                            "must have exactly 1 parameter but has " + parameterTypes.length);
                }
            } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                throw new EventBusException(methodName +
                        " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
            }
        }
    }

可以看到,首先会得到订阅类的class对象并通过反射获取订阅类中的所有方法信息,然后通过筛选获取到订阅方法集合。这里面就解释了为什么要以@Subscribe为注解的方法,且必须是public类型,方法参数只有一个的原因。


//**Subscribe.java**

@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,方法执行的线程,POSTING(默认值)、MAIN、BACKGROUND、ASYNC

sticky,粘性时间,默认值false

priority,优先级,默认值0

该方法流程是:

  1. 拿到当前 class 的所有方法
  2. 过滤掉不是 public 和是 abstract、static、bridge、synthetic 的方法 过滤出方法参数只有一个的方法
  3. 过滤出被Subscribe注解修饰的方法
  4. 将 method 方法和 event 事件添加到 findState 中
  5. 将 EventBus 关心的 method 方法、event 事件、threadMode、priority、sticky 封装成SubscriberMethod 对象添加到 findState.subscriberMethods 列表中

通过上面几步,我们就可以获得了所订阅的方法,然后分别进行注册这些订阅方法。通过下面的代码来执行:

//参数:1订阅者 2订阅方法
subscribe(subscriber, subscriberMethod);

接着分析这个注册方法。

//**EventBus.java**
// Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {

        //订阅方法的参数类型,也是事件类型
        Class<?> eventType = subscriberMethod.eventType;

        //订阅方法描述,实体类(当前类中的订阅方法)
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);

        //获取当前类中的所有订阅方法
        CopyOnWriteArrayList<Subscription> 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);
            }
        }

        //总的所有的订阅方法
        int size = subscriptions.size();
        for (int i = 0; i <= size; i++) {

            //根据优先级,将订阅者插入到指定的位置
            if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                subscriptions.add(i, newSubscription);
                break;
            }
        }

        //获取订阅者所有订阅的事件类型
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        //将该事件类型添加到typesBySubscriber中
        subscribedEvents.add(eventType);

        //如果接收sticky事件,立即分发sticky事件
        if (subscriberMethod.sticky) {
            if (eventInheritance) {
                // Existing sticky events of all subclasses of eventType have to be considered.
                // Note: Iterating over all events may be inefficient with lots of sticky events,
                // thus data structure should be changed to allow a more efficient lookup
                // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
                Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
                for (Map.Entry<Class<?>, Object> entry : entries) {
                    Class<?> candidateEventType = entry.getKey();
                    if (eventType.isAssignableFrom(candidateEventType)) {
                        Object stickyEvent = entry.getValue();
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                Object stickyEvent = stickyEvents.get(eventType);
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }

这里面涉及到一些对象,我们分别注释一下:

//Subscription.java

//订阅者信息
final class Subscription {
final Object subscriber;//订阅者
final SubscriberMethod subscriberMethod;//订阅方法
}

//subscriptionsByEventType
key订阅方法类型 values 所有订阅了该类型的订阅者集合
Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;

//typesBySubscriber
key订阅者 values订阅事件集合
Map<Object, List<Class<?>>> typesBySubscriber;

了解了这几个对象,上面的代码就很容易看懂了,

  1. 首先获取订阅方法的参数类型即订阅事件类型
  2. 根据订阅事件类型获取该事件类型的所有订阅者
  3. 将该订阅者添加到该事件类型的订阅者集合中即:subscriptionsByEventType
  4. 获取订阅者所有的订阅事件类型
  5. 将该事件类型添加到该订阅者的订阅事件类型集中即:typesBySubscriber

事件post分析

分析了注册事件后,我们来分析一下分发事件post的流程,首先通过

EventBus.getDefault().post(new MessageEvent(""));

这行代码进行事件消息的分发,我们进入到post中详细了解一下这个流程。

/** Posts the given event to the event bus. */
    public void post(Object event) {
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event);

        if (!postingState.isPosting) {
            postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
            postingState.isPosting = true;
            if (postingState.canceled) {
                throw new EventBusException("Internal error. Abort state was not reset");
            }
            try {
                //分发事件
                while (!eventQueue.isEmpty()) {
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

代码虽然不长,但是不大好理解,这里面多了一些不常见的对象,我们来看下,首先对于第一行代码:

 PostingThreadState postingState = currentPostingThreadState.get();

这里面的PostingThreadState是什么意思呢?

/** For ThreadLocal, much faster to set (and get multiple values). */
    final static class PostingThreadState {
        //当前线程的事件队列
        final List<Object> eventQueue = new ArrayList<Object>();
        //是否有事件正在分发
        boolean isPosting;
        //post的线程是否是主线程
        boolean isMainThread;
        Subscription subscription;
        Object event;
        boolean canceled;
    }

PostingThreadState中包含了当前线程的事件队列,就是当前线程所有分发的事件都保存在eventQueue事件队列中以及订阅者订阅事件等信息,有了这些信息我们就可以从事件队列中取出事件分发给对应的订阅者。

我们接着分析,对于这个当前线程的事件队列,我们是通过currentPostingThreadState.get();来得到的,对于这个currentPostingThreadState又是什么呢?

private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
        @Override
        protected PostingThreadState initialValue() {
            return new PostingThreadState();
        }
    };

ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,而这段数据是不会与其他线程共享的。可以看出currentPostingThreadState的实现是一个包含了PostingThreadState的ThreadLocal对象,这样可以保证取到的都是自己线程对应的数据。

接着就通过postSingleEvent(eventQueue.remove(0), postingState);来对事件进行分发。

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        if (eventInheritance) {
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();
            for (int h = 0; h < countTypes; h++) {
                Class<?> clazz = eventTypes.get(h);
                subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
            }
        } else {
            subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
        }
        if (!subscriptionFound) {
            if (logNoSubscriberMessages) {
                Log.d(TAG, "No subscribers registered for event " + eventClass);
            }
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                post(new NoSubscriberEvent(this, event));
            }
        }
    }

事件的分发最后还要通过postSingleEventForEventType它来执行,

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
            //根据事件类型获取所有的订阅者
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        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;
                }
            }
            return true;
        }
        return false;
    }

//将事件分发给对应的订阅者
 private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
            case POSTING:
                invokeSubscriber(subscription, event);
                break;
            case MAIN:
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case BACKGROUND:
                if (isMainThread) {
                    backgroundPoster.enqueue(subscription, event);
                } else {
                    invokeSubscriber(subscription, event);
                }
                break;
            case ASYNC:
                asyncPoster.enqueue(subscription, event);
                break;
            default:
                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }
    }

代码比较简单,它们最终是通过发射调用来将事件分发给对应的订阅者的:

void invokeSubscriber(Subscription subscription, Object event) {
        try {
            subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
        } catch (InvocationTargetException e) {
            handleSubscriberException(subscription, event, e.getCause());
        } catch (IllegalAccessException e) {
            throw new IllegalStateException("Unexpected exception", e);
        }
    }

以上便是事件的分发过程,我们总结概括一下:

  1. 首先获取当前线程的PostingThreadState对象从而获取到当前线程的事件队列
  2. 通过事件类型获取到所有订阅者集合
  3. 通过反射执行订阅者中的订阅方法

unregister分析

最后我们来分析一下取消订阅的方法:

 /** Unregisters the given subscriber from all event classes. */
    public synchronized void unregister(Object subscriber) {
       //获取订阅者的所有订阅的事件类型
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
            for (Class<?> eventType : subscribedTypes) {
            //从事件类型的订阅者集合中移除订阅者
                unsubscribeByEventType(subscriber, eventType);
            }
            typesBySubscriber.remove(subscriber);
        } else {
            Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
    private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
     //获取事件类型的所有订阅者
        List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        //遍历订阅者集合,将解除的订阅者移除
        if (subscriptions != null) {
            int size = subscriptions.size();
            for (int i = 0; i < size; i++) {
                Subscription subscription = subscriptions.get(i);
                if (subscription.subscriber == subscriber) {
                    subscription.active = false;
                    subscriptions.remove(i);
                    i--;
                    size--;
                }
            }
        }
    }

代码很简单,最后总结一下取消订阅的流程。

总结一下:

  1. 首先获取订阅者的所有订阅事件
  2. 遍历订阅事件
  3. 根据订阅事件获取所有的订阅了该事件的订阅者集合
  4. 将该订阅者移除
  5. 将步骤1中的集合中的订阅者移除

以上便是EventBus所有的工作流程,我们来简单说明一下:

register

1、首先用register()方法注册一个订阅者

2、获取该订阅者的所有订阅的方法

3、根据该订阅者的所有订阅的事件类型,将订阅者存入到每个以 事件类型为key 以所有订阅者为values的map集合中

4、然后将订阅事件添加到以订阅者为key 以订阅者所有订阅事件为values的map集合中

4.1、如果是订阅了粘滞事件的订阅者,从粘滞事件缓存区获取之前发送过的粘滞事件,响应这些粘滞事件。

post

1、首先获取当前线程的事件队列

2、将要发送的事件添加到事件队列中

3、根据发送事件类型获取所有的订阅者

4、根据响应方法的执行模式,在相应线程通过反射执行订阅者的订阅方法

unregister

1、首先通过unregister方法拿到要取消的订阅者

2、得到该订阅者的所有订阅事件类型

3、遍历事件类型,根据每个事件类型获取到所有的订阅者集合,并从集合中删除该订阅者

4、将订阅者从步骤2的集合中移除


参考

1、 Android EventBus源码解析 带你深入理解EventBus

2、 EventBus 源码解析

3、http://www.cnblogs.com/all88/archive/2016/03/30/5338412.html

4、http://www.jianshu.com/p/f057c460c77e

时间: 2024-12-19 04:50:41

android 消息传递机制EventBus的深入探究的相关文章

Android消息传递机制

背景需求 在Android中,当遇到子线程需要刷新UI时,最常的做法就是handler,当然还有其他方便的方法如Android给我们提供的runOnUiThread(runnable)方法,但归根结底都是使用handler来刷新UI的. Android消息传递原理 简单的讲:handler发送(post或send)一条消息:MessageQueue(队,实际上是一个用单链表实现的队列)接受并存储该消息:looper无限循环的从MessageQueue中取出一条消息,例如msg,然后调用msg.t

Android消息传递之基于RxJava实现一个EventBus - RxBus(四)

前言: 上篇文章学习了Android事件总线管理开源框架EventBus,EventBus的出现大大降低了开发成本以及开发难度,今天我们就利用目前大红大紫的RxJava来实现一下类似EventBus事件总线管理,现在很多人都在说用这种方式来替代EventBus,今天我们从开发效率,开发难度等维度来分析一下到底能不能取代EventBus? 先回顾一下什么是EventBus?请看这篇文章Android消息传递之EventBus 3.0使用详解(三) 需求: 虽然软件工程师就害怕听见“需求”这两个字,

android线程消息传递机制——Looper,Handler,Message

android线程消息传递机制——Looper,Handler,Message 在引入这些概念之前,我们先了解一下引入这些机制的背景. 出于性能优化的考虑,Android的UI操作并不是线程安全的(如果你不懂什么是线程安全,可以阅读一下<一起探究多进程与多线程>里的数据安全与可重入),这意味着如果有多个线程同时操作某个UI组件,可能导致线程安全问题.为了解决这个问题,Android制定了一条简单的规则:只允许UI线程修改Activity里的UI组件.这个UI线程也通常被我们称为主线程. 在此引

整理:3种消息传递机制的比较:Handler、BroadcastReceiver、EventBus

摘要:本文主要写了Handler.BroadcastReceiver.EventBus这三种消息传递机制的使用方法,这里强烈推荐使用最后一种,理由如下:1.完全解耦,发送者和接受者几乎没关联,删除其中一个对另外一个没影响(这一点Handler就不行).2.传参数方便,同时支持一个发送者发送多条消息.一个接受者接受多条消息. 1.Handler: (1).发送: public Handler parentHandler;//此Handle的赋值在目的地 // 发送Handle通知 Message

android触碰消息传递机制

前阵子要的工作是给桌面(Launcher启动器,其实也是一个activity)添加一个触摸特效(一个View),而这个特效是每次触碰都会有,不管你在桌面上做什么操作都会显示特效!之前一直摸索着不知道如何入手,后来慢慢的实验之后才知道有个android触碰消息传递机制.自己摸索的确很慢,要是早点知道这个机制那将会事半功倍. 用户的每次触碰(onClick,onLongClick,onScroll,etc.)都是由一个ACTION_DOWN+n个ACTION_MOVE+1个ACTION_UP组成的,

Android异步消息传递机制源码分析&amp;&amp;相关知识常被问的面试题

1.Android异步消息传递机制有以下两个方式:(异步消息传递来解决线程通信问题) handler 和 AsyncTask 2.handler官方解释的用途: 1).定时任务:通过handler.postDelay(Runnable r, time)来在指定时间执行msg. 2).线程间通信:在执行较为耗时操作的时候,在子线程中执行耗时任务,然后handler(主线程的)把执行的结果通过sendmessage的方式发送给UI线程去执行用于更新UI. 3.handler源码分析 一.在Activ

Android笔记二十五.Android事件Handler消息传递机制

因为Android平台不同意Activity新启动的线程訪问该Activity里的界面控件.这样就会导致新启动的线程无法动态改变界面控件的属性值.但在实际Android应用开发中,尤其是涉及动画的游戏开发中,须要让新启动的线程周期性地改变界面控件的属性值,这就须要借助Handler的消息传递机制实现. 一.Handler类简单介绍 1.功能 Handler类主要有两个作用 (1)在新启动的线程中发送消息; (2)在主线程中获取消息.处理消息.即当须要界面发生变化的时候.在子线程中调用Handle

Android事件分发机制详解(1)----探究View的事件分发

探究View的事件分发 在Activity中,只有一个按钮,注册一个点击事件 [java] view plaincopy button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Log.d("TAG", "onClick execute"); } }); 如果在需要一个触摸事件 [java] view plaincopy button.setO

解析Android的 消息传递机制Handler

1. 什么是Handler: Handler 网络释义"操纵者,管理者的"意思,在Android里面用于管理多线程对UI的操作: 2. 为什么会出现Handler: 在Android的设计机制里面,只允许主线程(一个程序第一次启动时所移动的线程,因为此线程主要是完成对UI相关事件的处理,所以也称UI线程) 对UI进行修改等操作,这是一种规则的简化,之所以这样简化是因为Android的UI操作时线程不安全的,为了避免多个线程同时操作UI造成线程安全 问题,才出现了这个简化的规则. 由此以