BroadcastReceiver与EventBus实现组件间通讯(简单理解)

本文详细解析了Android中的广播机制,包括普通广播、有序广播、粘性广播和本地广播的区别与应用场景,以及如何使用BroadcastReceiver和LocalBroadcastManager实现组件间通讯。
摘要由CSDN通过智能技术生成

原生广播(Broadcastreceiver)

Broadcast(广播) 是一种广泛应用在应用程序之间传输信息的机制,而BroadcastReceiver(广播接收器)则是用于接收来自系统和应用的广播对并对其进行响应的组件。Android提供了一套完整的API,允许应用程序自由地发送和接收广播,其中又用到可以传递信息的Intent,IntentFilter
在这里插入图片描述


Broadcast 直译广播,我们举个形象的例子来帮我理解下BroadcastReceiver:

记得以前读书 的时候,每个班级都会有一个挂在墙上的大喇叭,用来广播一些通知,比如,开学要去搬书,广播: “每个班级找几个同学教务处拿书”,发出这个广播后,所有同学都会在同一时刻收到这条广播通知, 收到,但不是每个同学都会去搬书,一般去搬书的都是班里的"大力士",这群"大力士"接到这条广播后就会动身去把书搬回!

那么,上面这个就是一个广播传递的一个很形象的例子:
大喇叭--> 发送广播 --> 所有学生都能收到广播 --> 学生处理广播

回到我们的概念,其实BroadcastReceiver就是应用程序间的全局大喇叭,即通信的一个手段, 系统自己在很多时候都会发送广播

  • 电量低/启动系统/网络变化/时间/内存健康/
  • 如果你想让你的应用在接收到 这个广播的时候做一些操作,比如:系统开机后,应用进行初始化操作等,这个时候你只需要为你的应用 注册一个用于监视开机的BroadcastReceiver,当接收到开机广播就可以做需要的功能设置。当然我们也可以自己发广播,比如:接到服务端推送信息,用户在别处登录,然后应该强制用户下线回到 登陆界面,并提示在别处登录等。

广播类别
在这里插入图片描述

  • 普通广播(Normal Broadcast)

    • 又称无序广播,所有与广播Intent匹配的BroadcastReceiver,都可以收到这条广播,并且不分先后顺序,视为同时收到,通过Context.sendBroadcast()方法发送。
    • 这种广播的效率比较高,但缺点是接收器不能将处理结果传递给下一个接收器,并且无法在中途终止广播。
    • 完全异步执行
  • 有序广播(Ordered Broadcast)

    • 同步执行的广播
    • 发送: 通过Context.sendOrderedBroadcast()方法发送,这种广播发出后,通过receiver的intent-filter中的android:priority属性来设置优先级,优先级从-1000~1000数越大,优先级越高
    • 传递: 使用setResult()方法把结果传递给下一个接收者,通过getResult()方法获取上一个接收者传递过来的结果
    • 终止: abortBroadcast()方法终止该广播,使该广播不再传递给下一个接收者。
  • 粘性广播(Sticky Broadcast)

    • 粘性广播通过Context.sendStickBroadcast()方法来发送,用此方法发送的广播会一直滞留,当有匹配此广播的接收器被注册后,该广播接收器就会收到此广播。
    • 使用此广播时,需要获得BROADCAST_STICKY权限。(在 android 5.0/api 21后不再推荐使用)
  • 系统广播(System Broadcast)

    Android系统中内置了多个系统广播,只要涉及到手机的基本操作,基本上都会发出相应的系统广播。如:开启启动,网络状态改变,拍照,屏幕关闭与开启,点亮不足等等。
    每个系统广播都具有特定的intent-filter,其中主要包括具体的action,系统广播发出后,将被相应的BroadcastReceiver接收。系统广播在系统内部当特定事件发生时,有系统自动发出。

  • 本地广播(Local Broadcast)

    • 以上广播都属于全局广播,发出去的广播,只要有匹配的接收者,就可以收到广播。
    • 造成一些问题,一是消耗性能,二是容易引起安全性的问题,为了能够简单的解决这方面的问题,Android引入了一套广播本地广播机制,使用该机制发出的广播只能够在本应用内部进行传递,并且广播接收器也只能接收来自本应用发出的广播。

接收系统广播
1)两种注册广播的方式
动态注册静态注册

[注]上面解释那么多为什么这是广播为什么要用,为了开篇大家就当复习牢固一下概念,下面回到正文,不在赘述基础问题



BroadcastReceiver与LocalBroadcastManager结合实现组件间通讯
1.接收广播界面的配置
  • 1.创建 BroadcastReceiver实现类
    BroadcastReceiver bordcastReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent)
        {
            String info = intent.getStringExtra("broadcastInfo");
            Toast.makeText(context, "接收到的信息" + info, Toast.LENGTH_SHORT).show();    
        }
    };
  • 2.获取LocalBroadcastManager实例

