android EventBus详解(三)

简介: post()方法调用流程 我们继续来看EventBus类,的另一个入口方法post() //已省略部分代码public void post(Object event) { PostingThreadState postingState = currentPostingThreadState.get(); List<Object> eventQu

post()方法调用流程

我们继续来看EventBus类,的另一个入口方法post()

//已省略部分代码
public void post(Object event) {
    PostingThreadState postingState = currentPostingThreadState.get();
    List<Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);

    if (!postingState.isPosting) {
        postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
        postingState.isPosting = true;
        if (postingState.canceled) {
            throw new EventBusException("Internal error. Abort state was not reset");
        }
        while (!eventQueue.isEmpty()) {
            postSingleEvent(eventQueue.remove(0), postingState);
        }
        postingState.isPosting = false;
        postingState.isMainThread = false;
    }
}
AI 代码解读

post() 方法首先从 currentPostingThreadState 对象中取了一个 PostingThreadState ,我们来看看这个 currentPostingThreadState 对象的创建代码。

private final ThreadLocal<PostingThreadState> currentPostingThreadState = new
ThreadLocal<PostingThreadState>() {
    @Override
    protected PostingThreadState initialValue() {
        return new PostingThreadState();
    }
};
AI 代码解读

ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,而这段数据是不会与其他线程共享的。其内部原理是通过生成一个它包裹的泛型对象的数组,在不同的线程会有不同的数组索引值,通过这样就可以做到每个线程通过 get() 方法获取的时候,取到的只能是自己线程所对应的数据。 
在 EventBus 中, ThreadLocal 所包裹的是一个 PostingThreadState 类,它仅仅是封装了一些事件发送中过程所需的数据。

final static class PostingThreadState {
    //通过post方法参数传入的事件集合
    final List<Object> eventQueue = new ArrayList<Object>(); 
    boolean isPosting; //是否正在执行postSingleEvent()方法
    boolean isMainThread;
    Subscription subscription;
    Object event;
    boolean canceled;
    }
AI 代码解读

回到 post() 方法,我们看到其核心代码是这句:

while (!eventQueue.isEmpty()) {
    postSingleEvent(eventQueue.remove(0), postingState);
}
AI 代码解读

每次调用post()的时候都会传入一个事件,这个事件会被加入到队列。而每次执行postSingleEvent()都会从队列中取出一个事件,这样不停循环取出事件处理,直到队列全部取完。 
再看 postSingleEvent() 方法

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    Class<?> eventClass = event.getClass();
    boolean subscriptionFound = false;
    if (eventInheritance) {
        //获取到eventClass所有父类的集合
        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) {
            Log.d(TAG, "No subscribers registered for event " + eventClass);
        }

        //参考sendNoSubscriberEvent注释
        if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                eventClass != SubscriberExceptionEvent.class) {
            post(new NoSubscriberEvent(this, event));
        }
    }
}
AI 代码解读

还记得 EventBusBuild 中的 eventInheritance是做什么的吗?它表示一个子类事件能否响应父类的onEvent() 方法。
再往下看 lookupAllEventTypes() 它通过循环和递归一起用,将一个类的父类,接口,父类的接口,父类接口的父类,全部添加到全局静态变量 eventTypes 集合中。之所以用全局静态变量的好处在于用全局静态变量只需要将那耗时又复杂的循环+递归方法执行一次就够了,下次只需要通过 key:事件类名 来判断这个事件是否以及执行过 lookupAllEventTypes() 方法。

postSingleEventForEventType()方法

然后我们继续往下,看发送方法 postSingleEventForEventType()

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
        //所有订阅了eventClass的事件集合
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    if (subscriptions != null && !subscriptions.isEmpty()) {
        //回调subscription的响应方法
        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;
}
AI 代码解读

它首先通过这一句

subscriptions = subscriptionsByEventType.get(eventClass);
AI 代码解读

获取到所有订阅了 eventClass 的事件集合,之前有讲过, subscriptionsByEventType 是一个以 key:订阅的事件 value:订阅这个事件的所有订阅者集合 的 Map 。
最后通过循环,遍历所有订阅了 eventClass 事件的订阅者,并向每一个订阅者发送事件。
看它的发送事件的方法:
postToSubscription(subscription, event, postingState.isMainThread); 
噢,又回到了和之前 Subscribe 流程中处理粘滞事件相同的方法里————对声明不同线程模式的事件做不同的响应方法,最终都是通过invokeSubscriber()反射订阅者类中的以onEvent开头的方法。

unregister()

我们继续来看EventBus类,的最后一个入口方法unregister()

public synchronized void unregister(Object subscriber) {
    List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
    if (subscribedTypes != null) {
        for (Class<?> eventType : subscribedTypes) {
            //取消注册subscriber对eventType事件的响应
            unsubscribeByEventType(subscriber, eventType);
        }
        //当subscriber对所有事件都不响应以后,移除订阅者
        typesBySubscriber.remove(subscriber);
    }
}
AI 代码解读

之前讲过typesBySubscriber key:订阅者对象 value:这个订阅者订阅的事件集合,表示当前订阅者订阅了哪些事件。 
首先遍历要取消注册的订阅者订阅的每一个事件,调用unsubscribeByEventType(),从这个事件的所有订阅者集合中将要取消注册的订阅者移除。最后再以:当前订阅者为 key 全部订阅事件集合为 value 的一个 Map 的 Entry 移除,就完成了取消注册的全部过程。

EventBus工作原理

最后我们再来从设计者的角度看一看EventBus的工作原理。

