【Andorid】EventBus浅探

初始部分

EventBus是适用于Android和Java的发布/订阅事件总线。主要功能是替代Intent、Handler、BroadCast在Activity、Fragment、Service线程之间传递消息。EventBus能够简化应用组件间的通信,解耦(有效分离)事件的发送者和接收者,避免复杂和容易出错的依赖和生命周期问题,开销小,代码更优雅。

Andorid组件间通信,可能都是用Handler消息机制或者广播机制来实现通信,但是它们代码量大,组件上容易产生耦合 。为什么选择使用EventBus来做通信

我们来看看EventBus的优点:

  • 1、简化了组件之间的通信;
  • 2、对事件通信双方进行解耦 ;
  • 3、在Activity、Fragment和后台线程中能很好使用;
  • 4、避免了复杂且容易出错的依赖性和生命周期问题 ;
  • 5、可以灵活方便指定工作线程和优先级 ;
  • 6、速度快,性能好,代码简单优雅;
  • 7、库比较小,不占内存。

要素与ThreadMode

发布者通过EventBus发布事件,订阅者通过EventBus订阅事件,当发布者发送事件时,订阅该事件的订阅者的事件处理方法将被调用。从图中看出,发布者发送一个事件时,则该事件将会同时传递给一个或多个该事件的订阅者。

什么是发布/订阅者模式?

订阅者(Subscriber)把自己想订阅的事件(Event)注册(register)到调度中心(EventBus),当发布者(Publisher)发布该事件到调度中心时,也就是该事件触发时,由调度中心统一调度订阅者注册到调度中心的处理代码(onEvent())。(发布/订阅者模式与观察者模式并不完全一样)

EventBus的三要素

  • **Event:**事件, 可以使任意类型;
  • **Publisher:**事件发布者,可以在任意线程中发布事件,一般情况,通过EventBus.getDefault()获取EventBus实例,通过post(Object event)发布事件;
  • **Subscriber:**事件订阅者,在EventBus3.0之前,订阅者的事件处理方法必须定义以onEvent开头的具体方法名,而在3.0之后方法名可以可以随意取,但是必须加上@Subscribe()注解,并且指定线程模式,默认为ThreadMode.POSTING

基本概念

  • 事件(Event):是一种在应用程序中发生的特定动作或情况的通知,例如用户点击按钮、数据加载完成、系统配置改变等。
  • 发布者(Publisher):也称为事件生产者,是产生事件的对象或模块。当特定的事件发生时,发布者会将该事件发布到 EventBus 上。
  • 订阅者(Subscriber):也称为事件消费者,是对特定事件感兴趣并希望在事件发生时得到通知的对象或模块。订阅者会向 EventBus 订阅感兴趣的事件类型。
  • EventBus:作为事件的中心枢纽,负责管理事件的发布和订阅,它提供了一种解耦的方式,使得发布者和订阅者之间不需要直接相互了解,从而降低了代码的耦合度,提高了系统的可维护性和可扩展性。

在这里插入图片描述

工作原理

  1. 订阅:订阅者通过向 EventBus 注册来表明它们对特定类型事件的兴趣。在注册时,订阅者通常会提供一个回调函数或方法,该函数将在相应事件发布时被调用。
  2. 发布:当事件发生时,发布者创建一个事件对象,并将其发布到 EventBus。EventBus 会查找所有已注册的订阅者,并调用它们为该事件类型注册的回调函数,将事件对象作为参数传递给回调函数。
  3. 处理事件:订阅者在接收到事件通知后,通过执行相应的回调函数来处理事件。在回调函数中,订阅者可以根据事件对象中包含的信息执行相应的操作,例如更新 UI、执行数据处理、触发其他业务逻辑等。

优点

  • 解耦:通过将事件的发布和订阅分离,使得不同的组件之间的依赖关系更加松散,降低了代码的耦合度。
  • 可扩展性:新的订阅者可以轻松地添加到系统中,而无需修改现有的发布者代码。同样,新的事件类型也可以方便地引入,而不会影响已有的订阅者。
  • 易于维护:由于各组件之间的交互通过事件进行,代码的流向更加清晰,易于理解和维护。当出现问题时,更容易定位和修改问题所在。
  • 提高响应性:事件的发布和处理是异步的,这意味着发布者不需要等待订阅者处理完事件就可以继续执行其他操作,从而提高了系统的响应性和性能。

