您好,登錄后才能下訂單哦!
今天小編給大家分享一下EventBus框架的作用是什么的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
EventBus的作用
Android中存在各種通信場景,如Activity
之間的跳轉,Activity
與Fragment
以及其他組件之間的交互,以及在某個耗時操作(如請求網絡)之后的callback回調等,互相之之間往往需要持有對方的引用,每個場景的寫法也有差異,導致耦合性較高且不便維護。
以Activity
和Fragment
的通信為例,官方做法是實現一個接口,然后持有對方的引用,再強行轉成接口類型,導致耦合度偏高。
再以Activity
的返回為例,一方需要設置setResult
,而另一方需要在onActivityResult
做對應處理,如果有多個返回路徑,代碼就會十分臃腫。而SimpleEventBus
(本文最終實現的簡化版事件總線)的寫法如下:
<pre class="juejin-editor-highlight" style="box-sizing: border-box; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.8em; position: relative; padding: 0.5em 1em; background: rgb(248, 248, 248); overflow: auto; border-radius: 2px; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class MainActivity extends AppCompatActivity { TextView mTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = findViewById(R.id.tv_demo); mTextView.setText("MainActivity"); mTextView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(MainActivity.this, SecondActivity.class); startActivity(intent); } }); EventBus.getDefault().register(this); } @Subscribe(threadMode = ThreadMode.MAIN) public void onReturn(Message message) { mTextView.setText(message.mContent); } @Override protected void onDestroy() { super.onDestroy(); EventBus.getDefault().unregister(this); } }
來源Activity
:
<pre class="juejin-editor-highlight" style="box-sizing: border-box; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 0.8em; position: relative; padding: 0.5em 1em; background: rgb(248, 248, 248); overflow: auto; border-radius: 2px; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class SecondActivity extends AppCompatActivity { TextView mTextView; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = findViewById(R.id.tv_demo); mTextView.setText("SecondActivity,點擊返回"); mTextView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Message message = new Message(); message.mContent = "從SecondActivity返回"; EventBus.getDefault().post(message); finish(); } }); } }
效果如下:
image
似乎只是換了一種寫法,但在場景愈加復雜后,EventBus
能夠體現出更好的解耦能力
背景知識
主要涉及三方面的知識:
觀察者模式(or 發布-訂閱模式)
Android消息機制
Java并發編程
實現過程
EventBus`的使用分三個步驟:注冊監聽、發送事件和取消監聽,相應本文也將分這三步來實現。
注冊監聽
定義一個注解:
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface Subscribe { ThreadMode threadMode() default ThreadMode.POST; }
greenrobot/EventBus還支持優先級和粘性事件,這里只支持最基本的能力:區分線程,因為如更新UI的操作必須放在主線程。
ThreadMode`如下:
public enum ThreadMode { MAIN, // 主線程 POST, // 發送消息的線程 ASYNC // 新開一個線程發送}
在對象初始化的時候,使用register
方法注冊,該方法會解析被注冊對象的所有方法,并解析聲明了注解的方法(即觀察者),核心代碼如下:
public class EventBus { ... public void register(Object subscriber) { if (subscriber == null) { return; } synchronized (this) { subscribe(subscriber); } } ... private void subscribe(Object subscriber) { if (subscriber == null) { return; } // TODO 巨踏馬難看的縮進 Class<?> clazz = subscriber.getClass(); while (clazz != null && !isSystemClass(clazz.getName())) { final Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { Subscribe annotation = method.getAnnotation(Subscribe.class); if (annotation != null) { Class<?>[] paramClassArray = method.getParameterTypes(); if (paramClassArray != null && paramClassArray.length == 1) { Class<?> paramType = convertType(paramClassArray[0]); EventType eventType = new EventType(paramType); SubscriberMethod subscriberMethod = new SubscriberMethod(method, annotation.threadMode(), paramType); realSubscribe(subscriber, subscriberMethod, eventType); } } } clazz = clazz.getSuperclass(); } } ... private void realSubscribe(Object subscriber, SubscriberMethod method, EventType eventType) { CopyOnWriteArrayList<Subscription> subscriptions = mSubscriptionsByEventtype.get(subscriber); if (subscriptions == null) { subscriptions = new CopyOnWriteArrayList<>(); } Subscription subscription = new Subscription(subscriber, method); if (subscriptions.contains(subscription)) { return; } subscriptions.add(subscription); mSubscriptionsByEventtype.put(eventType, subscriptions); } ... }
執行過這些邏輯后,該對象所有的觀察者方法都會被存在一個Map中,其Key是EventType
,即觀察事件的類型,Value是訂閱了該類型事件的所有方法(即觀察者)的一個列表,每個方法和對象一起封裝成了一個Subscription
類:
public class Subscription { public final Reference<Object> subscriber; public final SubscriberMethod subscriberMethod; public Subscription(Object subscriber, SubscriberMethod subscriberMethod) { this.subscriber = new WeakReference<>(subscriber);// EventBus3 沒用弱引用? this.subscriberMethod = subscriberMethod; } @Override public int hashCode() { return subscriber.hashCode() + subscriberMethod.methodString.hashCode(); } @Override public boolean equals(Object obj) { if (obj instanceof Subscription) { Subscription other = (Subscription) obj; return subscriber == other.subscribe && subscriberMethod.equals(other.subscriberMethod); } else { return false; } } }
如此,便是注冊監聽方法的核心邏輯了。
消息發送
消息的發送代碼很簡單:
public class EventBus { ... private EventDispatcher mEventDispatcher = new EventDispatcher(); private ThreadLocal<Queue<EventType>> mThreadLocalEvents = new ThreadLocal<Queue<EventType>>() { @Override protected Queue<EventType> initialValue() { return new ConcurrentLinkedQueue<>(); } }; ... public void post(Object message) { if (message == null) { return; } mThreadLocalEvents.get().offer(new EventType(message.getClass())); mEventDispatcher.dispatchEvents(message); } ... }
比較復雜一點的是需要根據注解聲明的線程模式在對應的線程進行發布:
public class EventBus { ... private class EventDispatcher { private IEventHandler mMainEventHandler = new MainEventHandler(); private IEventHandler mPostEventHandler = new DefaultEventHandler(); private IEventHandler mAsyncEventHandler = new AsyncEventHandler(); void dispatchEvents(Object message) { Queue<EventType> eventQueue = mThreadLocalEvents.get(); while (eventQueue.size() > 0) { handleEvent(eventQueue.poll(), message); } } private void handleEvent(EventType eventType, Object message) { List<Subscription> subscriptions = mSubscriptionsByEventtype.get(eventType); if (subscriptions == null) { return; } for (Subscription subscription : subscriptions) { IEventHandler eventHandler = getEventHandler(subscription.subscriberMethod.threadMode); eventHandler.handleEvent(subscription, message); } } private IEventHandler getEventHandler(ThreadMode mode) { if (mode == ThreadMode.ASYNC) { return mAsyncEventHandler; } if (mode == ThreadMode.POST) { return mPostEventHandler; } return mMainEventHandler; } }// end of the class ... }
三種線程模式分別如下,DefaultEventHandler(在發布線程執行觀察者放方法):
public class DefaultEventHandler implements IEventHandler { @Override public void handleEvent(Subscription subscription, Object message) { if (subscription == null || subscription.subscriber.get() == null) { return; } try { subscription.subscriberMethod.method.invoke(subscription.subscriber.get(), message); } catch (IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } } }
MainEventHandler(在主線程執行):
public class MainEventHandler implements IEventHandler { private Handler mMainHandler = new Handler(Looper.getMainLooper()); DefaultEventHandler hanlder = new DefaultEventHandler(); @Override public void handleEvent(final Subscription subscription, final Object message) { mMainHandler.post(new Runnable() { @Override public void run() { hanlder.handleEvent(subscription, message); } }); } }
AsyncEventHandler(新開一個線程執行):
public class AsyncEventHandler implements IEventHandler { private DispatcherThread mDispatcherThread; private IEventHandler mEventHandler = new DefaultEventHandler(); public AsyncEventHandler() { mDispatcherThread = new DispatcherThread(AsyncEventHandler.class.getSimpleName()); mDispatcherThread.start(); } @Override public void handleEvent(final Subscription subscription, final Object message) { mDispatcherThread.post(new Runnable() { @Override public void run() { mEventHandler.handleEvent(subscription, message); } }); } private class DispatcherThread extends HandlerThread { // 關聯了AsyncExecutor消息隊列的Handle Handler mAsyncHandler; DispatcherThread(String name) { super(name); } public void post(Runnable runnable) { if (mAsyncHandler == null) { throw new NullPointerException("mAsyncHandler == null, please call start() first."); } mAsyncHandler.post(runnable); } @Override public synchronized void start() { super.start(); mAsyncHandler = new Handler(this.getLooper()); } } }
以上便是發布消息的代碼。
注銷監聽
最后一個對象被銷毀還要注銷監聽,否則容易導致內存泄露,目前SimpleEventBus用的是WeakReference,能夠通過GC自動回收,但不知道greenrobot/EventBus為什么沒這樣實現,待研究。注銷監聽其實就是遍歷Map,拿掉該對象的訂閱即可:
public class EventBus { ... public void unregister(Object subscriber) { if (subscriber == null) { return; } Iterator<CopyOnWriteArrayList<Subscription>> iterator = mSubscriptionsByEventtype.values().iterator(); while (iterator.hasNext()) { CopyOnWriteArrayList<Subscription> subscriptions = iterator.next(); if (subscriptions != null) { List<Subscription> foundSubscriptions = new LinkedList<>(); for (Subscription subscription : subscriptions) { Object cacheObject = subscription.subscriber.get(); if (cacheObject == null || cacheObject.equals(subscriber)) { foundSubscriptions.add(subscription); } } subscriptions.removeAll(foundSubscriptions); } // 如果針對某個Event的訂閱者數量為空了,那么需要從map中清除 if (subscriptions == null || subscriptions.size() == 0) { iterator.remove(); } } } ... }
局限性
由于時間關系,目前只研究了EventBus的核心部分,還有幾個值得深入研究的點,在此記錄一下,也歡迎路過的大牛指點一二。
性能問題
實際使用時,注解和反射會導致性能問題,但EventBus3已經通過Subscriber Index基本解決了這一問題,實現也非常有意思,是通過注解處理器(Annotation Processor)把耗時的邏輯從運行期提前到了編譯期,通過編譯期生成的索引來給運行期提速,這也是這個名字的由來
可用性問題
如果訂閱者很多會不會影響體驗,畢竟原始的方法是點對點的消息傳遞,不會有這種問題,如果部分訂閱者尚未初始化怎么辦。等等。目前EventBus3提供了優先級和粘性事件的屬性來進一步滿足開發需求。但是否徹底解決問題了還有待驗證。
以上就是“EventBus框架的作用是什么”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。