订阅的逻辑

1、首先是调用register()方法注册一个订阅者A。
2、遍历这个订阅者A的全部以onEvent开头的订阅方法。
3、将A订阅的所有事件分别作为 key,所有能响应 key 事件的订阅者的集合作为 value,存入 Map<事件,List<订阅这个事件的订阅者>>

4、以A的类名为 key,所有 onEvent 参数类型的类名组成的集合为 value,存入 Map<订阅者,List<订阅的事件>>。

4.1、如果是订阅了粘滞事件的订阅者,从粘滞事件缓存区获取之前发送过的粘滞事件,响应这些粘滞事件。

发送事件的逻辑

1、取当前线程的发送事件封装数据,并从封装的数据中拿到发送事件的事件队列。
2、将要发送的事件加入到事件队列中去。
3、循环,每次发送队列中的一条事件给所有订阅了这个事件的订阅者。
3.1、如果是子事件可以响应父事件的事件模式,需要先将这个事件的所有父类、接口、父类的接口、父类接口的父类都找到,并让订阅了这些父类信息的订阅者也都响应这条事件。

响应事件的逻辑

1、发送事件处理完成后会将事件交给负责响应的逻辑部分。
2、首先判断时间的响应模式,响应模式分为四种:
PostThread 在哪个线程调用的post()方法,就在哪个线程执行响应方法。
MainThread 无论是在哪个线程调用的post()方法,最终都在主线程执行响应方法。
BackgroundThread 无论是在哪个线程调用的post()方法,最终都在后台线程执行响应方法。(串行执行,一次只执行一个任务,其他任务在队列中处于等待状态)
Async 无论是在哪个线程调用的post()方法,最终都在后台线程执行响应方法。(并行执行,只要有任务就开一个线程让他执行)

取消注册的逻辑

1、首先是调用unregister()方法拿到要取消注册的订阅者B。 
2、从这个类订阅的时候存入的 Map<订阅者,List<订阅的事件>> 中,拿到这个类的订阅事件集合。

3、遍历订阅时间集合,在注册的时候存入的 Map<事件,List<订阅这个事件的订阅者>> 中将对应订阅事件的订阅者集合中的这个订阅者移除。

4、将步骤2中的 Map<订阅者,List<订阅的事件>> 中这个订阅者相关的 Entry 移除。

工作原理图示

开源实验室:图8

目录
打赏
0
0
0
0
498
分享
相关文章
Android | 这是一份详细的 EventBus 使用教程 | 牛气冲天新年征文
Android | 这是一份详细的 EventBus 使用教程 | 牛气冲天新年征文
941 0
Android | 这是一份详细的 EventBus 使用教程 | 牛气冲天新年征文
Android基础入门:EventBus实现总线数据分发
我们之前学习了Activity-》Activity中的数据传送,是通过intent.putExtra()方法实现的,如果要实现Activity-》Fragment或者Fragment-》Fragment都是不方便的,通过EventBus能很好的解决这个问题
212 0
【EventBus】发布-订阅模式 ( Android 中使用 发布-订阅模式 进行通信 )
【EventBus】发布-订阅模式 ( Android 中使用 发布-订阅模式 进行通信 )
385 0
【EventBus】发布-订阅模式 ( Android 中使用 发布-订阅模式 进行通信 )
Android EventBus使用(不含源码解析)
官方文档:https://github.com/greenrobot/EventBus simplifies the communication between components decouples event senders and receiv...
1465 0
Android项目实战(十三):浅谈EventBus
原文:Android项目实战(十三):浅谈EventBus 概述: EventBus是一款针对Android优化的发布/订阅事件总线。 主要功能是替代Intent,Handler,BroadCast在Fragment,Activity,Service。
1058 0
Android——EventBus源码分析
停停走走又几天,断断续续这一年。时间还是如此之快,转眼间又到了17年的末尾了。趁着这些天还能静下心来看看代码,赶紧记录下自己的心得。 这一周,工作任务不多,总是想要看点什么但又不清楚该看啥,有些迷茫~不过既然自己还想要在Android路上继续前行,那么请深入探索吧! OK,闲话不多说。
770 0
simple Android EventBus code
import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.
725 0

热门文章

最新文章

  • 1
    Android历史版本与APK文件结构
    12
  • 2
    【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
    28
  • 3
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
    2
  • 4
    APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
    14
  • 5
    【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    4
  • 6
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    3
  • 7
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    2
  • 8
    escrcpy:【技术党必看】Android开发,Escrcpy 让你无线投屏新体验!图形界面掌控 Android,30-120fps 超流畅!🔥
    3
  • 9
    Cellebrite UFED 4PC 7.71 (Windows) - Android 和 iOS 移动设备取证软件
    6
  • 10
    【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
    2
  • 1
    android FragmentManager 删除所有Fragment 重建
    18
  • 2
    Android实战经验之Kotlin中快速实现MVI架构
    28
  • 3
    即时通讯安全篇(一):正确地理解和使用Android端加密算法
    35
  • 4
    escrcpy:【技术党必看】Android开发,Escrcpy 让你无线投屏新体验!图形界面掌控 Android,30-120fps 超流畅!🔥
    42
  • 5
    【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
    141
  • 6
    Cellebrite UFED 4PC 7.71 (Windows) - Android 和 iOS 移动设备取证软件
    46
  • 7
    【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    58
  • 8
    Android历史版本与APK文件结构
    159
  • 9
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    48
  • 10
    【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
    41
  • AI助理

    你好,我是AI助理

    可以解答问题、推荐解决方案等