应用场景

  • 用户界面更新:在图形用户界面应用程序中,当数据发生变化时,可以通过 EventBus 发布事件,通知相关的 UI 组件进行更新,从而实现数据与视图的分离。
  • 多模块通信:在大型的软件系统中,不同的模块之间可能需要进行通信和协作。EventBus 提供了一种简单而有效的方式来实现模块之间的解耦通信,使得各个模块可以独立地开发和演进。
  • 系统事件处理:例如系统配置的更改、网络状态的变化、应用程序的生命周期事件等,都可以通过 EventBus 进行统一的管理和处理,使得不同的组件能够及时响应系统的变化。
  • 异步任务处理:当执行一些耗时的异步任务时,任务完成后可以通过 EventBus 发布事件,通知其他组件任务的结果,以便进行后续的处理。

ThreadMode是一个重要的概念,它用于指定订阅者接收事件的线程模式。不同的ThreadMode决定了事件处理函数将在何种线程中被调用,这对于确保应用程序的线程安全和响应性非常关键。以下是对ThreadMode的详细介绍:

常见的 ThreadMode 类型

线程模式含义特点
1、POSTING默认线程,表示订阅者将在发布事件的同一线程中被调用适用于不需要主线程就可以在短时间内完成的简单任务,避免了线程切换,开销更小
2、MAIN表示在Android中,订阅者在Android主线程中被调用如果发布事件的线程是主线程,订阅者的事件处理函数将直接被调用。如果发布事件的线程不是主线程,则将事件加入主线程队列中,排队等待执行;因此这里不能进行耗时操作,注意不能阻塞主线程
3、MAIN_ORDERED表示在Android中,订阅者在Android主线程中被调用与MAIN不同的是,无论发布事件的线程是在哪个线程,事件都将发送到主线程队列总是排队等待传递。注意不能阻塞主线程
4、BACKGROUND表示在Android中,订阅者在后台线程中被调用,在Android中,如果发布线程不是主线程,订阅者的事件处理函数直接使用该线程,如果发布线程是主线程,那么事件处理函数会开启一个后台线程,有序分发事件,注意不能阻塞后台线程,这里不能进行UI操作;如果不是在Android上,总是使用一个后台线程
5、ASYNC表示无论发布线程是什么线程,订阅者都会创建一个新的子线程执行使用于耗时操作,尽量避免同时触发大量的耗时较长的异步操作,EventBus使用线程池高效的复用已经完成异步操作的线程
  • POSTING:(默认)
    • 含义:事件处理函数将在发布事件的同一线程中被调用。
    • 特点:这是最简单和最直接的模式,适用于事件处理逻辑简单且不涉及耗时操作的情况。如果发布事件的线程是主线程,那么事件处理函数也会在主线程中执行,可能会阻塞主线程,影响用户界面的响应性。
    • 这个事件在哪发出,事件处理函数就会在哪运行,也就是说发送线程和接受线程在同一个事件当中(意味着最少的开销,因为它完全避免了线程切换。)。 注意:避免耗时操作,因为他会阻塞事件的传递导致ANR
  • MAIN:
    • 含义:事件处理函数将在主线程中被调用,无论事件是在哪个线程发布的。
    • 特点:此模式主要用于需要更新用户界面的情况,因为只有主线程才能安全地操作 UI 组件。如果事件处理函数执行时间较长,可能会导致主线程阻塞,出现界面卡顿现象。
    • 处理事件会在Ui线程中执行,所以需要避免线程处理时间过长,如果发布线程是主线程,将直接调用订阅者方法,阻塞发布线程。否则,事件将排队等待交付(非阻塞)也就是说事件执行顺序不受控制,可能会与它们被发布的顺序不一致。
  • MAIN_ORDERED:
    • 处理事件同样会在 Ui被调用。与 MAIN 不同的是,处理接收该事件是串行的,第二个订阅者需要在第一个订阅者处理完后才会接收到事件,所以被称为 ordered。这个模型同样要避免阻塞主线程。
  • BACKGROUND:
    • 含义:如果事件是在主线程发布的,那么事件处理函数将被放入一个后台线程队列中,按顺序依次执行;如果事件是在后台线程发布的,那么事件处理函数将直接在该后台线程中执行。
    • 特点:该模式适用于处理一些不需要在主线程执行,但又不希望阻塞发布事件线程的操作,如数据的保存、网络请求等。
    • 处理事件会在后台线程执行, 如果发布事件是Ui线程那么处理事件会新开一个线程,如果发布事件是子线程那么处理事件还是在该子线程。注意:在处理函数禁止Ui操作
  • ASYNC:
    • 含义:事件处理函数将在一个单独的线程中异步执行,与发布事件的线程无关。
    • 特点:此模式适用于处理耗时较长且不依赖于发布事件线程的操作,如复杂的数据处理、大量数据的加载等。由于是异步执行,不会阻塞发布事件的线程,但需要注意线程安全问题,避免多个线程同时访问共享资源导致数据不一致。
    • 异步操作,无论事件在哪个线程发布,该事件的处理函数都是会在新的子线程执行;同样在处理函数禁止Ui操作。

