【EventBus】事件通信框架 ( 发送事件 | 判断发布线程是否是主线程 | 子线程切换主线程 | 主线程切换子线程 )
文章目錄
- 前言
- 一、根據不同的線程模式進行不同的線程切換操作
- 二、完整代碼示例
前言
發(fā)布線程發(fā)布事件之后 , 消息中心需要轉發(fā)這些事件 , 并執(zhí)行相應的訂閱方法 ;
在轉發(fā)的過程中 , 需要針對訂閱方法的 @Subscribe 注解的不同 threadMode 屬性進行不同的線程模式處理 ;
假如訂閱方法的線程模式屬性屬性是 POSTING , 直接在發(fā)布線程中調用訂閱方法 ;
假如訂閱方法的線程模式屬性屬性是 MAIN , 則需要判定發(fā)布線程是否是主線程 ;
- 如果發(fā)布線程是主線程 , 則直接執(zhí)行訂閱方法 ;
- 如果發(fā)布線程不是主線程 , 則需要在主線程中執(zhí)行訂閱方法 ;
假如訂閱方法的線程模式屬性是 BACKGROUND , 則需要判定發(fā)布線程是否是主線程 ;
- 如果發(fā)布線程是主線程 , 則切換到子線程執(zhí)行訂閱方法 ;
- 如果發(fā)布線程不是主線程 , 則直接執(zhí)行訂閱方法 ;
可參考 【Android 異步操作】Android 線程切換 ( 判定當前線程是否是主線程 | 子線程中執(zhí)行主線程方法 | 主線程中執(zhí)行子線程方法 ) 博客的部分操作 ;
一、根據不同的線程模式進行不同的線程切換操作
首先 , 獲取當前線程是否是主線程 : 參考 【Android 異步操作】Android 線程切換 ( 判定當前線程是否是主線程 | 子線程中執(zhí)行主線程方法 | 主線程中執(zhí)行子線程方法 ) 一、判定當前線程是否是主線程 博客章節(jié) ;
// 判斷當前線程是否是主線程// 獲取 mainLooper 與 myLooper 進行比較 , 如果一致 , 說明該線程是主線程boolean isMainThread = false;// 下面的情況下 , 線程是主線程if (Looper.getMainLooper() == Looper.myLooper()) {isMainThread = true;}然后 , 獲取訂閱方法的線程模式 , 不同的線程模式進行不同的處理 ;
// 判斷訂閱方法的線程模式MyThreadMode threadMode = subscription.getSubscriberMethod().getThreadMode();switch (threadMode) {case POSTING:break;case MAIN:break;case MAIN_ORDERED:break;case BACKGROUND:break;case ASYNC:break;}POSTING 線程模式下 , 不進行線程切換 , 直接調用反射方法執(zhí)行訂閱方法即可 ;
case POSTING:// 直接在發(fā)布線程調用訂閱方法invokeMethod(subscription, event);break;MAIN 線程模式下 , 需要判定發(fā)布線程是否是主線程 ;
- 如果發(fā)布線程是主線程 , 則直接執(zhí)行訂閱方法 ;
- 如果發(fā)布線程不是主線程 , 則需要在主線程中執(zhí)行訂閱方法 ;
為了方便 , 這里將 MAIN_ORDERED 與 MAIN 分支進行合并處理 ;
參考 【Android 異步操作】Android 線程切換 ( 判定當前線程是否是主線程 | 子線程中執(zhí)行主線程方法 | 主線程中執(zhí)行子線程方法 ) 二、子線程中執(zhí)行主線程方法 博客章節(jié) ;
case MAIN:case MAIN_ORDERED:// 如果發(fā)布線程是主線程, 直接調用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;BACKGROUND 線程模式下 , 判定發(fā)布線程是否是主線程 ;
- 如果發(fā)布線程是主線程 , 則切換到子線程執(zhí)行訂閱方法 ;
- 如果發(fā)布線程不是主線程 , 則直接執(zhí)行訂閱方法 ;
為了方便 , 這里將 ASYNC 與 BACKGROUND 分支進行合并處理 ;
參考 【Android 異步操作】Android 線程切換 ( 判定當前線程是否是主線程 | 子線程中執(zhí)行主線程方法 | 主線程中執(zhí)行子線程方法 ) 三、主線程中執(zhí)行子線程方法 博客章節(jié) ;
case BACKGROUND:case ASYNC:// 如果是主線程 , 切換到子線程執(zhí)行if (isMainThread) {// 在線程池中執(zhí)行方法executorService.execute(new Runnable() {@Overridepublic void run() {invokeMethod(subscription, event);}});} else {// 如果是子線程直接執(zhí)行invokeMethod(subscription, event);}break;部分代碼示例 :
/*** 調用訂閱方法* @param subscription* @param event*/private void postSingleSubscription(MySubscription subscription, Object event) {// 判斷當前線程是否是主線程// 獲取 mainLooper 與 myLooper 進行比較 , 如果一致 , 說明該線程是主線程boolean isMainThread = false;// 下面的情況下 , 線程是主線程if (Looper.getMainLooper() == Looper.myLooper()) {isMainThread = true;}// 判斷訂閱方法的線程模式MyThreadMode threadMode = subscription.getSubscriberMethod().getThreadMode();switch (threadMode) {case POSTING:// 直接在發(fā)布線程調用訂閱方法invokeMethod(subscription, event);break;case MAIN:case MAIN_ORDERED:// 如果發(fā)布線程是主線程, 直接調用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;}}二、完整代碼示例
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<>();/*** 解除注冊時使用* Key - 訂閱者對象* Value - 訂閱者對象中所有的訂閱方法的事件參數(shù)類型集合** 根據該訂閱者對象 , 查找所有訂閱方法的事件參數(shù)類型 , 然后再到 METHOD_CACHE 中 ,* 根據事件參數(shù)類型 , 查找對應的 MySubscriberMethod 集合* MySubscriberMethod 中封裝 訂閱者對象 + 訂閱方法**/private final Map<Object, List<Class<?>>> typesBySubscriber;/*** Key - 訂閱者方法事件參數(shù)類型* Value - 封裝 訂閱者對象 與 訂閱方法 的 MySubscription 集合* 在構造函數(shù)中初始化* CopyOnWriteArrayList 在寫入數(shù)據時會拷貝一個副本 ,* 寫完之后 , 將引用指向新的副本 ,* 該集合的線程安全級別很高*/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;}/*** 注冊訂閱者* @param subscriber*/public void register(Object subscriber) {// 獲取訂閱者所屬類Class<?> clazz = subscriber.getClass();// 查找訂閱方法List<MySubscriberMethod> subscriberMethods = findSubscriberMethods(clazz);// 遍歷所有訂閱方法 , 進行訂閱// 首先確保查找到的訂閱方法不為空 , 并且個數(shù)大于等于 1 個if (subscriberMethods != null && !subscriberMethods.isEmpty()) {for (MySubscriberMethod method : subscriberMethods) {// 正式進行訂閱subscribe(subscriber, method);}}}/*** 方法訂閱* 將 訂閱方法參數(shù)類型 和 訂閱類 + 訂閱方法 封裝類 , 保存到* Map<Class<?>, CopyOnWriteArrayList<MySubscription>> subscriptionsByEventType 集合中* Key - 訂閱者方法事件參數(shù)類型* Value - 封裝 訂閱者對象 與 訂閱方法 的 MySubscription 集合** 取消注冊數(shù)據準備* 取消注冊數(shù)據存放在 Map<Object, List<Class<?>>> typesBySubscriber 集合中* Key - 訂閱者對象* Value - 訂閱者方法參數(shù)集合** @param subscriber 訂閱者對象* @param subscriberMethod 訂閱方法*/private void subscribe(Object subscriber, MySubscriberMethod subscriberMethod) {// 獲取訂閱方法接收的參數(shù)類型Class<?> eventType = subscriberMethod.getEventType();// 獲取 eventType 參數(shù)類型對應的 訂閱者封裝類 ( 封裝 訂閱者對象 + 訂閱方法 ) 集合CopyOnWriteArrayList<MySubscription> subscriptions =subscriptionsByEventType.get(eventType);// 如果獲取的集合為空 , 說明 eventType 參數(shù)對應的訂閱方法一個也沒有注冊過// 這里先創(chuàng)建一個集合 , 放到 subscriptionsByEventType 鍵值對中if (subscriptions == null) {// 創(chuàng)建集合subscriptions = new CopyOnWriteArrayList<>();// 將集合設置到 subscriptionsByEventType 鍵值對集合中subscriptionsByEventType.put(eventType, subscriptions);}// 封裝 訂閱者對象 + 訂閱方法 對象MySubscription subscription = new MySubscription(subscriber, subscriberMethod);// 將創(chuàng)建的 訂閱者對象 + 訂閱方法 對象 添加到 CopyOnWriteArrayList 集合中subscriptions.add(subscription);// 為取消注冊準備數(shù)據// 設置 Map<Object, List<Class<?>>> typesBySubscriberList<Class<?>> eventTypes = typesBySubscriber.get(subscriber);if (eventTypes == null) {// 創(chuàng)建新的集合, 用于存放訂閱方法的參數(shù)類型eventTypes = new ArrayList<>();// 將新的集合設置到 Map<Object, List<Class<?>>> typesBySubscriber 集合中typesBySubscriber.put(subscriber, eventTypes);}// 將新的 訂閱方法類型 放入到集合中eventTypes.add(eventType);}/*** 根據訂閱方法的事件參數(shù)查找訂閱方法* @param subscriberClass 訂閱者對象的類型* @return*/private List<MySubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {// 獲取 Class<?> clazz 參數(shù)類型對應的 訂閱者封裝類List<MySubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);// 此處后期重構, 減少縮進if (subscriberMethods == null) {// 說明是首次獲取 , 初始化 METHOD_CACHE 緩存// 反射獲取 Class<?> subscriberClass 中的所有訂閱方法subscriberMethods = findByReflection(subscriberClass);if (! subscriberMethods.isEmpty()) {METHOD_CACHE.put(subscriberClass, subscriberMethods);}} else {// 如果當前不是第一次獲取, 則直接返回從 METHOD_CACHE 緩存中獲取的 訂閱者封裝類 集合return subscriberMethods;}// 該分支走不到return null;}/*** 通過反射獲取 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ù)長度必須是 1if (modifiers == Modifier.PUBLIC && params.length == 1) {// 獲取 MySubscribe 注解MySubscribe annotation = method.getAnnotation(MySubscribe.class);// 獲取注解不為空if (annotation != null) {// 獲取線程模式MyThreadMode threadMode = annotation.threadMode();// 此時已經完全確定該方法是一個訂閱方法 , 直接進行封裝MySubscriberMethod subscriberMethod = new MySubscriberMethod(method, // 方法對象threadMode, // 線程模式params[0] // 事件參數(shù));// 加入到返回集合中subscriberMethods.add(subscriberMethod);}}}return subscriberMethods;}/*** 接收到了 發(fā)布者 Publisher 發(fā)送給本消息中心 的 Event 消息事件對象* 將該事件對象轉發(fā)給相應接收該類型消息的 訂閱者 ( 訂閱對象 + 訂閱方法 )* 通過事件類型到* Map<Class<?>, CopyOnWriteArrayList<MySubscription>> subscriptionsByEventType* 集合中查找相應的 訂閱對象 + 訂閱方法* @param event*/public void post(Object event) {// 獲取事件類型Class<?> eventType = event.getClass();// 獲取事件類型對應的 訂閱者 集合CopyOnWriteArrayList<MySubscription> subscriptions =subscriptionsByEventType.get(eventType);// 確保訂閱者大于等于 1 個if (subscriptions != null && subscriptions.size() > 0) {// 遍歷訂閱者并調用訂閱方法for (MySubscription subscription : subscriptions) {postSingleSubscription(subscription, event);}}}/*** 調用訂閱方法* @param subscription* @param event*/private void postSingleSubscription(MySubscription subscription, Object event) {// 判斷當前線程是否是主線程// 獲取 mainLooper 與 myLooper 進行比較 , 如果一致 , 說明該線程是主線程boolean isMainThread = false;// 下面的情況下 , 線程是主線程if (Looper.getMainLooper() == Looper.myLooper()) {isMainThread = true;}// 判斷訂閱方法的線程模式MyThreadMode threadMode = subscription.getSubscriberMethod().getThreadMode();switch (threadMode) {case POSTING:// 直接在發(fā)布線程調用訂閱方法invokeMethod(subscription, event);break;case MAIN:case MAIN_ORDERED:// 如果發(fā)布線程是主線程, 直接調用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;}}/*** 調用訂閱者的訂閱方法* @param subscription 訂閱者對象 + 訂閱方法* @param event 發(fā)布者傳遞的消息事件*/private void invokeMethod(MySubscription subscription, Object event) {try {// 通過反射調用訂閱方法subscription.getSubscriberMethod().getMethod().invoke(subscription.getSubscriber(), // 訂閱者對象event // 事件參數(shù)類型);} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}}
總結
以上是生活随笔為你收集整理的【EventBus】事件通信框架 ( 发送事件 | 判断发布线程是否是主线程 | 子线程切换主线程 | 主线程切换子线程 )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【EventBus】事件通信框架 ( 发
- 下一篇: 【EventBus】事件通信框架 ( 取