热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

每天学习一个Android中的常用框架——14.EventBus

文章目录1.简介2.特性3.演示3.1集成3.2基本功能3.2.1声明事件类型——Event3.2.2声明订阅者——Subscriber3.2.3声明发布者——Publisher3

文章目录

  • 1.简介
  • 2.特性
  • 3.演示
    • 3.1 集成
    • 3.2 基本功能
      • 3.2.1 声明事件类型——Event
      • 3.2.2 声明订阅者——Subscriber
      • 3.2.3 声明发布者——Publisher
      • 3.2.4 总结
    • 3.3 拓展功能
      • 3.3.1 线程模型
      • 3.3.2 黏性事件
      • 3.3.3 优先级
  • 4.源码地址


1.简介

EventBus,作为我学习的继Android官方推出的Handler和AysncTask之后的第三个消息机制框架,在很多项目的开发中都能听说过这个框架,可见该框架的热门程度。作为一个相当有人气的框架,自然成为了需要学习的对象。

EventBus是一种用于Android的事件发布——订阅总线,由GreenRobot开发,Gihub地址是:EventBus。它简化了应用程序内各个组件之间进行通信的复杂度,尤其是碎片之间进行通信的问题,可以避免由于使用广播通信而带来的诸多不便。

关于 EventBus在开发中经常会选择使用它来进行模块间通信、解耦。平常使用这个库只是很浅显的操作三部曲:register,post,unregister,来达到基础的开发目的。因此这篇博客中除了演示EventBus的基本功能之外,还会再进阶地讲解一些其拓展功能,包括:线程模型、黏性事件、优先级,让读者可以更好地了解到EventBus的具体功能。

在演示EventBus之前,我们先在下一节简单介绍一下EventBus的特性,即发布——订阅模型。

2.特性

EventBus是一个开源库,它利用发布——订阅模式来对项目进行解耦。它可以利用很少的代码,来实现多组件间通信。Android的组件间通信,我们不由得会想到Handler消息机制和广播机制等方式,通过它们也可以进行通信。但是使用它们进行通信,代码量多,组件间容易产生耦合引用。关于EventBus的工作模式,这里引用一张官方图帮助理解:
在这里插入图片描述
这里按照从左到右的顺序,简单介绍一下图中出现的三个方框(角色)所代表的含义:

  • Publisher:事件的发布者,可以在任意线程里发布事件。一般情况下,使用EventBus.getDefault()就可以得到一个EventBus对象,然后再调用post(Object)方法发送消息(Event)即可。
  • Event:事件,它可以是任意类型,EventBus会根据事件类型进行全局的通知。在EventBus中,我们可以自定义消息。
  • Subscriber:事件的订阅者,在EventBus 3.0之前我们必须定义以onEvent开头的那几个方法,分别是onEventonEventMainThreadonEventBackgroundThreadonEventAsync,而在3.0之后事件处理的方法名可以随意取,不过需要加上注解@Subscribe,并且指定线程模型,默认是POSTING

从图中可以看出,Publisher(发布者)通过post()方法,把Event(事件)发布出去。之后Subscriber(订阅者)在onEvent()方法(方法名可以自定义)中接收事件,就完成了一次消息传递的过程。当然,在提及Subscriber时,还提到了关于线程模型的概念,这里我们后面再详细说明,接下来会首先演示一下EventBus的基本使用方法。


如果不能理解发布——订阅模型的读者,可以通俗地把这个模型理解成类似于小说网站中订阅小说的场景:

一般作者(Publisher,发布者)在推送小说中的最新章节(Event,事件)时,订阅了该小说的读者(Subscriber,订阅者)就会接收到这条消息(即“小说的最新章节更新了”)。这种模型的好处就是:作者可以随时随地将新章节推送出去(即发送消息),而订阅了小说的读者不需要每分每秒都去关注作者,只需要准时了解到小说的最新章节已经发布的消息(即接收消息),然后再去看小说即可。

发布——订阅模型的大概意思就是如上面的例子所说,如果还是不能理解的话,可以参看CSDN上详细讲解了该模型的博文,这里就不再赘述了。