EventBus的基本用法

首先导入依赖:

https://github.com/greenrobot/EventBus

implementation "org.greenrobot:eventbus:3.3.1"

官方介绍中:

EventBus 只需 3 步

  1. 定义事件:
public static class MessageEvent { /* Additional fields if needed */ }
  1. 准备订阅者:声明并注释您的订阅方法,可以选择指定线程模式
@Subscribe(threadMode = ThreadMode.MAIN)  
public void onMessageEvent(MessageEvent event) {
    // Do something
}

注册和取消注册您的订户。例如在 Android 上,活动和片段通常应根据其生命周期进行注册:

 @Override
 public void onStart() {
     super.onStart();
     EventBus.getDefault().register(this);
 }

 @Override
 public void onStop() {
     super.onStop();
     EventBus.getDefault().unregister(this);
 }
  1. 发布活动:
 EventBus.getDefault().post(new MessageEvent());

普通使用的小栗子

(1)定义事件对象

事件对象可以是任意java类型,没有特殊要求,比如String、int、自定义类等。

public class MessageEvent {    
    public String name;
}

(2)在接收消息的页面注册事件

EventBus.getDefault().register(this);
  • register(Object subscriber):EventBus订阅事件的方法,通过EventBus.getDefault()获取事件总线实例;参数subscriber为订阅者,订阅者有处理事件的方法,并且必须添加@Subscribe注解。

只有注册了订阅事件,才会接收到消息。注意:通常根据Activity和Fragment的生命周期注册和注销事件。

(3)订阅者实现事件处理方法

也称为"订阅者方法",当发布对应事件类型时,该方法被调用(在接收消息的页面)。

@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent message){    
    //TODO 接收事件后Do something
}
  • @Subscribe:必须使用@Subscribe注解来定义订阅者方法,否则事件处理方法无法生效。
  • **threadMode:**线程模式,表示在哪个线程里面执行,ThreadMode.MAIN表示在主线程执行该方法。(其他模式在下一篇讲解)
  • **onMessageEvent(MessageEvent message):**事件处理方法的方法名称,onMessageEvent()是任意的合法方法名,开发者可以自己定义;参数类型MessageEvent为定义接收事件的对象类型,要与发布事件的类型一致,否则无法接收事件。

(4)发布事件

EventBus.getDefault().post(Object event);
  • **post(Object event):**EventBus发送事件的方法,参数event为事件对象,是Object任意类型,这里发送的类型需要与接收事件的类型一致。

当前与事件类型匹配的所有已注册的事件都会接收到。

(5)在接收消息的页面注销(解除注册)事件

EventBus.getDefault().unregister(this);
  • unregister(Object subscriber): 给订阅者注销事件的方法,如果事件不需要使用了必须调用该方法注销事件。

当消息页面不存在或者不需要事件了注销该事件。

粘性事件的使用

普通事件中,订阅者要先注册,才能接收到发布的事件;也就是说接收消息的页面还没创建或者未注册订阅者,那么处理事件的方法根本无法接收发送者发布的事件。

EventBus提供了一种粘性事件,能在发送事件之后再订阅该事件也能接收到该事件。与普通事件不同,普通事件是先注册后发布,粘性事件可以先发布后注册

粘性事件的使用步骤和普通事件的使用步骤大致相同,不同的是发布和处理事件的方法:

(1)粘性事件的事件函数处理方法,需要在注解中添加**sticky = true**标识,表示该事件是粘性事件:

@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onStickyEvent(MessageEvent message) {    
    //TODO 接收事件后
    Do something
}

(2)使用**postSticky()**发布粘性事件:

EventBus.getDefault().postSticky(Object event);

发布粘性事件后,EventBus将会一直存在粘性事件,在不需要粘性事件的时候需要及时移除,移除的方法有下面几个:

//移除指定的粘性事件
removeStickyEvent(Object event);
//移除指定对象类型的粘性事件
removeStickyEvent(Class<T> eventType);
//移除所有粘性事件
removeAllStickyEvents();

我们举个栗子,创建两个Activity,

在StickySendActivity中向StickyReceiveActivity发布粘性事件,发布完后跳转到StickyReceiveActivity中,StickyReceiveActivity创建并注册EventBus

订阅粘性事件的处理方法:

/** 发布粘性事件 */
public class StickySendActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sticky_send);
        findViewById(R.id.btn_send).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_send: // 发布事件
                // 4.发布粘性事件
                EventBus.getDefault().postSticky("StickySendActivity发送粘性事件");
                startActivity(new Intent(this, StickyReceiveActivity.class));
                break;
        }
    }
}

点击发送粘性事件按钮向StickyReceiveActivity发布粘性事件,发布完后跳转到StickyReceiveActivity中;

/** 注册并接收粘性事件 */
public class StickyReceiveActivity extends AppCompatActivity {
    private TextView mTv_content;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sticky_receive);
        mTv_content = findViewById(R.id.tv_content);
        // 1. 注册事件
        EventBus.getDefault().register(this);
    }

    // 3. 接收StickySendActivity粘性事件处理, sticky = true表示是粘性事件
    @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
    public void onStickyEvent(String str) {
        // TODO 接收事件后Do something
        mTv_content.setText("onStickyEvent:接收到" + str);
        Toast.makeText(this, "onStickyEvent:接收到" + str, Toast.LENGTH_SHORT).show();
        EventBus.getDefault().removeStickyEvent(this); // 移除粘性事件
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 2. 反注册事件
        EventBus.getDefault().unregister(this);
    }
}

StickyReceiveActivity创建并注册EventBus,订阅粘性事件的处理方法,接收到粘性事件发送的数据,显示在mTv_content控件上并吐司提示,然后通过removeStickyEvent(this)移除当前的粘性事件。

注意:发送事件的参数类型一定要与接收事件的参数类型一致,否则无法接收到事件的。

测试一下evenbus的threadmode类型:

几种线程模式使用

首先来验证一下不同的线程模式处理事件在哪个线程,创建两个Activity,ReceiveActivity中注册EventBus并且实现订阅者事件处理函数,MainActivity负责发布事件。

ReceiveActivity:

作为接受消息的页面,是订阅者,首先在在onCreate()注册EventBus,onDestroy()中反注册,并且实现几种线程模式的订阅者的事件处理函数,将接收数据的线程通过Thread.currentThread().getName()线程名字打印log,点击按钮跳转到ModeSendActivity发布者中在主线程发布事件。

package com.example.eventbustest.EVENTBUS;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

import com.example.eventbustest.MainActivity;
import com.example.eventbustest.R;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

/**
 * 几种线程模式
 * 注册并接收事件
 */
public class ReceiveActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "ReceiveActivity";
    private TextView mTv_content;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_receive);

        findViewById(R.id.button_r).setOnClickListener(this);
        mTv_content = findViewById(R.id.textView_r);
        //1.注册事件
        EventBus.getDefault().register(this);
        Log.e("ReceiveActivity", "onMessageEventSent:oncreate");
    }

    //3.订阅者的接收事件处理函数

    //处理函数执行线程与发布线程一致
    @Subscribe(threadMode = ThreadMode.POSTING)
    public void onMessageEventPosting(String str) {
        //TODO 接收事件后Do something
        mTv_content.setText("onMessageEvent:" + str);
        Toast.makeText(this, "onMessageEvent:" + str, Toast.LENGTH_SHORT).show();
        Log.e(TAG, "onMessageEventPosting:" + Thread.currentThread().getName());
    }

    //处理函数执行线程为主线程
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEventMain(String str) {
        Log.e(TAG, "onMessageEventMain:" + Thread.currentThread().getName());
    }

    //处理函数执行线程为主线程
    @Subscribe(threadMode = ThreadMode.MAIN_ORDERED)
    public void onMessageEventMainOrdered(String str) {
        Log.e(TAG, "onMessageEventMainOrdered:" + Thread.currentThread().getName());
    }

    //处理函数执行线程为后台线程
    @Subscribe(threadMode = ThreadMode.BACKGROUND)
    public void onMessageEventBackground(String str) {
        Log.e(TAG, "onMessageEventBackground:" + Thread.currentThread().getName());
    }

    //处理函数执行线程为后台线程
    @Subscribe(threadMode = ThreadMode.ASYNC)
    public void onMessageEventAsync(String str) {
        Log.e(TAG, "onMessageEventAsync:" + Thread.currentThread().getName());
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.button_r) {
            startActivity(new Intent(this, MainActivity.class));
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.e("ReceiveActivity", "onMessageEventSent:onDestroy");
        //2.反注册事件
        EventBus.getDefault().unregister(this);
    }
}

