当前位置: 代码迷 >> 综合 >> 【Android Broadcast】BroadcastReceiver
  详细解决方案

【Android Broadcast】BroadcastReceiver

热度:59   发布时间:2023-12-16 11:31:33.0

作为安卓中的四大组件广播的作用也是非常大的,听名字我们或许就知道他的功能啦!接下来我们就详细回顾、总结下这个重要的组件。

在这里插入图片描述

一、简介

1、分类
  • 广播发送者:发送广播,发布广播后,订阅过的接收者就可以接收到广播。
  • 广播接收者:接收广播(想要接收广播,必须要注册即订阅相应的广播)
2、BroadcastReceiver定义

BroadcastReceiver是一个全局的监听器,它可以监听安卓系统、其他App、自己App发出的广播。并作出相应的处理。是安卓的四大组件之一。

3、BroadcastReceiver的作用

BroadcastReceiver就是广播接收者,接收广播。

4、应用场景

(1)同一app内部的同一组件内的消息通信(单个或多个线程之间)
(2)同一app内部的不同组件之间的消息通信(单个进程)
(3)同一app有不同的组件,且运行在不同的进程中(多个进程之间)
(4)不同app之间的组件之间消息通信(多个进程之间)
(5)Android系统在特定情况下与App之间的消息通信

场景1实际上没必要用广播实现。通过扩展作用于的范围、使用接口回调、使用handler机制都可以解决
场景2同一app内部的不同组件之间的消息通信(单个进程),对于此类需求,在有些教复杂的情况下单纯的依靠基于接口的回调等方式不好处理,此时可以直接使用EventBus等,相对而言,EventBus由于是针对统一进程,用于处理此类需求非常适合,且轻松解耦.
场景3、4、5可以使用广播

5、原理基于

发布者、订阅者,一听我们就想到了“观察者设计模式”,没错安卓中的广播机制就是基于观察者模式的,只不过安卓中通过“消息中心ASM”来处理沟通广播接收者、发送者。为啥要把ASM设计为消息中心呢?
1、这就考虑到广播发送者、接收者的作用了-----发送、接收全局消息。这时肯定不局限与同一个app内的进程中啦。而安卓的ipc机制底层又是基于Binder的所以广播发送者、广播接收者、ASM之间就通过Binder联系起来啦!
2、由于四大组件的启动都牵涉到ASM所以,我们启动广播也是离不开他的,ASM便可根据发送者的要求在安卓的注册列表中寻找合适的广播

寻找依据:

  • 广播的权限
  • intent filter 内的匹配规则

(1)原理草图

在这里插入图片描述

1.广播接收者BroadcastReceiver通过Binder机制向AMS(Activity Manager Service)进行注册;
2.广播发送者通过binder机制向AMS发送广播;
3.AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到BroadcastReceiver(一般情况下是Activity)相应的消息循环队列中;
4.消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法。
ps:本小节“原理基于”里面的内容如果吃力可以参考一下文章:
接口的回调机制
观察者设计模式
Android IPC(一):IPC简介和安卓中的多进程模式
Activity的工作流程

二、使用流程

1、创建广播接收者

步骤:
1、自定义类继承BroadcastReceiver
2、重写onReceiver方法

