【EventBus】事件通信框架 ( 取消注册 | 获取事件参数类型 | 根据事件类型获取订阅者 | 移除相关订阅者 )
文章目錄
- 一、取消注冊(cè)訂閱者
- 二、完整代碼示例
一、取消注冊(cè)訂閱者
取消注冊(cè)操作 : 從 Map<Object, List<Class<?>>> typesBySubscriber 集合中獲取 訂閱者對(duì)象 中的 訂閱方法 參數(shù)集合 ; 然后再到 Map<Class<?>, CopyOnWriteArrayList<MySubscription>> subscriptionsByEventType 集合中獲取 訂閱方法參數(shù)類型 對(duì)應(yīng)的 CopyOnWriteArrayList<MySubscription>> 集合 , MySubscription 中封裝了 訂閱者對(duì)象 + 訂閱方法 , 刪除相應(yīng)的訂閱者即可 ;
1. 獲取參數(shù)類型集合 : 從 Map<Object, List<Class<?>>> typesBySubscriber 集合中獲取 訂閱者對(duì)象 中的 訂閱方法 參數(shù)集合 ;
// 首先獲取 訂閱者 對(duì)象中的訂閱方法的參數(shù)集合List<Class<?>> types = typesBySubscriber.get(subscriber);2. 獲取參數(shù)類型對(duì)應(yīng)的訂閱者集合 : 到 Map<Class<?>, CopyOnWriteArrayList<MySubscription>> subscriptionsByEventType 集合中獲取 訂閱方法參數(shù)類型 對(duì)應(yīng)的 CopyOnWriteArrayList<MySubscription>> 集合 ;
// 獲取 接收 type 事件類型的 訂閱者集合// MySubscription 中封裝了訂閱者對(duì)象 + 訂閱方法CopyOnWriteArrayList<MySubscription> subscriptions =subscriptionsByEventType.get(type);3. 移除符合條件的訂閱者 : 遍歷上述訂閱者集合 , 判定 CopyOnWriteArrayList<MySubscription> 集合中的 MySubscription 元素 , 如果如果 封裝類對(duì)象 中的 訂閱者對(duì)象 與 本次取消注冊(cè)的訂閱者對(duì)象相同 , 則從集合中移除該訂閱者 ;
// 判定 CopyOnWriteArrayList<MySubscription> 集合中的 MySubscription 元素// 如果如果 封裝類對(duì)象 中的 訂閱者對(duì)象 與 本次取消注冊(cè)的訂閱者對(duì)象相同 , 則從集合中移除該訂閱者// 記錄集合大小int subscriptionsSize = subscriptions.size();for (int i = 0; i < subscriptionsSize; i++) {// 獲取 訂閱者對(duì)象 + 訂閱方法 封裝類 對(duì)象MySubscription subscription = subscriptions.get(i);// 如果 封裝類對(duì)象 中的 訂閱者對(duì)象 與 本次取消注冊(cè)的訂閱者對(duì)象相同// 將其從該集合中刪除if (subscription.getSubscriber() == subscriber) {// 刪除 i 索引元素subscriptions.remove(i);// 應(yīng)用新的集合大小 , 集合少了一個(gè)元素subscriptionsSize--;// 第 i 個(gè)元素被刪除了 , 之后會(huì)自增遍歷下一個(gè)元素// 下一次遍歷的還是第 i 個(gè)元素// 由于后面循環(huán)操作需要自增 , 想要之后仍然遍歷第 i 個(gè)元素 ,// 這里對(duì) i 進(jìn)行自減操作i--;}}// 刪除了訂閱者 , 就完成了取消注冊(cè)操作部分代碼示例 :
/*** 取消注冊(cè)* 從 Map<Object, List<Class<?>>> typesBySubscriber 集合中獲取* 訂閱者對(duì)象 中的 訂閱方法 參數(shù)集合** 然后再到* Map<Class<?>, CopyOnWriteArrayList<MySubscription>> subscriptionsByEventType* 集合中獲取 訂閱方法參數(shù)類型 對(duì)應(yīng)的 CopyOnWriteArrayList<MySubscription>> 集合* MySubscription 中封裝了 訂閱者對(duì)象 + 訂閱方法* @param subscriber*/public void unregister(Object subscriber) {// 首先獲取 訂閱者 對(duì)象中的訂閱方法的參數(shù)集合List<Class<?>> types = typesBySubscriber.get(subscriber);// 遍歷參數(shù)類型for (Class<?> type: types) {// 獲取 接收 type 事件類型的 訂閱者集合// MySubscription 中封裝了訂閱者對(duì)象 + 訂閱方法CopyOnWriteArrayList<MySubscription> subscriptions =subscriptionsByEventType.get(type);// 判定 CopyOnWriteArrayList<MySubscription> 集合中的 MySubscription 元素// 如果如果 封裝類對(duì)象 中的 訂閱者對(duì)象 與 本次取消注冊(cè)的訂閱者對(duì)象相同 , 則從集合中移除該訂閱者// 記錄集合大小int subscriptionsSize = subscriptions.size();for (int i = 0; i < subscriptionsSize; i++) {// 獲取 訂閱者對(duì)象 + 訂閱方法 封裝類 對(duì)象MySubscription subscription = subscriptions.get(i);// 如果 封裝類對(duì)象 中的 訂閱者對(duì)象 與 本次取消注冊(cè)的訂閱者對(duì)象相同// 將其從該集合中刪除if (subscription.getSubscriber() == subscriber) {// 刪除 i 索引元素subscriptions.remove(i);// 應(yīng)用新的集合大小 , 集合少了一個(gè)元素subscriptionsSize--;// 第 i 個(gè)元素被刪除了 , 之后會(huì)自增遍歷下一個(gè)元素// 下一次遍歷的還是第 i 個(gè)元素// 由于后面循環(huán)操作需要自增 , 想要之后仍然遍歷第 i 個(gè)元素 ,// 這里對(duì) i 進(jìn)行自減操作i--;}}// 刪除了訂閱者 , 就完成了取消注冊(cè)操作}}二、完整代碼示例
package com.eventbus_demo.myeventbus;import android.os.Handler; import android.os.Looper;import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class MyEventBus {/*** 方法緩存* Key - 訂閱類類型* Value - 訂閱方法 MySubscriberMethod 的集合* 取名與 EventBus 一致*/private static final Map<Class<?>, List<MySubscriberMethod>> METHOD_CACHE = new HashMap<>();/*** 解除注冊(cè)時(shí)使用* Key - 訂閱者對(duì)象* Value - 訂閱者對(duì)象中所有的訂閱方法的事件參數(shù)類型集合** 根據(jù)該訂閱者對(duì)象 , 查找所有訂閱方法的事件參數(shù)類型 , 然后再到 METHOD_CACHE 中 ,* 根據(jù)事件參數(shù)類型 , 查找對(duì)應(yīng)的 MySubscriberMethod 集合* MySubscriberMethod 中封裝 訂閱者對(duì)象 + 訂閱方法**/private final Map<Object, List<Class<?>>> typesBySubscriber;/*** Key - 訂閱者方法事件參數(shù)類型* Value - 封裝 訂閱者對(duì)象 與 訂閱方法 的 MySubscription 集合* 在構(gòu)造函數(shù)中初始化* CopyOnWriteArrayList 在寫入數(shù)據(jù)時(shí)會(huì)拷貝一個(gè)副本 ,* 寫完之后 , 將引用指向新的副本 ,* 該集合的線程安全級(jí)別很高*/private final Map<Class<?>, CopyOnWriteArrayList<MySubscription>> subscriptionsByEventType;/*** 線程池*/private final ExecutorService executorService;/*** 全局單例*/private static MyEventBus instance;private MyEventBus() {subscriptionsByEventType = new HashMap<>();typesBySubscriber = new HashMap<>();executorService = Executors.newCachedThreadPool();}public static MyEventBus getInstance() {if (instance == null) {instance = new MyEventBus();}return instance;}/*** 注冊(cè)訂閱者* @param subscriber*/public void register(Object subscriber) {// 獲取訂閱者所屬類Class<?> clazz = subscriber.getClass();// 查找訂閱方法List<MySubscriberMethod> subscriberMethods = findSubscriberMethods(clazz);// 遍歷所有訂閱方法 , 進(jìn)行訂閱// 首先確保查找到的訂閱方法不為空 , 并且個(gè)數(shù)大于等于 1 個(gè)if (subscriberMethods != null && !subscriberMethods.isEmpty()) {for (MySubscriberMethod method : subscriberMethods) {// 正式進(jìn)行訂閱subscribe(subscriber, method);}}}/*** 方法訂閱* 將 訂閱方法參數(shù)類型 和 訂閱類 + 訂閱方法 封裝類 , 保存到* Map<Class<?>, CopyOnWriteArrayList<MySubscription>> subscriptionsByEventType 集合中* Key - 訂閱者方法事件參數(shù)類型* Value - 封裝 訂閱者對(duì)象 與 訂閱方法 的 MySubscription 集合** 取消注冊(cè)數(shù)據(jù)準(zhǔn)備* 取消注冊(cè)數(shù)據(jù)存放在 Map<Object, List<Class<?>>> typesBySubscriber 集合中* Key - 訂閱者對(duì)象* Value - 訂閱者方法參數(shù)集合** @param subscriber 訂閱者對(duì)象* @param subscriberMethod 訂閱方法*/private void subscribe(Object subscriber, MySubscriberMethod subscriberMethod) {// 獲取訂閱方法接收的參數(shù)類型Class<?> eventType = subscriberMethod.getEventType();// 獲取 eventType 參數(shù)類型對(duì)應(yīng)的 訂閱者封裝類 ( 封裝 訂閱者對(duì)象 + 訂閱方法 ) 集合CopyOnWriteArrayList<MySubscription> subscriptions =subscriptionsByEventType.get(eventType);// 如果獲取的集合為空 , 說明 eventType 參數(shù)對(duì)應(yīng)的訂閱方法一個(gè)也沒有注冊(cè)過// 這里先創(chuàng)建一個(gè)集合 , 放到 subscriptionsByEventType 鍵值對(duì)中if (subscriptions == null) {// 創(chuàng)建集合subscriptions = new CopyOnWriteArrayList<>();// 將集合設(shè)置到 subscriptionsByEventType 鍵值對(duì)集合中subscriptionsByEventType.put(eventType, subscriptions);}// 封裝 訂閱者對(duì)象 + 訂閱方法 對(duì)象MySubscription subscription = new MySubscription(subscriber, subscriberMethod);// 將創(chuàng)建的 訂閱者對(duì)象 + 訂閱方法 對(duì)象 添加到 CopyOnWriteArrayList 集合中subscriptions.add(subscription);// 為取消注冊(cè)準(zhǔn)備數(shù)據(jù)// 設(shè)置 Map<Object, List<Class<?>>> typesBySubscriberList<Class<?>> eventTypes = typesBySubscriber.get(subscriber);if (eventTypes == null) {// 創(chuàng)建新的集合, 用于存放訂閱方法的參數(shù)類型eventTypes = new ArrayList<>();// 將新的集合設(shè)置到 Map<Object, List<Class<?>>> typesBySubscriber 集合中typesBySubscriber.put(subscriber, eventTypes);}// 將新的 訂閱方法類型 放入到集合中eventTypes.add(eventType);}/*** 根據(jù)訂閱方法的事件參數(shù)查找訂閱方法* @param subscriberClass 訂閱者對(duì)象的類型* @return*/private List<MySubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {// 獲取 Class<?> clazz 參數(shù)類型對(duì)應(yīng)的 訂閱者封裝類List<MySubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);// 此處后期重構(gòu), 減少縮進(jìn)if (subscriberMethods == null) {// 說明是首次獲取 , 初始化 METHOD_CACHE 緩存// 反射獲取 Class<?> subscriberClass 中的所有訂閱方法subscriberMethods = findByReflection(subscriberClass);if (! subscriberMethods.isEmpty()) {METHOD_CACHE.put(subscriberClass, subscriberMethods);}} else {// 如果當(dāng)前不是第一次獲取, 則直接返回從 METHOD_CACHE 緩存中獲取的 訂閱者封裝類 集合return subscriberMethods;}// 該分支走不到return subscriberMethods;}/*** 通過反射獲取 Class<?> subscriberClass 訂閱方法* @param subscriberClass 訂閱類* @return*/private List<MySubscriberMethod> findByReflection(Class<?> subscriberClass) {// 要返回的 MySubscriberMethod 集合List<MySubscriberMethod> subscriberMethods = new ArrayList<>();// 通過反射獲取所有帶 @MySubscribe 注解的方法Method[] methods = subscriberClass.getMethods();// 遍歷所有的方法 , 查找注解for (Method method : methods) {// 獲取方法修飾符int modifiers = method.getModifiers();// 獲取方法參數(shù)Class<?>[] params = method.getParameterTypes();// 確保修飾符必須是 public , 參數(shù)長(zhǎng)度必須是 1if (modifiers == Modifier.PUBLIC && params.length == 1) {// 獲取 MySubscribe 注解MySubscribe annotation = method.getAnnotation(MySubscribe.class);// 獲取注解不為空if (annotation != null) {// 獲取線程模式MyThreadMode threadMode = annotation.threadMode();// 此時(shí)已經(jīng)完全確定該方法是一個(gè)訂閱方法 , 直接進(jìn)行封裝MySubscriberMethod subscriberMethod = new MySubscriberMethod(method, // 方法對(duì)象threadMode, // 線程模式params[0] // 事件參數(shù));// 加入到返回集合中subscriberMethods.add(subscriberMethod);}}}return subscriberMethods;}/*** 接收到了 發(fā)布者 Publisher 發(fā)送給本消息中心 的 Event 消息事件對(duì)象* 將該事件對(duì)象轉(zhuǎn)發(fā)給相應(yīng)接收該類型消息的 訂閱者 ( 訂閱對(duì)象 + 訂閱方法 )* 通過事件類型到* Map<Class<?>, CopyOnWriteArrayList<MySubscription>> subscriptionsByEventType* 集合中查找相應(yīng)的 訂閱對(duì)象 + 訂閱方法* @param event*/public void post(Object event) {// 獲取事件類型Class<?> eventType = event.getClass();// 獲取事件類型對(duì)應(yīng)的 訂閱者 集合CopyOnWriteArrayList<MySubscription> subscriptions =subscriptionsByEventType.get(eventType);// 確保訂閱者大于等于 1 個(gè)if (subscriptions != null && subscriptions.size() > 0) {// 遍歷訂閱者并調(diào)用訂閱方法for (MySubscription subscription : subscriptions) {postSingleSubscription(subscription, event);}}}/*** 調(diào)用訂閱方法* @param subscription* @param event*/private void postSingleSubscription(MySubscription subscription, Object event) {// 判斷當(dāng)前線程是否是主線程// 獲取 mainLooper 與 myLooper 進(jìn)行比較 , 如果一致 , 說明該線程是主線程boolean isMainThread = false;// 下面的情況下 , 線程是主線程if (Looper.getMainLooper() == Looper.myLooper()) {isMainThread = true;}// 判斷訂閱方法的線程模式MyThreadMode threadMode = subscription.getSubscriberMethod().getThreadMode();switch (threadMode) {case POSTING:// 直接在發(fā)布線程調(diào)用訂閱方法invokeMethod(subscription, event);break;case MAIN:case MAIN_ORDERED:// 如果發(fā)布線程是主線程, 直接調(diào)用if (isMainThread) {invokeMethod(subscription, event);} else {// 將訂閱方法放到主線程執(zhí)行// 獲取主線程 Looper , 并通過 Looper 創(chuàng)建 HandlerHandler handler = new Handler(Looper.getMainLooper());// 在主線程中執(zhí)行訂閱方法handler.post(new Runnable() {@Overridepublic void run() {invokeMethod(subscription, event);}});}break;case BACKGROUND:case ASYNC:// 如果是主線程 , 切換到子線程執(zhí)行if (isMainThread) {// 在線程池中執(zhí)行方法executorService.execute(new Runnable() {@Overridepublic void run() {invokeMethod(subscription, event);}});} else {// 如果是子線程直接執(zhí)行invokeMethod(subscription, event);}break;}}/*** 調(diào)用訂閱者的訂閱方法* @param subscription 訂閱者對(duì)象 + 訂閱方法* @param event 發(fā)布者傳遞的消息事件*/private void invokeMethod(MySubscription subscription, Object event) {try {// 通過反射調(diào)用訂閱方法subscription.getSubscriberMethod().getMethod().invoke(subscription.getSubscriber(), // 訂閱者對(duì)象event // 事件參數(shù)類型);} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}/*** 取消注冊(cè)* 從 Map<Object, List<Class<?>>> typesBySubscriber 集合中獲取* 訂閱者對(duì)象 中的 訂閱方法 參數(shù)集合** 然后再到* Map<Class<?>, CopyOnWriteArrayList<MySubscription>> subscriptionsByEventType* 集合中獲取 訂閱方法參數(shù)類型 對(duì)應(yīng)的 CopyOnWriteArrayList<MySubscription>> 集合* MySubscription 中封裝了 訂閱者對(duì)象 + 訂閱方法* @param subscriber*/public void unregister(Object subscriber) {// 首先獲取 訂閱者 對(duì)象中的訂閱方法的參數(shù)集合List<Class<?>> types = typesBySubscriber.get(subscriber);// 遍歷參數(shù)類型for (Class<?> type: types) {// 獲取 接收 type 事件類型的 訂閱者集合// MySubscription 中封裝了訂閱者對(duì)象 + 訂閱方法CopyOnWriteArrayList<MySubscription> subscriptions =subscriptionsByEventType.get(type);// 判定 CopyOnWriteArrayList<MySubscription> 集合中的 MySubscription 元素// 如果如果 封裝類對(duì)象 中的 訂閱者對(duì)象 與 本次取消注冊(cè)的訂閱者對(duì)象相同 , 則從集合中移除該訂閱者// 記錄集合大小int subscriptionsSize = subscriptions.size();for (int i = 0; i < subscriptionsSize; i++) {// 獲取 訂閱者對(duì)象 + 訂閱方法 封裝類 對(duì)象MySubscription subscription = subscriptions.get(i);// 如果 封裝類對(duì)象 中的 訂閱者對(duì)象 與 本次取消注冊(cè)的訂閱者對(duì)象相同// 將其從該集合中刪除if (subscription.getSubscriber() == subscriber) {// 刪除 i 索引元素subscriptions.remove(i);// 應(yīng)用新的集合大小 , 集合少了一個(gè)元素subscriptionsSize--;// 第 i 個(gè)元素被刪除了 , 之后會(huì)自增遍歷下一個(gè)元素// 下一次遍歷的還是第 i 個(gè)元素// 由于后面循環(huán)操作需要自增 , 想要之后仍然遍歷第 i 個(gè)元素 ,// 這里對(duì) i 進(jìn)行自減操作i--;}}// 刪除了訂閱者 , 就完成了取消注冊(cè)操作}} } 《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀
總結(jié)
以上是生活随笔為你收集整理的【EventBus】事件通信框架 ( 取消注册 | 获取事件参数类型 | 根据事件类型获取订阅者 | 移除相关订阅者 )的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【EventBus】事件通信框架 ( 发
- 下一篇: 【EventBus】事件通信框架 ( 总