这里为了代码简洁性,对LocalBroadcastManager进行封装了工具类 LocalBroadcastManagerUtils

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.LocalBroadcastManager;

/**
 * CN:      LocalBroadcastManagerUtils
 * Author: JSYL-DINGCL (dingcl@jsyl.com.cn)
 * Des:    LocalBroadcastManager工具类
 */
@SuppressWarnings("WeakerAccess")
public class LocalBroadcastManagerUtils {
    private LocalBroadcastManagerUtils() {
        throw new RuntimeException("Do not need instantiate!");
    }
    @NonNull
    public static LocalBroadcastManager getBroadcastManager(@NonNull Context ctx) {
        return LocalBroadcastManager.getInstance(ctx);
    }
    @Nullable
    public static IntentFilter getIntentFilter(@NonNull String... actions) {
        IntentFilter filter = null;
        if (actions.length > 0) {
            filter = new IntentFilter();
            for (String action : actions) {
                filter.addAction(action);
            }
        }
        return filter;
    }
    /**
     * @Desc 通过Action注册广播接收者
     * @Param [ctx, receiver, actions]
     */
    public static void registerReceiver(@NonNull Context ctx, @NonNull BroadcastReceiver receiver, @NonNull String... actions) {
        IntentFilter filter = getIntentFilter(actions);
        if (filter != null) {
            registerReceiver(ctx, receiver, filter);
        }
    }

    /**
     * @Desc 通过IntentFilter注册广播接收者
     * @Param [ctx, receiver, filter]
     */
    public static void registerReceiver(@NonNull Context ctx, @NonNull BroadcastReceiver receiver, @NonNull IntentFilter filter) {
        getBroadcastManager(ctx).registerReceiver(receiver, filter);
    }

    /**
     * @Desc 注销广播接收者
     * @Param [ctx, receiver]
     */
    public static void unRegisterReceiver(@NonNull Context ctx, BroadcastReceiver receiver) {
        getBroadcastManager(ctx).unregisterReceiver(receiver);
    }

    /**
     * @Desc 通过Action发送广播
     * @Param [ctx, action]
     */
    public static void sendBroadcast(@NonNull Context ctx, @NonNull String action) {
        sendBroadcast(ctx, new Intent(action));
    }

    /**
     * @Desc 通过intent发送广播
     * @Param [ctx, intent]
     */
    public static void sendBroadcast(@NonNull Context ctx, @NonNull Intent intent) {
        getBroadcastManager(ctx).sendBroadcast(intent);
    }

    /**
     * @Desc 通过Action同步发送广播
     * @Param [ctx, action]
     */
    public static void sendBroadcastSync(@NonNull Context ctx, @NonNull String action) {
        sendBroadcastSync(ctx, new Intent(action));
    }

    /**
     * @Desc 通过Intent同步发送广播
     * @Param [ctx, intent]
     */
    public static void sendBroadcastSync(@NonNull Context ctx, @NonNull Intent intent) {
        getBroadcastManager(ctx).sendBroadcastSync(intent);
    }
}

获取实例:

 LocalBroadcastManager broadcastManager = LocalBroadcastManagerUtils.getBroadcastManager(MainActivity.this);
  • 3.注册广播
   private void registBrocastReceiver() {
        IntentFilter localIntentFilter = new IntentFilter();
        localIntentFilter.addAction("android.intent.action.MEDICAL_BROADCAST");
        LocalBroadcastManagerUtils.registerReceiver(MainActivity.this,bordcastReceiver, localIntentFilter);
    }
  • 4.注销广播

界面销毁的时候調用,一般遵循有始有终 绑定注册对应注销

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (broadcastManager != null && bordcastReceiver != null) {
            LocalBroadcastManagerUtils.unRegisterReceiver(MainActivity.this,bordcastReceiver);
        }
    }

这样广播的接收者配置就完成了

  • 5.完整代码
/**
* 广播的接收者
*/
public class MainActivity extends AppcompatActivity{
	 private LocalBroadcastManager broadcastManager;
	
	  /**
	   * 创建广播体
	   */
	  BroadcastReceiver bordcastReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent){
            String info = intent.getStringExtra("broadcastInfo");
            Toast.makeText(context, "接收到的信息" + info, Toast.LENGTH_SHORT).show();            
        }
    };
	
	@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.xxx);
		//注册广播
        registBrocastReceiver();
        broadcastManager = LocalBroadcastManagerUtils.getBroadcastManager(MainActivity.this);
    }

 	private void registBrocastReceiver() {
        IntentFilter localIntentFilter = new IntentFilter();
        localIntentFilter.addAction("android.intent.action.MEDICAL_BROADCAST");
        LocalBroadcastManagerUtils.registerReceiver(MainActivity.this,bordcastReceiver, localIntentFilter);
    }

	@Override
    protected void onDestroy() {
        super.onDestroy();
        //注销广播
        if (broadcastManager != null && bordcastReceiver != null) {
            LocalBroadcastManagerUtils.unRegisterReceiver(MainActivity.this,bordcastReceiver);
        }
    }
}