MainActivity:点击发布事件按钮ReceiveActivity发送事件,这里发布的线程是主线程

package com.example.eventbustest;

import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

import org.greenrobot.eventbus.EventBus;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private TextView mTv_content;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.button).setOnClickListener(this);
        mTv_content = findViewById(R.id.textView);
    }
    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.button) {
            mTv_content.setText("对ReceiveActivity发布事件");
            EventBus.getDefault().post("接收到MainActivity发送过来的事件啦");
        }
    }
}

这里记得改一下默认启动的活动

这里可以看到,toast了一下信息;

打印数据如下:

在这里插入图片描述

可以看到,POSTING模式的线程与发布线程一致,都是主线程;MAINMAIN_ORDERED模式的线程为主线程,BACKGROUNDASYNC模式线程为后台线程。

关于注册时机的问题

在这里我看到朋友的代码,在onstart中注册,stop中取消,发现有问题,在上方代码中加入如下内容,并将onCreate中的注册与onDestory中的取消注册去掉:

@Override
protected void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
    Log.e("ReceiveActivity", "onMessageEventSent:onStart");
}
@Override
protected void onStop() {
    super.onStop();
    Log.e("ReceiveActivity", "onMessageEventSent:onStop");
    //2.反注册事件
    EventBus.getDefault().unregister(this);
}

这个时候发现:

接收不到传来的信息了,这是因为在回到原来的activity时这个注册被取消了,更别说我要toast了,log都log不出来啦

故而得出下面的结论:

  • 如果希望组件仅在可见时才处理事件,使用 onStart()onStop() 会更加合适;
  • 如果需要在组件整个生命周期内都接收事件,那么 onCreate()onDestroy() 可能更符合需求。

onStart() 中注册

  • 优点:
    • 及时响应:onStart() 方法在 Activity 或 Fragment 变得可见时被调用,此时注册 EventBus 能够确保组件在可见状态下及时接收到事件并做出响应,避免了过早注册可能导致的一些不必要的事件处理开销,特别是在组件创建后可能有一段时间处于不可见状态的情况下。
    • 与生命周期紧密结合:与 Activity 和 Fragment 的生命周期紧密关联,符合组件可见性与事件处理的逻辑对应关系。当组件对用户可见时,开始接收事件进行相应处理,增强了代码的可读性和可维护性。

onStop() 中取消注册

  • 优点:
    • 资源释放及时:onStop() 方法在 Activity 或 Fragment 不再可见时被调用,此时取消注册 EventBus 可以及时释放与事件接收相关的资源,避免在组件不可见期间仍然接收和处理事件,从而减少不必要的资源占用和潜在的性能开销。
    • 防止内存泄漏:与在 onDestroy() 中取消注册类似,在 onStop() 中取消注册也有助于防止因长时间持有对 EventBus 的引用而导致的内存泄漏问题,特别是当组件在不可见后可能不会立即被销毁的情况下,如 Activity 进入后台暂停状态时,及时取消注册能够更好地管理内存资源。

与其他生命周期方法对比

  • onCreate()onDestroy() 对比:将注册和取消注册放在 onStart()onStop() 中,相比于 onCreate()onDestroy(),更侧重于组件的可见性与事件处理的关联。而 onCreate()onDestroy() 更关注组件的创建和销毁过程中的资源初始化和释放。在某些情况下,
  • 如果希望组件仅在可见时才处理事件,使用 onStart()onStop() 会更加合适;
  • 如果需要在组件整个生命周期内都接收事件,那么 onCreate()onDestroy() 可能更符合需求。
  • onResume()onPause() 对比:onResume()onPause() 主要用于处理与 Activity 前台和后台状态切换相关的逻辑,如暂停或恢复动画、音频播放等。与 onStart()onStop() 相比,它们的调用频率更高,因为每次 Activity 在前台和后台之间切换时都会调用。因此,如果将 EventBus 的注册和取消注册放在 onResume()onPause() 中,可能会导致不必要的频繁注册和取消注册操作,增加系统开销。而 onStart()onStop() 在 Activity 的可见性发生变化时调用,调用频率相对较低,更适合用于 EventBus 的注册和取消注册管理。

