IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> EventBus执行原理详解 -> 正文阅读

[移动开发]EventBus执行原理详解

EventBus是一款发布/订阅事件总线框架,基于观察者模式。将事件的接收者和发送者分开,简化了组件之间的通信,使用简单、效率高、体积小。
在这里插入图片描述
EventBus实现原理分析主要从以下4点着手分析:
1.EventBus事件的注册EventBus.getDefault().register(this);
2.EventBus事件的解注册EventBus.getDefault().unRegister(this);
3.EventBus发送事件EventBus.getDefault().post(new MessageEvent());
4.EventBus发送粘性事件EventBus.getDefault().postSticky(new MessageEvent());

分析前,列举出在EventBus在实现发布/订阅事件总线框架过程中使用到的5个核心类。

在这里插入图片描述

数据类型解释说明
subscriptionsByEventType存储key&value键值对,其中key是eventType,value是List集合。List集合中存放的数据类型是Subscription。
typesBySubscriber存储key&value键值对,其中key是subscriber订阅者Class对象,value是List集合。集合中存放的数据类型是EventType的List集合。
EventType订阅方法参数类型。
Subscription一种数据类型,类似JavaBean,封装了SubscriberMethod。
SubscriberMethod内部封装了订阅方法的Method、线程类型(threadMode)、事件类型(eventType)、事件优先级、是否是粘性事件等。

EventBus事件的注册
注册事件所要实现的结果,是使用EventBus中定义的Map——subscriptionsByEventType,去收集和存放 以事件类Class为key,和以订阅方法属性的包装类集合作为value的键值对。
在这里插入图片描述

/// EventBus.java
public void register(Object subscriber) {
        ...
    Class<?> subscriberClass = subscriber.getClass();
    // 获取包含订阅方法属性对象SubscriberMethod的集合
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
               // 遍历,以订阅方法事件类型分类,将订阅方法包装对象存放到Map中
                subscribe(subscriber, subscriberMethod);
            }
       }
 }

SubscriberMethodFinder通过当前注册类subscriberClass,获取SubscriberMethod的集合。SubscriberMethod中封装了订阅方法所有的方法属性。之后拿着SubscriberMethod的集合进行遍历,并执行subscribe方法。通过方法subscribe将所有注册类下的订阅方法,以订阅事件EventType作为分类,存放到subscriptionsByEventType字典Map中。

/// EventBus.java
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;
        // Subscription 封装了订阅方法的订阅属性
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<>();
            // 以eventType进行分类存放订阅者方法属性包装对象
            subscriptionsByEventType.put(eventType, subscriptions);
        } ...

        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;
            }
        }

SubscriberMethodFinder
如何通过注册类获取SubscriberMethod集合?

// SubscriberMethodFinder.java
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        ..
   // 使有生成的索引,也强制使用反射(默认值:false)。  
    if (ignoreGeneratedIndex) {
      subscriberMethods = findUsingReflection(subscriberClass);
  } else {
    // 默认执行这里
    subscriberMethods = findUsingInfo(subscriberClass);
  }
        ...
}

// 方法执行流转到这里继续执行
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
	... findUsingReflectionInSingleClass(findState); ...
}

