ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

EventBus 发送的消息,如何做到线程切换?,看完吊打面试官

2021-12-24 12:31:43  阅读:195  来源: 互联网

标签:面试官 主线 吊打 线程 EventBus MAIN event subscription


作者:承香墨影

链接:https://juejin.im/post/6844903944561377293

二. EventBus 的线程切换


2.1 EventBus 切换线程

EventBus 是一个基于观察者模式的事件订阅/发布框架。利用 EventBus 可以在不同模块之间,实现低耦合的消息通信。

EventBus 诞生以来这么多年,在很多生产项目中都可以看到它的身影。而从更新日志可以看到,除了体积小,它还很稳定,这两年就没更新过,最后一次更新也只是因为支持所有的 JVM,让其使用范围不仅仅局限在 Android 上。

可谓是非常的稳定,稳定到让人有一种感觉,要是你使用 EventBus 出现了什么问题,那一定是你使用的方式不对。

EventBus 的使用方式,对于 Android 老司机来说,必然是不陌生的,相关资料太多,这里就不再赘述了。

在 Android 下,线程的切换是一个很常用而且很必须的操作,EventBus 除了可以订阅和发送消息之外,它还可以指定接受消息处理消息的线程。

也就是说,无论你 post() 消息时处在什么线程中,EventBus 都可以将消息分发到你指定的线程上去,听上去就感觉非常的方便。

不过无论怎么切换,无外乎几种情况:

  • UI 线程切子线程。

  • 子线程切 UI 线程。

  • 子线程切其他子线程。

在我们使用 EventBus 注册消息的时候,可以通过 @Subscribe 注解来完成注册事件, @Subscribe 中可以通过参数 threadMode 来指定使用那个线程来接收消息。

@Subscribe(threadMode = ThreadMode.MAIN)

fun onEventTest(event:TestEvent){

// 处理事件

}

threadMode 是一个 enum,有多种模式可供选择:

  1. POSTING,默认值,那个线程发就是那个线程收。

  2. MAIN,切换至主线程接收事件。

  3. MAIN_ORDERED,v3.1.1 中新增的属性,也是切换至主线程接收事件,但是和 MAIN 有些许区别,后面详细讲。

  4. BACKGROUND,确保在子线程中接收事件。细节就是,如果是主线程发送的消息,会切换到子线程接收,而如果事件本身就是由子线程发出,会直接使用发送事件消息的线程处理消息。

  5. ASYNC,确保在子线程中接收事件,但是和 BACKGROUND 的区别在于,它不会区分发送线程是否是子线程,而是每次都在不同的线程中接收事件。

EventBus 的线程切换,主要涉及的方法就是 EventBus 的 postToSubscription() 方法。

private void postToSubscription(Subscription subscription, Object event, boolean isM

《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》

【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享

ainThread) {

switch (subscription.subscriberMethod.threadMode) {

case POSTING:

invokeSubscriber(subscription, event);

break;

case MAIN:

if (isMainThread) {

invokeSubscriber(subscription, event);

} else {

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.enqueue(subscription, event);

} else {

invokeSubscriber(subscription, event);

}

break;

case ASYNC:

asyncPoster.enqueue(subscription, event);

break;

default:

throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);

}

}

可以看到,在 postToSubscription() 方法中,对我们配置的 threadMode 值进行了处理。

这端代码逻辑非常的简单,接下来我们看看它们执行的细节。

2.2 切换至主线程接收事件

想在主线程接收消息,需要配置 threadMode 为 MAIN。

case MAIN:

if (isMainThread) {

invokeSubscriber(subscription, event);

} else {

mainThreadPoster.enqueue(subscription, event);

}

这一段的逻辑很清晰,判断是主线程就直接处理事件,如果是非主线程,就是用 mainThreadPoster 处理事件。

追踪 mainThreadPoster 的代码,具体的逻辑代码都在 HandlerPoster 类中,它实现了 Poster 接口,这就是一个普通的 Handler,只是它的 Looper 使用的是主线程的 「Main Looper」,可以将消息分发到主线程中。

为了提高效率,EventBus 在这里还做了一些小优化,值得我们借鉴学习。

为了避免频繁的向主线程 sendMessage(),EventBus 的做法是在一个消息里尽可能多的处理更多的消息事件,所以使用了 while 循环,持续从消息队列 queue 中获取消息。

同时为了避免长期占有主线程,间隔 10ms (maxMillisInsideHandleMessage = 10ms)会重新发送 sendMessage(),用于让出主线程的执行权,避免造成 UI 卡顿和 ANR。

MAIN 可以确保事件的接收,在主线程中,需要注意的是,如果事件就是在主线程中发送的,则使用 MAIN 会直接执行。为了让开发和可配置的成都更高,在 EventBus v3.1.1 新增了 MAIN_ORDERED,它不会区分当前线程,而是通通使用 mainThreadPoster 来处理,也就是必然会走一遍 Handler 的消息分发。

当事件需要在主线程中处理的时候,要求不能执行耗时操作,这没什么好说的,另外对于 MAIN 或者 MAIN_ORDERED 的选择,就看具体的业务要求了。

2.3 切换至子线程执行

想要让消息在子线程中处理,可以配置 threadMode 为 BACKGROUND 或者 AYSNC,他们都可以实现,但是也有一些区别。

先来看看 BACKGROUND,通过 postToSubscription() 中的逻辑可以看到,BACKGROUND 会区分当前发生事件的线程,是否是主线程,非主线程这直接分发事件,如果是主线程,则 backgroundPoster 来分发事件。

case BACKGROUND:

if (isMainThread) {

backgroundPoster.enqueue(subscription, event);

} else {

invokeSubscriber(subscription, event);

}

break;

BackgroundPoster 也实现了 Poster 接口,其中也维护了一个用链表实现的消息队列 PendingPostQueue,

在一些编码规范里就提到,不要直接创建线程,而是需要使用线程池。EventBus 也遵循这个规范,在 BackgroundPoster 中,就使用了 EventBus 的 executorService 线程池对象去执行。

为了提高效率,EventBus 在处理 BackgroundPoster 时,也有一些小技巧值得我们学习。

标签:面试官,主线,吊打,线程,EventBus,MAIN,event,subscription
来源: https://blog.csdn.net/m0_64384202/article/details/122125053

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有