将 EventBus 的注册放在 onStart() 方法中,取消注册放在 onStop() 方法中,能够根据组件的可见性合理地管理事件接收,有效地平衡了资源利用和事件处理的及时性,是一种较为合理的使用方式,但具体的选择还应根据实际的业务需求和组件的具体使用场景来确定。

事件的优先级

EventBus支持定义订阅者事件处理方法时指定事件传递的优先级。默认情况下,事****件优先级为0,数值越大,优先级越高,在相同线程模式下,优先级高的比优先级低的先接收到事件。

注意:优先级只有在相同线程模式下才有效

    @Subscribe(threadMode = ThreadMode.MAIN, priority = 0)
    public void onMessageEvent(String str) {
        //TODO 接收事件后Do something
    }
  • priority :事件优先级, int类型,默认为0;数值越大,优先级越高,在相同线程模式下,优先级高的比优先级低的先接收到事件。

我们来修改刚才的Activity检验一下,ReceiveActivity实现订阅者的事件处理方法,不同优先级的多个方法,MainActivity负责发布事件

将刚才测试注册时机问题的代码恢复到原来的,加入三个方法

    //3.订阅者的接收事件处理函数, 事件优先级0,1,2
@Subscribe(threadMode = ThreadMode.MAIN, priority = 0)
public void onMessageEvent(String str) {
    //TODO 接收事件后Do something
    mTv_content.setText("onMessageEvent:" + str);
    Log.e(TAG, "onMessageEvent:priority = 0 " + str);
}

@Subscribe(threadMode = ThreadMode.MAIN, priority = 1)
public void onMessageEvent1(String str) {
    Log.e(TAG, "onMessageEvent:priority = 1 "+str);
}

@Subscribe(threadMode = ThreadMode.MAIN, priority = 2)
public void onMessageEvent2(String str) {
    Log.e(TAG, "onMessageEvent:priority = 2 " + str);
}

结果如下:

在这里插入图片描述

从数据可以看到,优先级priority = 0<1<2,优先级高的比优先级低的先接收到数据。

还有一个特点:

发布者发送一个事件时,则该事件将会同时传递给一个或多个该事件的订阅者。如果发布事件的事件对象类型与订阅者处理事件方法对象类型匹配,那么匹配的所有已注册的事件都会接收到。

AndroidEventBus的使用

我们可以使用AndroidEventBus,EventBus是AndroidEventBus框架的核心类,也是用户的入口类。它存储了用户注册的订阅者信息和方法,事件类型和该事件对应的tag标识一个种类的事件EventType,每一种事件对应有一个或者多个订阅者,订阅者中的订阅函数通过@Subscriber注解来标识tag和线程模型,这样使得用户体检较为友好,代码也更加整洁。

AndroidEventBus与EventBus不同的是,多了一种该事件对应的tag标识和订阅者中的订阅函数通过@Subscriber注解(EventBus的是@Subscribe)。

// 发布者发布事件,tag为from_three
EventBus.getDefault().post(new MessageEvent("接收到AndroidBusSendActivity发送过来的事件啦"), "from_three");
// 订阅者的事件处理函数,有tag为from_three
@Subscribe(tag = "from_three", threadMode = ThreadMode.MAIN)
public void eventAndroidEventBus(MessageEvent message) {
    // 事件处理逻辑
}

post(Object event, String tag**) 方法**

  • 用途:发布者发布事件的方法。
  • 参数
    • event:要发布的事件,可以是任意类型。
    • tag:事件的标识,用于区分其他事件,类型为 String(区分大小写)。

@Subscriber 注解

  • 用途:必须使用 @Subscriber 注解来定义订阅者方法,否则事件处理方法无法生效。与 EventBus 的 @Subscribe 类同。
  • 参数
    • tag:表示事件的标识,用于区分其他事件。与发布事件的 tag 相对应。
    • mode:线程模式,表示在哪个线程里面执行。与 EventBus 的 threadMode 类同。

AndroidEventBus 普通事件

