【Android】EventBus实践

来源:互联网 发布:mac设置iphone铃声acc 编辑:程序博客网 时间:2024/06/05 09:00
github位置:
https://github.com/greenrobot/EventBus
了解:

EventBus is a publish/subscribe event bus for Android and Java.

EventBus 是一个 Android 事件发布/订阅框架,通过解耦发布者和订阅者简化 Android 事件传递,这里的事件可以理解为消息,本文中统一称为事件。事件传递既可用于 Android 四大组件间通讯,也可以用户异步线程和主线程间通讯等等。 传统的事件传递方式包括:Handler、BroadCastReceiver、Interface 回调,相比之下 EventBus 的优点是代码简洁,使用简单,并将事件发布和订阅充分解耦。

image

simplifies the communication between components

decouples event senders and receiversperforms well with Activities, Fragments, and background threadsavoids complex and error-prone dependencies and life cycle issues

makes your code simpler

is fast

is tiny (~50k jar)

is proven in practice by apps with 100,000,000+ installs has advanced features like delivery threads, subscriber priorities, etc.

Add EventBus to your project

引用方式:

Via Gradle:

compile 'org.greenrobot:eventbus:3.1.1'

Via Maven:

<dependency>    <groupId>org.greenrobot</groupId>    <artifactId>eventbus</artifactId>    <version>3.1.1</version></dependency>

Or download the latest JAR from Maven Central.

EventBus in 3 steps 使用步骤

Define events:

public static class MessageEvent { /* Additional fields if needed */ }

Prepare subscribers: Declare and annotate your subscribing method, optionally specify a thread mode:

@Subscribe(threadMode = ThreadMode.MAIN)  public void onMessageEvent(MessageEvent event) {/* Do something */};

Register and unregister your subscriber. For example on Android, activities and fragments should usually register according to their life cycle:

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

Post events:

EventBus.getDefault().post(new MessageEvent());
简单示例:

第1个activity:MainActivity,第2个activity:ConstraintActivity, 从第一个activity可以进入第二个activity,第二个activity点击上面一个按钮,关闭当前页面,同时给第一个activity传递一个值

建立一个自定义event

public class MainEvent {    private String mMsg;    public MainEvent(String msg) {        // TODO Auto-generated constructor stub        mMsg = msg;    }    public String getMsg(){        return mMsg;    }}

第二个页面ConstraintActivity:

public class ConstraintActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_constraint);        TextView tab1 = findViewById(R.id.tab1);        TextView tab2 = findViewById(R.id.tab2);        tab1.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                EventBus.getDefault().post(new MainEvent("MainEvent get msg tab1"));                finish();            }        });        tab2.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                EventBus.getDefault().post(new MainEvent("MainEvent get msg tab2"));                finish();            }        });    }}

第一个页面:

@Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //注册EventBus        EventBus.getDefault().register(this);        initLeftView();        initView();        addListener();    }    @Override    protected void onDestroy() {        super.onDestroy();        //反注册EventBus        EventBus.getDefault().unregister(this);    }    @Subscribe(threadMode = ThreadMode.MAIN)    public void onMessageEvent(MainEvent event) {        String msg = "第一个页面收到了消息:" + event.getMsg();        Toast.makeText(this, msg, Toast.LENGTH_LONG).show();    }

运行app后:第一个页面收到了第二个页面发来的消息。

源码查看
1.ThreadMode

看看@Subscribe(threadMode = ThreadMode.MAIN) 这句话:查看ThreadMode源码可以得知EventBus的几种使用场景:

package org.greenrobot.eventbus;/** * Each subscriber method has a thread mode, which determines in which thread the method is to be called by EventBus. * EventBus takes care of threading independently from the posting thread. *  * @see EventBus#register(Object) * @author Markus */public enum ThreadMode {    /**     * Subscriber will be called directly in the same thread, which is posting the event. This is the default. Event delivery     * implies the least overhead because it avoids thread switching completely. Thus this is the recommended mode for     * simple tasks that are known to complete in a very short time without requiring the main thread. Event handlers     * using this mode must return quickly to avoid blocking the posting thread, which may be the main thread.     */    POSTING,//订阅者将直接在同一个线程中发布,即发布该事件。这是最少的开销,因为它避免了完全的线程切换。 因此这是推荐的模式,简单的任务已知在很短的时间内完成而不需要主线程。 事件处理程序,使用这种模式必须快速返回以避免阻塞发布线程,发布线程有可能是主线程。    /**     * On Android, subscriber will be called in Android's main thread (UI thread). If the posting thread is     * the main thread, subscriber methods will be called directly, blocking the posting thread. Otherwise the event     * is queued for delivery (non-blocking). Subscribers using this mode must return quickly to avoid blocking the main thread.     * If not on Android, behaves the same as {@link #POSTING}.     */    MAIN,//在Android上,用户将在Android的主线程(UI线程)中被调用。如果发布线程是主线程,用户方法将被直接调用,阻塞发布线程。 否则事件正在排队交付(非阻塞)。使用这种模式的用户必须快速返回以避免阻塞主线程。如果不在Android上,则表现与{@link #POSTING}相同。    /**     * On Android, subscriber will be called in Android's main thread (UI thread). Different from {@link #MAIN},     * the event will always be queued for delivery. This ensures that the post call is non-blocking.     */    MAIN_ORDERED,//在Android上,用户将在Android的主线程(UI线程)中被调用。 与{@link #MAIN}不同,活动将始终排队等待交付。 这确保了邮政呼叫是非阻塞的。    /**     * On Android, subscriber will be called in a background thread. If posting thread is not the main thread, subscriber methods     * will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single     * background thread, that will deliver all its events sequentially. Subscribers using this mode should try to     * return quickly to avoid blocking the background thread. If not on Android, always uses a background thread.     */    BACKGROUND,//在Android上,用户将在后台线程中被调用。如果发布线程不是主线程,则为用户方法将在发布线程中直接调用。如果发布线程是主线程,EventBus使用一个后台线程,将按顺序传递所有事件。 订阅者使用这种模式应该尝试快速返回以避免阻塞后台线程。如果不是在Android上,总是使用后台线程。    /**     * Subscriber will be called in a separate thread. This is always independent from the posting thread and the     * main thread. Posting events never wait for subscriber methods using this mode. Subscriber methods should     * use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number     * of long running asynchronous subscriber methods at the same time to limit the number of concurrent threads. EventBus     * uses a thread pool to efficiently reuse threads from completed asynchronous subscriber notifications.     */    ASYNC// 订阅者将在一个单独的线程中被调用。这总是独立于发布线程和主线程。 发布事件永远不会等待使用此模式的订户方法。如果用户方法的执行可能需要一些时间,则它们应该使用这种模式。用于网络访问。避免触发大量长时间运行异步用户方法,同时限制并发线程的数量。EventBus使用线程池来有效地重用已完成的异步订户通知中的线程。}
2.EventBus.getDefault() 双重检查锁 单例模式
/** 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():

public EventBus() {        this(DEFAULT_BUILDER);}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.EventBus.getDefault().register(this);
public void register(Object subscriber) {        //首先获得订阅者的class对象。        Class<?> subscriberClass = subscriber.getClass();        //通过subscriberMethodFinder来找到订阅者订阅了那些事件        // ,返回一个SubscriberMethod对象的list,SubscriberMethod        // 里包含了这个方法的Method对象,以及将来响应订阅是在哪个线程的ThreadMode        //以及订阅的事件类型eventType,以及优先级priority,以及是否接收粘性sticky事件的boolean值.        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);        synchronized (this) {            for (SubscriberMethod subscriberMethod : subscriberMethods) {                            subscribe(subscriber, subscriberMethod);            }        }    }        // 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);        }        subscribedEvents.add(eventType);        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);            }        }    }

订阅的类存储在一个Map里面:

private final Map<Object, List<Class<?>>> typesBySubscriber;
4.EventBus.getDefault().unregister(this);
/** 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 {            logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());        }    }
5.EventBus.getDefault().post(Object event)
public void post(Object event) {        PostingThreadState postingState = currentPostingThreadState.get();        List<Object> eventQueue = postingState.eventQueue;        eventQueue.add(event);        if (!postingState.isPosting) {            postingState.isMainThread = isMainThread();            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;            }        }    }

查看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) {                logger.log(Level.FINE, "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 MAIN_ORDERED:                if (mainThreadPoster != null) {                    mainThreadPoster.enqueue(subscription, event);                } else {                    // temporary: technically not correct as poster not decoupled from subscriber                    invokeSubscriber(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);        }    }
原创粉丝点击