最后,我们再看看使用EventBus的几条理由:

  • 简化了组件间交流的方式
  • 对事件通信双方进行解耦
  • 可以灵活方便的指定工作线程,通过ThreadMode
  • 速度快,性能好
  • 库比较小,不占内存
  • 使用这个库的app多,有权威性
  • 功能多,使用方便

现在,我们就来体验EventBus的强大之处吧。

3.演示

3.1 集成

老样子,在我们使用某个框架之前,都是需要先集成的。我们去EventBus的GItHub官网上获取最新的依赖,然后修改moudle的build.gradle,代码如下:

implementation 'org.greenrobot:eventbus:3.2.0'

3.2 基本功能


3.2.1 声明事件类型——Event

想要使用EventBus进行消息传递,首先需要创建一个自定义的消息事件类,消息事件类型可以是String,int等常见类,也可以是自己自定义一个事件类,方便管理。为了更好地演示,这里自定义了一个名为EventMessage的消息事件类。事件类的编写很简单,基本上跟常见的Java Bean结构相同,代码如下:

package com.androidframelearn.event_eventbus;public class EventMessage {private String type;private String message;public EventMessage() {}public EventMessage(String type, String message) {this.type = type;this.message = message;}@Overridepublic String toString() {return "消息类型为:" + type + "===消息为:" + message;}public String getType() {return type;}public void setType(String type) {this.type = type;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}
}

3.2.2 声明订阅者——Subscriber

创建好消息事件后,接下来就是要声明订阅者。为了便于演示,这里将订阅者设定到一个名为SubscriberActivity的Activity中。在该Activity中,我们首先要注册EventBus,标示这是一个被EventBus管理的订阅者。接着,我们编写onMessageEvent()方法,作为接受事件消息的回调。最后,我们再设定一个按钮,让这个按钮跳转到下一节将要编写的PublisherActivity中,我们将在那个Activity中发布消息。SubscriberActivity的Java代码和布局文件代码如下:

package com.androidframelearn.event_eventbus;import androidx.appcompat.app.AppCompatActivity;import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;public class SubscriberActivity extends AppCompatActivity {private static final String TAG = "SubscriberActivity";private Button btn_next_activity;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_subscriber);// 初始化UIinitUI();// 注册点击事件btn_next_activity.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent(SubscriberActivity.this,PublisherActivity.class);startActivity(intent);}});}/*** 初始化UI*/private void initUI() {btn_next_activity = findViewById(R.id.btn_next_activity);}@Overrideprotected void onStart() {super.onStart();// 订阅者——Subscriber需要绑定EventBusEventBus.getDefault().register(this);}// @Override
// protected void onStop() {
// super.onStop();
// // 订阅者——Subscriber需要解绑EventBus
// EventBus.getDefault().unregister(this);
// }@Overrideprotected void onDestroy() {super.onDestroy();// 订阅者——Subscriber需要解绑EventBusEventBus.getDefault().unregister(this);}@Subscribe(threadMode = ThreadMode.MAIN)public void onMessageEvent(EventMessage message) {Log.i(TAG,"收到消息啦!" + "消息的类型为:" + message.getType() + "消息的内容为:" + message.getMessage());};
}


注意:在EventBus的官方实例中,注册和注销EventBus的过程(即EventBus.getDefault().register()EventBus.getDefault().unregister())推荐是在onStart()onStop()方法中的。这里为了演示,将注销EventBus的过程放在了onDestroy()中。实际上,这两个步骤是可以根据需求进行改动的。

除此之外,EventBus还有两个问题需要留意一下:

  1. EventBus注册完了,在不用的时候千万别忘了注销;
  2. EventBus不能重复注册。即在注册了一次之后,没有注销的情况下,又注册了一次。

另外:在类中我们用@Subscribe声明了onMessageEvent()这句接受事件消息的方法。在创建这个方法时,同样需要注意几个问题:

  1. 该方法有且只有一个参数。
  2. 该方法必须是public修饰符修饰,不能用static关键字修饰,不能是抽象的(abstract)
  3. 该方法需要用@Subscribe注解进行修饰。