AndroidEventBus 与 EventBus 的用法大致相同。以下是如何使用 AndroidEventBus 来处理普通事件的步骤:

  1. 添加依赖:在 build.gradle 文件中添加依赖。

  2. gradle implementation 'org.simple:androideventbus:1.0.5.1'

  3. 创建 Activity:创建两个 Activity,AndroidBusSendActivity 作为事件的发布者,AndroidBusReceiveActivity 用于注册和反注册事件,并实现不同 tag 的多个订阅者事件处理方法。

  4. 使用 AndroidEventBus 包:确保使用 org.simple.eventbus 包中的 EventBus 类。

import org.simple.eventbus.EventBus;
import org.simple.eventbus.Subscriber;
import org.simple.eventbus.ThreadMode;

/**
AndroidEventBus
接收消息页面
*/
public class AndroidBusReceiveActivity extends AppCompatActivity implements View.OnClickListener {
    public static final String TAG = "AndroidEventBus";
    private TextView mTv_content, mTv_content2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_android_receive);

        findViewById(R.id.btn_skip).setOnClickListener(this);
        mTv_content = findViewById(R.id.tv_content);
        mTv_content2 = findViewById(R.id.tv_content2);

        // 1. 注册事件
        EventBus.getDefault().register(this);
    }

    // 3. 接收事件处理, 有tag为from_three
    @Subscriber(tag = "from_three", mode = ThreadMode.MAIN)
    public void eventAndroidEventBus(MessageEvent message) {
        mTv_content2.setText("eventAndroidEventBus: tag = from_three | " + message.name);
        Log.e(TAG, "eventAndroidEventBus ==  tag = from_three | " + message.name);
    }

    // 接收事件处理, 有tag为FROM_THREE
    @Subscriber(tag = "FROM_THREE", mode = ThreadMode.MAIN)
    public void eventAndroidEventBus1(MessageEvent message) {
        mTv_content.setText("eventAndroidEventBus: tag = FROM_THREE | " + message.name);
        Log.e(TAG, "eventAndroidEventBus1 ==  tag = FROM_THREE | " + message.name);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 2. 反注册事件
        EventBus.getDefault().unregister(this);
    }
}
import org.simple.eventbus.EventBus;

/**
AndroidEventBus
发送消息页面
*/
public class AndroidBusSendActivity extends AppCompatActivity implements View.OnClickListener {
    private TextView mTv_content;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_android_send);
        findViewById(R.id.btn_send).setOnClickListener(this);
        mTv_content = findViewById(R.id.tv_content);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_send: // 发布事件
                mTv_content.setText("对AndroidBusReceiveActivity发布事件");
                // 4. 发布事件,tag为from_three
                EventBus.getDefault().post(new MessageEvent("接收到AndroidBusSendActivity发送过来的事件啦"), "from_three");
                break;
            default:
                break;
        }
    }
}

从结果可以看到:即使事件类型一致,但是只有发布事件和订阅函数处理的tag相同才能接收到事件。

AndroidEventBus 粘性事件概述

粘性事件的特点在于能够在发送事件之后再进行订阅,依然可以接收到该事件。这与普通事件不同,普通事件遵循先注册订阅者后发布事件的顺序,而粘性事件允许先发布后注册的操作方式。

AndroidEventBus的粘性事件的使用与EventBus的粘性事件不同,AndroidEventBus需要注册粘性事件,订阅者的事件处理函数不需要添加sticky标识

在这里插入图片描述

来看看AndroidEventBus的粘性事件的使用步骤:

AndroidEventBus 粘性事件使用步骤

1. 在 Activity 的 onCreate() 方法中注册粘性事件,****onDestroy() 方法中反注册事件

@Override
protected void onCreate(Bundle savedInstanceState) {
    // 1. 注册粘性事件
    EventBus.getDefault().registerSticky(this);
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_layout);
    // 其他初始化操作...
}

@Override
protected void onDestroy() {
    super.onDestroy();
    // 2. 反注册事件
    EventBus.getDefault().unregister(this);
}

onCreate() 里调用 registerSticky() 方法来注册当前 Activity 对粘性事件的接收,确保后续可以处理相关粘性事件。而在 onDestroy() 中通过 unregister() 方法进行反注册,避免内存泄漏以及不必要的资源占用,这是遵循 Android 组件生命周期管理的良好实践。

2. 订阅者的事件处理函数

定义订阅者处理粘性事件的方法,示例如下:

// 3. 接收粘性事件处理,有 tag 为 from_android_sticky
@Subscriber(tag = "from_android_sticky", mode = ThreadMode.MAIN)
public void eventAndroidEventBus(String message) {
    Log.e(TAG, "eventAndroidEventBus ==  tag = from_android_sticky | " + message);

    // 5. 移除粘性事件
    EventBus.getDefault().removeStickyEvent(String.class, "from_android_sticky");
}

