原生广播(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开头的那几个方法,分别是
onEvent
、onEventMainThread
、onEventBackgroundThread
和onEventAsync
,而在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