关于@Subscribe注解的详细介绍,我们放在拓展功能再介绍,现在就按照这样写即可。



<LinearLayout xmlns:android&#61;"http://schemas.android.com/apk/res/android"xmlns:app&#61;"http://schemas.android.com/apk/res-auto"xmlns:tools&#61;"http://schemas.android.com/tools"android:layout_width&#61;"match_parent"android:layout_height&#61;"match_parent"tools:context&#61;".SubscriberActivity"><Buttonandroid:id&#61;"&#64;&#43;id/btn_next_activity"android:layout_width&#61;"match_parent"android:layout_height&#61;"wrap_content"android:text&#61;"跳转到发布者Activity"/>LinearLayout>

3.2.3 声明发布者——Publisher

创建好消息的订阅者后&#xff0c;接下来就是要声明发布者。为了便于演示&#xff0c;这里将订阅者设定到一个名为PublisherActivity的Activity中。在该Activity中&#xff0c;我们同样设置一个按钮&#xff0c;然后在按钮的点击事件中调用EventBus的post(Object event)方法来发送消息&#xff0c;这样就可以在日志中看到SubscriberActivity接收到了该条消息&#xff0c;PublisherActivity的Java代码和布局文件代码如下&#xff1a;

package com.androidframelearn.event_eventbus;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;
import android.view.View;
import android.widget.Button;import org.greenrobot.eventbus.EventBus;public class PublisherActivity extends AppCompatActivity {private Button btn_send_message;&#64;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_publisher);// 初始化UIinitUI();// 为按钮注册点击事件&#xff0c;发送消息btn_send_message.setOnClickListener(new View.OnClickListener() {&#64;Overridepublic void onClick(View v) {EventMessage message &#61; new EventMessage("测试消息","你好&#xff01;");EventBus.getDefault().post(message);}});}/*** 初始化控件*/private void initUI() {btn_send_message &#61; findViewById(R.id.btn_send_message);}
}


<LinearLayout xmlns:android&#61;"http://schemas.android.com/apk/res/android"xmlns:tools&#61;"http://schemas.android.com/tools"android:layout_width&#61;"match_parent"android:layout_height&#61;"match_parent"tools:context&#61;".PublisherActivity"android:orientation&#61;"vertical"><Buttonandroid:id&#61;"&#64;&#43;id/btn_send_message"android:layout_width&#61;"match_parent"android:layout_height&#61;"wrap_content"android:text&#61;"发送消息"/>LinearLayout>

当以上的三个步骤都完成之后&#xff0c;我们首先从SubscriberActivity点击按钮进入PublisherActivity&#xff0c;然后在PublisherActivity中点击按钮后&#xff0c;可以看到SubscriberActivity确实收到了消息&#xff0c;并且打印了日志&#xff0c;如图所示&#xff1a;
在这里插入图片描述

3.2.4 总结

完成了EventBus基础功能的演示后&#xff0c;我们在这一小节里简单地总结一下EventBus传递消息的整个过程&#xff1a;

  1. 首先需要自定义一个类似于Java Bean一样的消息事件类&#xff08;Event&#xff09;&#xff0c;当然也可以直接用Stringint等常见的数据类型&#xff1b;
  2. 接下来就需要编写消息订阅者&#xff08;Subscriber&#xff09;&#xff0c;在订阅者中注册/注销EventBus&#xff0c;然后编写用&#64;Subscribe声明的接收消息的订阅方法&#xff1b;
  3. 最后编写消息发布者&#xff08;Publisher&#xff09;&#xff0c;调用EventBus的post(Object event)即可将消息发送出去。


注意&#xff1a;该该例子中&#xff0c;我使用了EventBus.getDefault()方法来获取EventBus的对象实例。实际上&#xff0c;该方法会获取一个单例&#xff0c;所以才可以随时使用。如果不是用这种单例模式&#xff0c;就需要想办法把订阅者&#xff08;Subscriber&#xff09;注册时用的EventBus的引用传给需要发送事件的模块中。简而言之&#xff0c;就是Subscriber用的EventBus和post方法需要的EventBus需要是同一个EventBus&#xff08;即一个对象&#xff09;。