这里使用 @Subscriber 注解标记事件处理方法,通过 tag 属性指定了事件的标识(这里是 “from_android_sticky”),并且设置了 ThreadMode.MAIN 表示该事件处理函数将在主线程中执行(常用于涉及 UI 更新的情况)。在处理完事件后,如果后续不再需要该粘性事件,应当及时调用 removeStickyEvent() 方法移除它,这样可以避免重复处理以及可能出现的其他问题。

3. 发布者发布粘性事件

发布粘性事件通过 postSticky() 方法来实现,示例代码如下:

// 4. 发布粘性事件,tag 为 from_android_sticky 
EventBus.getDefault().postSticky("接收到 AndroidStickySendActivity 发送过来的事件啦", "from_android_sticky");

发布者使用 postSticky() 方法发布带有特定 tag 的粘性事件以及相应的事件数据(这里是一个字符串消息),这个事件会被 EventBus 记录下来,以便后续注册的订阅者可以接收到,即便订阅者是在事件发布之后才完成注册的情况也能获取到该事件。

示例:

创建两个 Activity 来演示 AndroidEventBus 粘性事件的完整流程,分别是 AndroidStickySendActivity(发送消息页面)和 AndroidStickyReceiveActivity(接收消息页面)。

AndroidStickySendActivity(发送消息页面)

/**
AndroidEventBus 粘性事件
发送消息页面
*/
public class AndroidStickySendActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_android_send_sticky);
        findViewById(R.id.btn_send).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_send:// 发布事件
                // 4. 发布粘性事件,tag 为 from_android_sticky
                EventBus.getDefault().postSticky("接收到 AndroidStickySendActivity 发送过来的事件啦", "from_android_sticky");
                startActivity(new Intent(this, AndroidStickyReceiveActivity.class));
                break;
        }
    }
}

在这个 Activity 中,点击按钮时会发布粘性事件(通过 postSticky() 方法),并且携带相应的消息内容以及指定的 tag,随后跳转到 AndroidStickyReceiveActivity

AndroidStickyReceiveActivity(接收消息页面)

/**
AndroidEventBus 粘性事件
接收消息页面
*/
public class AndroidStickyReceiveActivity extends AppCompatActivity {
    public static final String TAG = "StickyReceiveActivity";
    private TextView mTv_content;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_android_receive_sticky);
        mTv_content = findViewById(R.id.tv_content);

        // 1. 注册粘性事件
        EventBus.getDefault().registerSticky(this);
    }

    // 3. 接收粘性事件处理,有 tag 为 from_android_sticky
    @Subscriber(tag = "from_android_sticky", mode = ThreadMode.MAIN)
    public void eventAndroidEventBus(String message) {
        mTv_content.setText("eventAndroidEventBus ==  tag = from_android_sticky | " + message);
        Log.e(TAG, "eventAndroidEventBus ==  tag = from_android_sticky | " + message);

        // 5. 移除粘性事件
        EventBus.getDefault().removeStickyEvent(String.class, "from_android_sticky");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 2. 反注册事件
        EventBus.getDefault().unregister(this);
    }
}

tivity_android_receive_sticky);
mTv_content = findViewById(R.id.tv_content);

    // 1. 注册粘性事件
    EventBus.getDefault().registerSticky(this);
}

// 3. 接收粘性事件处理,有 tag 为 from_android_sticky
@Subscriber(tag = "from_android_sticky", mode = ThreadMode.MAIN)
public void eventAndroidEventBus(String message) {
    mTv_content.setText("eventAndroidEventBus ==  tag = from_android_sticky | " + message);
    Log.e(TAG, "eventAndroidEventBus ==  tag = from_android_sticky | " + message);

    // 5. 移除粘性事件
    EventBus.getDefault().removeStickyEvent(String.class, "from_android_sticky");
}

@Override
protected void onDestroy() {
    super.onDestroy();
    // 2. 反注册事件
    EventBus.getDefault().unregister(this);
}

}


在此 `Activity` 中,`onCreate()` 方法里先注册粘性事件,之后通过定义的 `eventAndroidEventBus()` 方法来处理接收到的粘性事件,在这个方法里可以对接收到的消息进行相应处理(如更新 UI 等操作),处理完成后移除该粘性事件,最后在 `onDestroy()` 方法中进行反注册操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值