【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 的优点是代码简洁,使用简单,并将事件发布和订阅充分解耦。
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); } }
- 【Android】EventBus实践
- Android——EventBus——实践
- Otto EventBus实践
- 第一个EventBus实践
- greenrobot EventBus 使用实践
- Android eventBus
- android EventBus
- android-eventbus
- android eventbus
- Android--EventBus
- Android -- EventBus
- android - EventBus
- android eventbus
- android EventBus
- Android EventBus
- Android EventBus
- Android EventBus
- android EventBus
- android 软键盘弹出 布局上移动
- 二叉搜索树的第k个结点
- JBPM 入门
- json对象 按字典排序
- Spark:使用Java实现所有的Transformation操作
- 【Android】EventBus实践
- HashSet以及重写equals()和hashCode()
- 要点提炼|开发艺术之 Activity
- python中多线程编程
- angularJS添加
- Python Numpy数组保存
- 技能测试1:面向对象编码实现用户注册
- C/C++学习笔记-开篇
- ehcache 缓存使用