/*** Created by sunnyDay on 2019/8/13 18:09*/
public class MyBroadcastReceiver extends BroadcastReceiver {
    /*** 接收到广播后此方法回调* */@Overridepublic void onReceive(Context context, Intent intent) {
    Toast.makeText(context, "收到广播", Toast.LENGTH_SHORT).show();//TODO 处理事件}
}

ps:其实我们也可以像activity、service一样使用编译器快捷创建

2、广播的注册

(1)动态注册

public class MainActivity extends AppCompatActivity {
    @Overrideprotected void onResume() {
    super.onResume();IntentFilter filter = new IntentFilter();filter.addAction("sunny.day.custom.broadcast"); //添加过滤条件,接收广播MyBroadcastReceiver mBroadcastReceiver = new MyBroadcastReceiver();registerReceiver(mBroadcastReceiver, filter);}@Overrideprotected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}/*** 点击按钮发送自定义广播* */public void send(View view) {
    Intent intent = new Intent();intent.setAction("sunny.day.custom.broadcast");// 发送者定义 过滤条件sendBroadcast(intent);}@Overrideprotected void onPause() {
    super.onPause();// activity 销毁时解注册unregisterReceiver(mBroadcastReceiver);}
}

说明:
1、如上我们接收自己的广播,这时我们通过点击按钮发送一条广播。
2、在onResume中进行广播的注册,有广播发送时,广播接收者就会收到
3、上面的action是我们自己定义的字符串、其实安卓系统也定义了很多,比如“android.net.conn.CONNECTIVITY_CHANGE”网络状态变化。我们也可以监听系统广播。监听系统广播时只需要注册下系统广播即可。当系统发送广播时,我们就可以收到。

注意:
1、广播注册后要在activity退出时解注册,否则会造成内存泄漏。
2、广播接收器也是运行在UI线程,因此,onReceive方法中不能执行太耗时的操作。否则将因此ANR。一般情况下,根据实际业务需求,onReceive方法中都会涉及到与其他组件之间的交互,如发送Notification、启动service等。

注册解注册的时机:
不在onCreate() & onDestory() 或 onStart() & onStop()注册、注销是因为:
当系统因为内存不足(优先级更高的应用需要内存,请看上图红框)要回收Activity占用的资源时,Activity在执行完onPause()方法后就会被销毁,有些生命周期方法onStop(),onDestory()就不会执行。当再回到此Activity时,是从onCreate方法开始执行。
假设我们将广播的注销放在onStop(),onDestory()方法里的话,有可能在Activity被销毁后还未执行onStop(),onDestory()方法,即广播仍还未注销,从而导致内存泄露。但是,onPause()一定会被执行,从而保证了广播在App死亡前一定会被注销,从而防止内存泄露。

(2)静态注册

也就是在安卓的manifest中通过receiver节点下添加 intentfilter,然后在intentfilter中添加过滤条件(注册条件)
ps:安卓8.0开始限制了静态广播的注册,静态注册的广播隐式启动时失效。
详情参考(自备梯子哈):https://developer.android.google.cn/guide/components/broadcasts.html

 <receiver android:name=".MyBroadcastReceiver"><intent-filter ><!--静态广播 --><action android:name="sunny.day.custom.broadcast" /></intent-filter></receiver>
---------------------------------/*** 点击按钮发送自定义广播* */public void send(View view) {
    Intent intent = new Intent();intent.setAction("sunny.day.custom.broadcast");sendBroadcast(intent);}// public void send(View view) {
    
 Intent intent = new Intent(this,MyBroadcastReceiver.class); // 显式启动
 intent.setAction("sunny.day.custom.broadcast");
 sendBroadcast(intent);
 }

如上:8.0开始,以后的手机接收不到静态注册的广播。
ps:注意我们上面说了静态注册的广播,隐式启动时完全失效。但是我们可以显式启动啊,如上你把发送广播的代码替换为注释部分则会看到又可以接收到啦。这也只是发送自定义广播情况下,当我们使用系统的广播时还是动态注册吧。。。。。

receiver节点的属性:
1、exported :此broadcastReceiver能否接收其他App的发出的广播,这个属性默认值有点意思,其默认值是由receiver中有无intent-filter决定的,如果有intent-filter,默认值为true,否则为false。(同样的,activity/service中的此属性默认值一样遵循此规则)同时,需要注意的是,这个值的设定是以application或者application user id为界的,而非进程为界(一个应用中可能含有多个进程)。
2、name :此broadcastReceiver类名。
3、permission:如果设置,具有相应权限的广播发送方发送的广播才能被此broadcastReceiver所接收。
4、process:四大组件都具有这个属性。为组件指定单独的进程。不指定默认为app默认的进程。

(3)动态静态注册的区别

1、静态注册:

优点:属于常驻广播,清单文件注册后不受任何组件的影响。即使我们的app退出关闭后有广播来了我们的app依旧会被系统调用。
缺点:由于需要时刻监听广播所以耗电、占内存。

2、动态注册:

特点:灵活,跟随组件的生命周期变化。组件销毁时必须要解注册广播,否则容易造成内存泄漏。

三、广播的分类

1、普通广播

普通广播就是我们简单自定义的广播。通过intent携带actionActivity组件发送一个广播,想要接收这个普通广播只需给相应的广播接收者注册下即可(如下)

// 发送普通广播public void send(View view) {
    Intent intent = new Intent();intent.setAction("sunny.day.custom.broadcast");sendBroadcast(intent);}
// 广播接收者注册广播IntentFilter filter = new IntentFilter();filter.addAction("sunny.day.custom.broadcast"); //添加过滤条件,接收广播MyBroadcastReceiver mBroadcastReceiver = new MyBroadcastReceiver();registerReceiver(mBroadcastReceiver, filter);

(2)广播的权限添加

有些广播为了安全或许会添加一些自定义权限,如下我们在清单文件中自定义一个权限:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.sunnyday.broadcastreceiverrevision"><!-- 自定义权限--><permissionandroid:name="this.is.custom.permission"android:protectionLevel="signature"/>
</manifest>

