Android事件总线EventBus的用法详解

Android中模块更新的机制有很多种,EventBus又是其中使用起来较为方便简单的一种,本文就针对EventBus在平常工作中的用法,做一个个人总结。主要分为以下几点来展示:

  1. 介绍
  2. 源码分析
  3. 用法示例

下图也就是本文要讲的主要内容:
这里写图片描述

1、介绍:
EventBus是针一款对Android的发布/订阅事件总线。它可以让我们很轻松的实现在Android各个组件之间传递消息,并且代码的可读性更好,耦合度更低。我们可以在要进行实时界面更新的交互上使用该模块以替代一些消息机制的处理。EventBus的·优点是用法简单,不必写繁琐的代码,而且耦合度也低。缺点其实个人发现无明显的缺点,可能就是得针对每个事件定义也一个事件类,感觉会造成重复造轮子的感觉。此外,关于事件总线还有的一个框架是Otto,这边就不分析这个框架了。

2、源码分析:
按照惯例,我们在代码里面查看EventBus.java的源码,这里就不贴出整个类的源码了。我们首先看下,该类的构造方法:

/**
     * Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a
     * central bus, consider {@link #getDefault()}.
     */
    public EventBus() {
        this(DEFAULT_BUILDER);
    }

    EventBus(EventBusBuilder builder) {
        subscriptionsByEventType = new HashMap<Class<?>, CopyOnWriteArrayList<Subscription>>();
        typesBySubscriber = new HashMap<Object, List<Class<?>>>();
        stickyEvents = new ConcurrentHashMap<Class<?>, Object>();
        mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
        backgroundPoster = new BackgroundPoster(this);
        asyncPoster = new AsyncPoster(this);
        subscriberMethodFinder = new SubscriberMethodFinder(builder.skipMethodVerificationForClasses);
        logSubscriberExceptions = builder.logSubscriberExceptions;
        logNoSubscriberMessages = builder.logNoSubscriberMessages;
        sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
        sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
        throwSubscriberException = builder.throwSubscriberException;
        eventInheritance = builder.eventInheritance;
        executorService = builder.executorService;
    }

主要是一些要用到类的对象的初始化,用的是Builder模式。
注册的方法:

/**
     * 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();
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

通过SubscriberMethodFinder该类的findSubscriberMethods方法,查询到SubscriberMethod列表,SubscriberMethod类的构造方法:

public SubscriberMethod(Method method, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) {
        this.method = method;
        this.threadMode = threadMode;
        this.eventType = eventType;
        this.priority = priority;
        this.sticky = sticky;
    }

可知,五个参数为方法、事件类型、线程模式、优先级、sticky,SubscriberMethod的其他方法:

@Override
    public boolean equals(Object other) {
        if (other == this) {
            return true;
        } else if (other instanceof SubscriberMethod) {
            checkMethodString();
            SubscriberMethod otherSubscriberMethod = (SubscriberMethod)other;
            otherSubscriberMethod.checkMethodString();
            // Don't use method.equals because of http://code.google.com/p/android/issues/detail?id=7811#c6
            return methodString.equals(otherSubscriberMethod.methodString);
        } else {
            return false;
        }
    }

    private synchronized void checkMethodString() {
        if (methodString == null) {
            // Method.toString has more overhead, just take relevant parts of the method
            StringBuilder builder = new StringBuilder(64);
            builder.append(method.getDeclaringClass().getName());
            builder.append('#').append(method.getName());
            builder.append('(').append(eventType.getName());
            methodString = builder.toString();
        }
    }

现在清楚了,其实就是查询SubscriberMethodFinder类的作用是在Subscriber中找到所有以methodName(即默认的onEvent)开头的方法,具体我们可以看下findSubscriberMethods方法的代码。
EventBus中 findSubscriberMethods方法 2.4源码:

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        String key = subscriberClass.getName();
        List<SubscriberMethod> subscriberMethods;
        synchronized (methodCache) {
            subscriberMethods = methodCache.get(key);
        }
        if (subscriberMethods != null) {
            return subscriberMethods;
        }
        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) {
                String methodName = method.getName();
                if (methodName.startsWith(ON_EVENT_METHOD_NAME)) {
                    int modifiers = method.getModifiers();
                    if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                        Class<?>[] parameterTypes = method.getParameterTypes();
                        if (parameterTypes.length == 1) {
                            String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());
                            ThreadMode threadMode;
                            if (modifierString.length() == 0) {
                                threadMode = ThreadMode.PostThread;
                            } else if (modifierString.equals("MainThread")) {
                                threadMode = ThreadMode.MainThread;
                            } else if (modifierString.equals("BackgroundThread")) {
                                threadMode = ThreadMode.BackgroundThread;
                            } else if (modifierString.equals("Async")) {
                                threadMode = ThreadMode.Async;
                            } else {
                                if (skipMethodVerificationForClasses.containsKey(clazz)) {
                                    continue;
                                } else {
                                    throw new EventBusException("Illegal onEvent method, check for typos: " + method);
                                }
                            }
                            Class<?> eventType = parameterTypes[0];
                            methodKeyBuilder.setLength(0);
                            methodKeyBuilder.append(methodName);
                            methodKeyBuilder.append('>').append(eventType.getName());
                            String methodKey = methodKeyBuilder.toString();
                            if (eventTypesFound.add(methodKey)) {
                                // Only add if not already found in a sub class
                                subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));
                            }
                        }
                    } else if (!skipMethodVerificationForClasses.containsKey(clazz)) {
                        Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + clazz + "."
                                + methodName);
                    }
                }
            }
            clazz = clazz.getSuperclass();
        }
        if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called "
                    + ON_EVENT_METHOD_NAME);
        } else {
            synchronized (methodCache) {
                methodCache.put(key, subscriberMethods);
            }
            return subscriberMethods;
        }
    }

现在应该豁然开朗,就是2.4版本,必须使用三种方法接收post过来的消息:MainThread、BackgroundThread、Async。而在3.0中的源码则是这样的:

 List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }

        if (ignoreGeneratedIndex) {
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            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;
        }
    }

subscriberMethods = findUsingReflection(subscriberClass);说的是利用反射来查询方法。
所以我们3.0通过注解,可以使用自定义的方法,不在受限,看完了注册的代码,
接下来就看下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,然后就是这段代码:

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));
            }
        }
    }

    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);
        }
    }

这里就用到ThreadMode了:

如果是PostThread,直接执行
如果是MainThread,判断当前线程,如果本来就是UI线程就直接执行,否则加入mainThreadPoster队列
如果是后台线程,如果当前是UI线程,加入backgroundPoster队列,否则直接执行
如果是Async,加入asyncPoster队列。关于post,就暂时写到这里啊,主要是理清内部的处理机制。

另外还有优先级和Stick的使用。这里就不阐述了。EventBus关键的部分源码我就写到这里,以下写下我们如何在代码中简单使用该模块。

3、使用示例:
一、首先是引入该模块:
我用的是gradle构建(demo源码和资源在最后会提供):

//    compile files('libs/eventbus-2.4.0.jar')
    compile 'org.greenrobot:eventbus:3.0.0'//3.0

二、create方法中注册:

EventBusManager.getInstance().getGlobaEventBus().register(this);

EventBusManager.java自己封装的类:


//import de.greenrobot.event.EventBus;//2.4

import org.greenrobot.eventbus.EventBus;//3.0

/**
 * @创建者 :viviant
 * @时间 :2016/06/28  13:47
 * @描述 : 事件总线管理
 */
public class EventBusManager {

    private static EventBusManager busManager;
    private EventBus globalEventBus;

    private EventBusManager() {
    }

    public EventBus getGlobaEventBus() {
        if (globalEventBus == null) {
            globalEventBus = new EventBus();
        }
        return globalEventBus;
    }

    public static EventBusManager getInstance() {
        if (busManager == null) {
            synchronized (EventBusManager.class) {
                if (busManager == null) {
                    busManager = new EventBusManager();
                }
            }
        }
        return busManager;
    }
}

Event实体类编写:

/**
 * 作者:viviant on 2016/7/4 10:35
 * 描述:
 */
public class TestEvent {
    private String eventName;

    public void setEventName(String eventName) {
        this.eventName = eventName;
    }

    public String getEventName() {
        return eventName;
    }
}

接收页面方法编写(demo源码中有说明版本区别):

/**
     * 不同模块之间数据的同步
     *2.4的用法必须使用以下的几个方法,不需要注解
     * @param event
     */
     @Subscribe
    public void onEventReceive(TestEvent event) {
        Log.d("weiwei", "onEventMainThread收到了消息:" );

        if (event.getEventName().equals("tv1")) {
            tv1.setText("change by bt1");

        } else if (event.getEventName().equals("tv2")) {
            tv2.setText("tv2");

        } else if (event.getEventName().equals("tv3")) {

        } else if (event.getEventName().equals("tv4")) {

        }
    }