换句话来说&#xff0c;EventBus也是支持跨模块的。


通过这三步&#xff0c;我们就完成了EventBus所提供的消息传递流程。当然&#xff0c;EventBus的功能不仅局限于这些&#xff0c;下面还会介绍一些它的拓展功能。

3.3 拓展功能

上面我们在编写订阅者中接收消息事件的方法时&#xff0c;提到过需要特别加上&#64;Subscribe注解来声明方法。在这一节中&#xff0c;我们将要说明&#64;Subscribe注解的一些其他用法。

&#64;Subscribe是EventBus自定义的一种注解&#xff0c;它总共可以接收三个参数。ThreadModeboolean stickyint priority

所以上面的接收Event方法的代码&#xff0c;完整版的可以这样写&#xff1a;

&#64;Subscribe(threadMode &#61; ThreadMode.MAIN,sticky &#61; true,priority &#61; 1)
public void onMessageEvent(EventMessage message) {Log.i(TAG,"收到消息啦&#xff01;" &#43; "消息的类型为&#xff1a;" &#43; message.getType() &#43; "消息的内容为&#xff1a;" &#43; message.getMessage());};

这三个参数代表对&#64;Subscribe注解的方法中的一些配置&#xff0c;可以根据需要选择是否使用。

接下来&#xff0c;就专门介绍这三个参数的用法。

3.3.1 线程模型

&#64;Subscribe的第一个参数——threadMode&#xff0c;顾名思义&#xff0c;代表着线程模型的意思。在EventBus中&#xff0c;定义了总共5种线程模型&#xff08;原先看博文时只提到了4种&#xff0c;可能是新版本的缘故又出现了第5种线程模型&#xff0c;即MAIN_ORDERED&#xff09;&#xff0c;这5种线程模型如图所示&#xff1a;
在这里插入图片描述
EventBus为什么要定义线程模型&#xff1f;主要还是出于实际应用场景考虑。线程模型约束了post()&#xff08;发布事件方法&#xff0c;Publisher&#xff09;和onMessageEvent()&#xff08;接收事件方法&#xff0c;Subscriber &#xff09;分别在不同线程下的情况&#xff0c;以满足生产上的需要。

首先&#xff0c;我们先看看EventBus提供的五种线程模型&#xff1a;

  1. POSTING&#xff1a;默认&#xff0c;表示事件处理函数的线程跟发布事件的线程在同一个线程&#xff1b;
  2. MAIN&#xff1a;表示事件接收函数的线程在主线程(UI)线程&#xff0c;因此在这里不能进行耗时操作&#xff1b;
  3. MAIN_ORDERED&#xff1a;类似MAIN_ORDERED&#xff0c;但是无论事件发布者在主线程或者是子线程&#xff0c;都不会造成线程阻塞&#xff1b;
  4. BACKGROUND&#xff1a;表示事件处理函数的线程在后台线程&#xff0c;因此不能进行UI操作。如果发布事件的线程是主线程(UI线程)&#xff0c;那么事件处理函数将会开启一个后台线程&#xff0c;如果果发布事件的线程是在后台线程&#xff0c;那么事件处理函数就使用该线程&#xff1b;
  5. ASYNC&#xff1a;表示无论事件发布的线程是哪一个&#xff0c;事件处理函数始终会新建一个子线程运行&#xff0c;同样不能进行UI操作。

最开始看到这5种线程模型的说明时&#xff0c;确实一下子不太好理解。事实上&#xff0c;线程模型就是为了约束当事件发布者处于什么线程时&#xff0c;事件订阅者就处于什么线程。为了便于描述&#xff0c;作者这里绘制了一个表格来总结一下这5种线程模型&#xff0c;它们所处的线程关系可以看作是一一对应的关系&#xff0c;表格如下&#xff1a;

线程模型事件发布者所处线程&#xff08;Publisher&#xff09;事件订阅者所处线程&#xff08;Subscriber&#xff09;
POSTING&#xff08;默认&#xff09;主线程/子线程主线程/子线程
MAIN主线程&#xff08;会阻塞&#xff09;/子线程主线程
MAIN_ORDERED主线程&#xff08;不会阻塞&#xff09;/子线程主线程
BACKGROUND主线程/子线程子线程/跟Publisher一样的子线程
ASYNC主线程/子线程子线程

一般来说&#xff0c;使用POSTING模式适合使用在执行简单任务的情况下&#xff0c;不需要复杂运算&#xff0c;因为这种模式不需要做线程切换的判断逻辑&#xff0c;直接分发至相同的线程环境&#xff0c;速度快&#xff0c;耗时少。我们使用的最多的&#xff0c;也是这个模式。

3.3.2 黏性事件

&#64;Subscribe的第二个参数——sticky&#xff0c;是一个boolean类型的参数&#xff0c;默认值是false&#xff0c;表示不启用黏性特性&#xff0c;在这里也代表着黏性事件的意思。该特性代表什么意思呢&#xff1f;我们之前举的EventBus事件传递的例子时&#xff0c;都是先对订阅者&#xff08;Subscriber&#xff09;进行注册&#xff0c;然后再post事件的&#xff08;顺序是&#xff1a;1.声明消息事件——>2.声明订阅者——>3.声明发布者&#xff09;。那黏性事件的意义则是最后两步反过来&#xff1a;先post事件&#xff0c;后对订阅者注册&#xff08;顺序是&#xff1a;1.声明消息事件——>2.声明发布者——>3.声明订阅者&#xff09;。

沿用上面的代码&#xff0c;可以举一个简单的例子。这里想要使用黏性事件需要修改两处&#xff1a;

  1. 修改接收消息的方法&#xff0c;在&#64;Subscribe上添加参数sticky &#61; true&#xff0c;表示开启黏性事件&#xff0c;例如&#64;Subscribe(threadMode &#61; ThreadMode.MAIN,sticky &#61; true)&#xff1b;
  2. 修改发布事件的方法&#xff0c;使用postSticky()来替代post()&#xff0c;表示发出的是黏性事件。

代码如下&#xff1a;

private View.OnClickListener mGoListener &#61; new View.OnClickListener() {&#64;Overridepublic void onClick(View v) {Log.e(TAG, "onClick: post");EventMessage message &#61; new EventMessage("测试黏性消息","你好&#xff01;");EventBus.getDefault().postSticky(message);}};private View.OnClickListener mRegisterListener &#61; new View.OnClickListener() {&#64;Overridepublic void onClick(View v) {Log.e(TAG, "onClick: start register" );EventBus.getDefault().register(SubscriberActivity.this);}};&#64;Subscribe(threadMode &#61; ThreadMode.MAIN,sticky &#61; true)public void onMessageEvent(EventMessage message) {Log.i(TAG,"收到消息啦&#xff01;" &#43; "消息的类型为&#xff1a;" &#43; message.getType() &#43; "消息的内容为&#xff1a;" &#43; message.getMessage());};

这里我们先执行mGoListener绑定的点击事件&#xff0c;将消息发布出去&#xff0c;然后再执行mRegisterListener绑定的点击事件&#xff0c;注册EventBus。接下来可以查看日志信息&#xff0c;就可以发现代码成功运行了。

3.3.3 优先级

&#64;Subscribe的第三个参数——priority&#xff0c;是一个int类型的参数&#xff0c;默认值是0。这个参数比较好理解&#xff0c;就是指定订阅方法的优先级&#xff0c;值越大表示优先级越大。在某个事件被发布出来的时候&#xff0c;优先级较高的订阅方法会首先接收到事件。

这里有两个地方需要注意&#xff1a;

  1. 只有当两个订阅方法使用相同的ThreadMode参数的时候&#xff0c;它们的优先级才会与priority指定的值一致&#xff1b;
  2. 只有当某个订阅方法的ThreadMode参数为POSTING的时候&#xff0c;它才能停止该事件的继续分发。

4.源码地址

AFL——Android框架学习


推荐阅读
author-avatar
那时候的我和你_173
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有