// 方法执行流转到这里,通过反射执行必要逻辑
private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
      // This is faster than getMethods, especially when subscribers are fat classes like Activities
      // 反射获取所有方法类的方法
      methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
      // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
      try {
        // 失败,则反射获取所有子类、父类的public访问权限修饰符的方法
          methods = findState.clazz.getMethods();
      } catch (LinkageError error) { ...}
   }
   for (Method method : methods) {
       int modifiers = method.getModifiers();
       // 筛选访问修饰符仅是public的订阅方法
       if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
           Class<?>[] parameterTypes = method.getParameterTypes();
           // 订阅方法参数——事件类型,必须只一个。
           if (parameterTypes.length == 1) {
              // 获取订阅方法的注解类型实例——被{@link Subscribe}注释的事件处理方法
               Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
               if (subscribeAnnotation != null) {
                 // 获取订阅方法参数的事件类型
                   Class<?> eventType = parameterTypes[0];
                   if (findState.checkAdd(method, eventType)) {
                       // 检测未添加,则获取该订阅方法执行的线程模式 
                       ThreadMode threadMode = subscribeAnnotation.threadMode();
                       // 封装订阅方法的方法、事件类型、线程模式、优先级、粘性否等,并添加存入FindState.subscriberMethods集合中[List<SubscriberMethod>]
                       findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
              }
                    ... ...
   // 返回集合List<SubscriberMethod>
   return getMethodsAndRelease(findState);
}

// 在方法getMethodsAndRelease中,返回集合List<SubscriberMethod>。因此在EvebtBus.register执行subscriberMethodFinder.findSubscriberMethods(subscriberClass)时,可获取到集合List<SubscriberMethod>的对象。
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
    List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
    findState.recycle();
     ......
    return subscriberMethods;
}

EventBus发送事件
EventBus发送事件原理是,subscriptionsByEventType通过事件类型EventType.class获取到订阅方法包装类List集合。然后通过postToSubscription方法,在方法中匹配发送事件的线程模式threadMode,例如匹配到主线程时,会直接通过反射调用订阅方法subscription.subscriberMethod.method.invoke(subscription.subscriber, event)实现事件的发布-订阅执行。
在这里插入图片描述

/// EventBus.java 发起事件,经到这里会匹配发起事件的线程模式
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    switch (subscription.subscriberMethod.threadMode) {
        case POSTING:
            // 直接通过invoke反射调用订阅方法的方法执行通知
            invokeSubscriber(subscription, event);
            break;
        case MAIN:
            if (isMainThread) {// 上面时序图画的这个过程
                // 直接通过invoke反射调用订阅方法的方法执行通知
                invokeSubscriber(subscription, event);
            } else {
                // mainThreadPoster实现类 HandlerPoster extends Handler,
                // 即如果发送通知不在主线程,该通知会通过Handler将线程切换到主线程,
                // 然后通过Android消息机制在主线程的handleMessage中通过invoke反射调用订阅方法的方法执行通知
                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 implements Runnable 将事件处理交给工作线程线程池ExecutorService.execute(this)处理,终在工作线程中通过invoke反射调用订阅方法的方法执行通知
                backgroundPoster.enqueue(subscription, event);
            } else {
                // 直接通过invoke反射调用订阅方法的方法执行通知
                invokeSubscriber(subscription, event);
            }
            break;
        case ASYNC://底部时序图画的这个过程。后台线程执行该事件-订阅方法
            asyncPoster.enqueue(subscription, event);
            break;
        default:
            throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
    }
}

如果匹配到的是上面源码中case ASYNC,AsyncPoster会将Subscription和事件类的实例封装到PendingPost中,并入队PendingPostQueue。然后在异步线程中执行eventBus.invokeSubscriber(pendingPost);,并最终是在工作线程中通过反射调用执行订阅方法。具体代码是subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
在这里插入图片描述

EventBus事件的解注册
EventBus解注册原理则是清空typesBySubscriber和subscriptionsByEventType。
源码实现很简单~

// EventBus.java 方法解注册
public synchronized void unregister(Object subscriber) {
    List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
    if (subscribedTypes != null) {
        for (Class<?> eventType : subscribedTypes) {
           // 解注册 —— 移除Map中当前类(定义的)对应的事件类型的Subscription集合
            unsubscribeByEventType(subscriber, eventType);
        }
        // 解注册当前类的注册,直接在Map中remove掉即可
        typesBySubscriber.remove(subscriber);
    } else {
        logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
    }
}
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
	// 移除(解注册)条件:需满足是eventType事件类型的方法订阅
    List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions != null) {
        int size = subscriptions.size();
        for (int i = 0; i < size; i++) {
            Subscription subscription = subscriptions.get(i);
            if (subscription.subscriber == subscriber) {
               // 移除(解注册)条件:需满足在当前注册类的方法订阅,才能执行移除
                subscription.active = false;
                subscriptions.remove(i);
                i--;
                size--;
            }
        }
    }
 }

EventBus发送粘性事件
EventBus在处理粘性事件的原理,先通过postSticky方法将键值对(key=事件类型class,value=事件实例)存入字典stickyEvents中。而真实的执行粘性事件的订阅方法,则是通过EventBus.register()来实现的。发布事件的订阅类在注册时如果判断得知该事件属于粘性事件,那么就会通过循环遍历字典stickyEvents,以执行订阅方法的执行~ 【在执行订阅方法进入方法postToSubscription时,跟非粘性事件处理就一致了】

// EventBus.js 发起粘性事件通知
public void postSticky(Object event) {
    synchronized (stickyEvents) {
    	// 在发起事件的时候,将键值对(key=事件类型class,value=事件实例)存入字典stickyEvents中
        stickyEvents.put(event.getClass(), event);
    }
    // Should be posted after it is putted, in case the subscriber wants to remove immediately
    post(event);
}
///EventBus.js
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
	...
	if (subscriberMethod.sticky) {// 如果是粘性事件
		if (eventInheritance) {// 默认情况下true,EventBus考虑事件类(订阅者)的层次结构(超类的订阅者将被通知)
            Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
            // 遍历字典stickyEvents,获取存入的key=事件类型class,value=事件实例
            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);
        }
	}
}

/// 从这里开启粘性订阅方法的被调用执行~
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
    if (stickyEvent != null) {
        // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
        // --> Strange corner case, which we don't take care of here.
        // 如果存在了粘性事件,那么将会通过方法postToSubscription发起订阅方法执行
        postToSubscription(newSubscription, stickyEvent, isMainThread());
    } 
}
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-04-01 23:32:51  更:2022-04-01 23:35:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 19:59:35-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码