操作activity中的相关代码:

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;

import constraintlayout.test.test.viviant.eventbustest.event.TestEvent;
import constraintlayout.test.test.viviant.eventbustest.manager.EventBusManager;

/**
 * 作者:viviant on 2016/7/4 10:37
 * 描述:
 */
public class SecActivity extends AppCompatActivity implements View.OnClickListener{
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_sec);

        Button bt1 = (Button) findViewById(R.id.bt1);
        Button bt2 = (Button) findViewById(R.id.bt2);
        Button bt3 = (Button) findViewById(R.id.bt3);
        Button bt4 = (Button) findViewById(R.id.bt4);

        bt1.setOnClickListener(this);
        bt2.setOnClickListener(this);
        bt3.setOnClickListener(this);
        bt4.setOnClickListener(this);


    }

    @Override
    public void onClick(View view) {
        TestEvent te = new TestEvent();
        switch (view.getId()) {

            case R.id.bt1:
                te.setEventName("tv1");
                EventBusManager.getInstance().getGlobaEventBus().post(te);
                break;

            case R.id.bt2:
                te.setEventName("tv2");
                EventBusManager.getInstance().getGlobaEventBus().post(te);
                break;

            case R.id.bt3:
                break;

            case R.id.bt4:
                break;
            default:
                break;
        }
    }
}

云行之后我们先点击跳转activity,再点击相应but,然后返回去,便可以看到发生相应的变化。
MainActivity界面:
这里写图片描述

SecActivity界面:

这里写图片描述

点击第一个按钮后回到MainActivity的界面:
这里写图片描述

源码地址:
https://github.com/viviant1224/EventBusTest

2.4jar包下载地址:
http://download.csdn.net/detail/u013606974/9566754

demo中查看gradle中可切换EventBus版本。改完之后也要改相应的引包类。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
EventBus是一个开源的发布/订阅事件总线库,它简化了不同组件之间的通信,避免了紧密耦合的代码。它使用了观察者模式,让不同的组件能够在没有直接关联的情况下相互通信。下面是EventBusAndroid中的使用详解。 1.导入EventBus库 在build.gradle文件中添加以下代码: ``` dependencies { implementation 'org.greenrobot:eventbus:3.2.0' } ``` 2.定义事件类 定义一个事件类,用于传递数据。 ```java public class MessageEvent { public final String message; public MessageEvent(String message) { this.message = message; } } ``` 3.注册EventBus 在需要接收事件的组件中注册EventBus。 ```java @Override public void onStart() { super.onStart(); EventBus.getDefault().register(this); } ``` 4.注销EventBus 在不需要接收事件的组件中注销EventBus。 ```java @Override public void onStop() { super.onStop(); EventBus.getDefault().unregister(this); } ``` 5.发布事件 在发送事件的组件中发布事件。 ```java EventBus.getDefault().post(new MessageEvent("Hello EventBus!")); ``` 6.订阅事件 在接收事件的组件中订阅事件。 ```java @Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEvent(MessageEvent event) { String message = event.message; // 处理事件 } ``` 7.指定线程模式 EventBus支持在不同的线程中处理事件,可以通过@Subscribe注解的threadMode参数指定线程模式。 ```java @Subscribe(threadMode = ThreadMode.MAIN) // 主线程 public void onMessageEvent(MessageEvent event) { String message = event.message; // 处理事件 } @Subscribe(threadMode = ThreadMode.BACKGROUND) // 后台线程 public void onMessageEvent(MessageEvent event) { String message = event.message; // 处理事件 } @Subscribe(threadMode = ThreadMode.ASYNC) // 异步线程 public void onMessageEvent(MessageEvent event) { String message = event.message; // 处理事件 } ``` EventBus的使用非常简单,但是需要注意以下几点: - 订阅方法必须使用@Subscribe注解,并且方法必须是public、void类型。 - 发布事件和接收事件的参数类型必须一致。 - 在注册EventBus时,不要忘记在onStop()方法中注销EventBus,否则可能会引起内存泄漏。 - EventBus默认在主线程中处理事件,如果事件处理需要耗时操作,可以使用不同的线程模式。 - EventBus不支持跨进程通信,只能在同一进程中的组件之间进行通信。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值