注册广播添加权限:

IntentFilter filter = new IntentFilter();filter.addAction("sunny.day.custom.broadcast"); //添加过滤条件,接收广播mBroadcastReceiver = new MyBroadcastReceiver();registerReceiver(mBroadcastReceiver, filter,"this.is.custom.permission",null);

这时我们点击按钮会发现没收到广播。。。。。原因是你的应用程序没有这个"this.is.custom.permission"权限。你需要声明下。和平常一样: 清单文件<uses-permission android:name=“this.is.custom.permission”/>申请下即可。

2、系统广播

安卓系统定义个好多广播:比如网络变化会发出相应的广播、来电收到短信也会收到相应的广播、手机开关机、电量低等等、、、、、、

(1)使用

我们不需要手动发送这些广播,这些广播是系统再特定的时机发送的。我们只需注册下相应的action即可。

(2)常见的系统广播action

在这里插入图片描述

图片来源:https://blog.csdn.net/carson_ho/article/details/52973504
又大佬总结啦就偷个懒不去扒开发文档了 嘿嘿!!!
ps:有些系统广播是需要权限的,我们使用时声明下即可。

3、有序广播

前面我们接收系统的广播,接收自己自定义的广播都是无序广播,只要订阅了发送者的广播,所有接收者都可以同时接收到。没有先后顺序。接下来便看下有序广播。

(1)接收顺序

1、发出广播后,接收者按照优先级顺序接收广播。
2、所以有序广播是针对广播接收者而言的。

(2)广播接收者接收广播的规则

1、按照Priority属相的大小顺序(大的优先级大)
2、代码注册优先于静态注册(同优先级下)
ps:Priority为intent filter的属性,可以代码设置也可以清单文件属性添加。

(3)广播特点

1、广播接收者按照顺序接收广播(优先级)
2、优先级高的接收者接收广播后可以拦截广播,让优先级低的接收不到。
3、优先级高的广播接收者还可以对广播的内容进行修改,这样优先级低的收到的就是修改后的广播内容。

(4)发送
在这里插入图片描述

4、粘性广播

粘性广播(在 android 5.0/api 21中deprecated,不再推荐使用,相应的还有粘性有序广播,同样已经deprecated)

5、本地广播

(1)为啥使用app内部广播

Android中的广播可以跨App直接通信(exported对于有intent-filter情况下默认值为true)这会导致安全问题:
1、其他app针对我们广播接收器匹配规则发出相应的action的广播,我们的接收器就接收、处理。
2、其他app注册了与app一致的action便可获取广播信息

(2)安全问题的解决

1、注册广播时将exported属性设置为false,使得非本App内部发出的此广播不被接收;
2、在广播发送和接收时,增设相应权限permission,用于权限验证;
3、发送广播时指定该广播接收器所在的包名,此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。(intent.setPackage(packageName))
安卓针对上面的解决思路进行了封装:LocalBroadcastManager类

(3)使用LocalBroadcastManager发送应用内的广播

LocalBroadcastManager这种发送方式只能代码注册。


public class MainActivity extends AppCompatActivity {
    private MyBroadcastReceiver mBroadcastReceiver;private static LocalBroadcastManager localBroadcastManager; // 获得单例@Overrideprotected void onResume() {
    super.onResume();localBroadcastManager = LocalBroadcastManager.getInstance(this);IntentFilter filter = new IntentFilter();filter.addAction("sunny.day.custom.broadcast"); //添加过滤条件,接收广播mBroadcastReceiver = new MyBroadcastReceiver();localBroadcastManager.registerReceiver(mBroadcastReceiver, filter); //注册广播}@Overrideprotected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}/*** 点击按钮发送自定义 app内部广播*/public void send(View view) {
    Intent intent = new Intent();intent.setAction("sunny.day.custom.broadcast");localBroadcastManager.sendBroadcast(intent);  //发送应用内广播}@Overrideprotected void onPause() {
    super.onPause();// activity 销毁时解注册localBroadcastManager.unregisterReceiver(mBroadcastReceiver);}
}

和传统的自定义广播区别:
1、使用了LocalBroadcastManager 类来管理
2、通过LocalBroadcastManager 类发送广播
3、通过LocalBroadcastManager 注册送广播
4、通过LocalBroadcastManager 解绑送广播

end

参考:
1、Android总结篇系列:Android广播机制
2、Android四大组件:BroadcastReceiver史上最全面解析
3、https://github.com/Omooo/Android-Notes/blob/master/blogs/Android/BroadcastReceiver.md
4、Android动态广播添加权限
5、AndroidManifest.xml清单文件详解–permission节点
6、android:exported 属性详解

  相关解决方案