2.广播发送界面配置(广播发送者)

发送广播

public class SecondFragment extends Fragment {
	@OnClick(R.id.sendReceiver)
    public void onClick() {
        Intent localIntent = new Intent("android.intent.action.MEDICAL_BROADCAST");
        localIntent.putExtra("broadcastInfo", "broadcast发送");
        LocalBroadcastManagerUtils.sendBroadcast(getActivity(),localIntent);
    }
}

[注] 上述发送界面是一个Fragment,基础配置如上述代码,在界面中有一个发送广播的按钮。
这样就实现了界面之间的消息通讯(组件间通讯)



EventBus

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

1.三个角色

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

2.四种线程模型

EventBus3.0有四种线程模型,分别是:

  • POSTING: 默认,表示事件处理函数的线程跟发布事件的线程在同一个线程。
  • MAIN: 表示事件处理函数的线程在主线程(UI)线程,因此在这里不能进行耗时操作。
  • BACKGROUND: 表示事件处理函数的线程在后台线程,因此不能进行UI操作。如果发布事件的线程是主线程(UI线程),那么事件处理函数将会开启一个后台线程,如果果发布事件的线程是在后台线程,那么事件处理函数就使用该线程。
  • ASYNC: 表示无论事件发布的线程是哪一个,事件处理函数始终会新建一个子线程运行,同样不能进行UI操作。

发布/订阅事件(基本使用)

引入依赖:

implementation 'org.greenrobot:eventbus:3.1.1'

定义事件

public class MessageEvent {
    private String message;

    public MessageEvent(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

注册/注销事件
在需要订阅事件的模块中,注册/注销eventbus

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

注意:

  • 注册完了,在不用的时候千万别忘了unregister。
  • 不能重复注册。注册之后,没有unregister,然后又注册了一次。
  • register与unregister的时间根据实际需求来把控,官方的例子是在onStart()回调方法进行注册,onStop()回调方法进行unregister(),根据需求做改动。

实现接收事件的方法(在注册界面)

    //接收事件消息
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onShowEventMessage(MessageEvent messageEvent){
        //将数据显示到页面上
        Log.e("eventbus","接收事件消息体:"+messageEvent.getMessage());
        tvEventTest.setText(messageEvent.getMessage());
    }

创建的这个方法要注意:

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

事件发送

public class EventBusActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_eventbus);
        Button btnNotifyMsg = findViewById(R.id.btnNotifyMsg);
        btnNotifyMsg.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //发送事件
                EventBus.getDefault().post(new MessageEvent("[我是事件发送界面]发送事件消息"));
                finish();
            }
        });
     }
}

EventBus.getDefault() 方法,该方法会获取一个单例。所以才可以随时使用,如果不是用这种单例模式,需要想办法把订阅者(Subscriber)注册时用的EventBus的引用传给需要发送事件的模块中,简而言之就是Subscriber用的eventbus 和post方法需要的eventbus需要是同一个eventbus。

以上就是简单利EventBus实现注册与解绑,事件监听发送与接收

这里在需要接收事件的模块进行EventBus注册监听,然后按下跳转按钮跳转到另一个Activity,并在另一个Activity发布我们输入的事件。在上面的Activity中,我们会添加一个监听的方法,即onShowEventMessage ,这里我们需要为其加入注解Subscribe 并指定线程模型为主线程 MAIN 。最后,就是在Activity的 onDestroy 方法中取消注册该Activity。


粘性事件

所谓的 粘性事件 ,就是指发送了该事件之后再订阅者依然能够接收到的事件。使用黏性事件的时候有两个地方需要做些修改。一个是订阅事件的地方,这里我们在先打开的Activity中注册监听黏性事件:

	@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
	public void onShowEventMessage(MessageWrap message) {
    	String txt = "Sticky event: " + message.message;
     	tvEventTest.setText(txt );
	}

另一个是发布事件的地方,这里我们在新的开的Activity中发布黏性事件。即调用EventBus的postSticky方法来发布事件:

	public class EventBusActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_eventbus);
        Button btnNotifyMsg = findViewById(R.id.btnNotifyMsg);
        btnNotifyMsg.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //发送事件
                //EventBus.getDefault().post(new MessageEvent("[我是事件发送界面]发送事件消息"));
                EventBus.getDefault().postSticky(new MessageEvent("[我是事件发送界面]发送粘性事件消息"));
                
                finish();
            }
        });
     }
}


参考

如果想要更深入了解,那么推荐几篇文章

EventBus用法及源码解析
EventBus 3.0进阶:源码及其设计模式 完全解析
EventBus源码分析(一)
EventBus阅读
EventBus源码解读详细注释
Rxjava和EventBus对比


>>转载请注明出处

作者: maiduoduo
邮箱: maiduoduo0@163.com
博客主页: https://blog.csdn.net/Maiduoudo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值