Android四大组件之一 BroadcastReceiver解析

来源:互联网 发布:绫致时装官方店淘宝 编辑:程序博客网 时间:2024/05/19 22:50


继上一篇讲完Service后,现在来详述下Android中的广播,笔者也是自己的一点经验之谈,如果有错误,请在评论区指出。具体代码可见https://github.com/Mangosir/BroadcastReview

广播是Android四大组件之一,这也说明了它的重要性,那什么是广播呢,有哪些分类呢,有哪些注册方式,具体该怎么用呢,接下来一一道来。



概述

在Android中,Broadcast是一种广泛运用在应用程序之间(单进程通信或者进程间通信)传输信息的机制。而BroadcastReceiver是一个专注于接收广播通知信息,并做出对应处理的组件。很多广播是源自于系统的─比如,通知时区改变、电池电量低、拍摄了一张照片或者用户改变了语言选项。应用程序也可以进行广播──比如说,通知其它应用程序一些数据下载完成并处于可用状态。

应用程序可以拥有任意数量的广播接收器以对所有它感兴趣的通知信息予以响应。所有的接收器均继承自BroadcastReceiver基类。

广播接收器没有用户界面。然而,它们可以启动一个activity来响应它们收到的信息,或者用NotificationManager来通知用户。通知可以用很多种方式来吸引用户的注意力──闪动背灯、震动、播放声音等等。


分类

Android中的广播主要分为一下类型:

1.普通广播(Normal Boradcast)

开发者自己定义的Intent,具体可以使用的方法有:

sendBroadcast(intent)/sendBroadcast(intent,receiverPermission)/sendBroadcastAsUser(intent,userHandler)/sendBroadcastAsUser(intent, userHandler,receiverPermission)。


2.系统广播(System Boradcast)

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


3.有序广播(Ordered Boradcast)

有序广播中的“有序”是针对广播接收者而言的,指的是发送出去的广播被BroadcastReceiver按照先后循序接收。有序广播的定义过程与普通广播无异,只是其主要发送方式变为:sendOrderedBroadcast(intent, receiverPermission, ...)。

对于有序广播,其主要特点总结如下:

1>多个具当前已经注册且有效的BroadcastReceiver接收有序广播时,是按照先后顺序接收的,先后顺序判定标准遵循为:将当前系统中所有有效的动态注册和静态注册的BroadcastReceiver按照priority属性值从大到小排序,对于具有相同的priority的动态广播和静态广播,动态广播会排在前面。

2>先接收的BroadcastReceiver可以对此有序广播进行截断,使后面的BroadcastReceiver不再接收到此广播,也可以对广播进行修改,使后面的BroadcastReceiver接收到广播后解析得到错误的参数值。当然,一般情况下,不建议对有序广播进行此类操作,尤其是针对系统中的有序广播。


4.本地广播(Local Boradcast)

BroadcastReceiver设计的初衷是从全局考虑可以方便应用程序和系统、应用程序之间、应用程序内的通信,所以对单个应用程序而言BroadcastReceiver是存在安全性问题。

可能出现安全隐患如下:

1.其他App可能会针对性的发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收到广播并处理;

2.其他App可以注册与当前App一致的intent-filter用于接收广播,获取广播具体信息。


无论哪种情形,这些安全隐患都确实是存在的。由此,最常见的增加安全性的方案是:

1.对于同一App内部发送和接收广播,将exported属性人为设置成false,使得非本App内部发出的广播不被接收;

2.在广播发送和接收时,都增加上相应的permission,用于权限验证;

3.发送广播时,指定特定广播接收器所在的包名,具体是通过intent.setPackage(packageName)指定在,这样此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。

4.使用LocalBroadcastManager,即App应用内广播

App应用内广播可以理解成一种局部广播的形式,广播的发送者和接收者都同属于一个App。实际的业务需求中,App应用内广播确实可能需要用到。同时,之所以使用应用内广播时,而不是使用全局广播的形式,更多的考虑到的是Android广播机制中的安全性问题。相比于全局广播。

App应用内广播优势体现在:

1、因广播数据在本应用范围内传播,你不用担心隐私数据泄露的问题。

2、不用担心别的应用伪造广播,造成安全隐患。

3、相比在系统内发送全局广播,它更高效


看看本地广播的源码

public static LocalBroadcastManager getInstance(Context context) {    synchronized (mLock) {        if (mInstance == null) {            mInstance = new LocalBroadcastManager(context.getApplicationContext());        }        return mInstance;    }}private LocalBroadcastManager(Context context) {    mAppContext = context;    mHandler = new Handler(context.getMainLooper()) {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case MSG_EXEC_PENDING_BROADCASTS:                    executePendingBroadcasts();                    break;                default:                    super.handleMessage(msg);            }        }    };}

从这个部分源码可以看出两点: 

1.在获取LocalBroadcastManager对象实例的时候,这里用了单例模式。并且把外部传进来的Context转化成了ApplicationContext,有效的避免了当前Context的内存泄漏的问题。这一点我们在设计单例模式框架的时候是值得学习的,看源码可以学习到很多东西。

2.在LocalBroadcastManager构造函数中创建了一个Handler.可见 LocalBroadcastManager的本质上是通过Handler机制发送和接收消息的。

3.在创建Handler的时候,用了context.getMainLooper() ,说明这个Handler是在Android主线程中创建的,广播接收器的接收消息的时候会在Android主线程,所以我们决不能在广播接收器里面做耗时操作,以免阻塞UI



注册方式

1.静态注册:直接在Androidmanifest.xml中注册:

<receiver

android:exported=["true" |"false"]

android:name="string"

android:permission="string"

android:process="string" >

<intent-filter>

        <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>

    </intent-filter>

</receiver>

以上是一些注册属性,其中:

android:exported  ——此broadcastReceiver能否接收其他App的发出的广播,这个属性默认值有点意思,其默认值是由receiver中有无intent-filter决定的,如果有intent-filter,默认值为true,否则为false。(同样的,activity/service中的此属性默认值一样遵循此规则)同时,需要注意的是,这个值的设定是以application或者application user id为界的,而非进程为界(一个应用中可能含有多个进程);

android:name  —— 此broadcastReceiver类名;

android:permission  ——如果设置,具有相应权限的广播发送方发送的广播才能被此broadcastReceiver所接收;

android:process  ——broadcastReceiver运行所处的进程。默认为app的进程。可以指定独立的进程(Android四大基本组件都可以通过此属性指定自己的独立进程)

intent-filter ——用于指定此广播接收器接收特定的广播类型


看注册样例:

<receiver android:name=".broadcast.BroadcastDemo">    <intent-filter >        <action android:name="android.intent.action.ACTION_POWER_CONNECTED"></action>    </intent-filter></receiver>
可能有的同学还有这个印象,通过静态注册广播,即使应用退出了,广播接收器依然还在工作,收到相应广播后还会处理,但是Android 3.1开始,系统在Intent与广播相关的flag增加了参数,分别是

FLAG_INCLUDE_STOPPED_PACKAGES:包含已经停止的包(停止:即包所在的进程已经退出)FLAG_EXCLUDE_STOPPED_PACKAGES:不包含已经停止的包

系统本身则增加了对所有app当前是否处于运行状态的跟踪。在发送广播时,不管是什么广播类型,系统默认直接增加了值为FLAG_EXCLUDE_STOPPED_PACKAGESflag,导致即使是静态注册的广播接收器,对于其所在进程已经退出的app,同样无法接收到广播。


2.动态注册:在代码里注册

BroadcastDemo broadcastDemo = new BroadcastDemo();IntentFilter intentFilter = new IntentFilter();intentFilter.addAction("com,mangoer.boradcast");registerReceiver(broadcastDemo,intentFilter);

其中BoradcastDemo是我自定义的一个广播,给这个广播加了一个过滤器IntentFilter,表示这个广播接收器只接受这个类型的广播。注册之后一定要记得取消注册

if (broadcastDemo != null) {    unregisterReceiver(broadcastDemo);}

对于本地广播

注册方式不一样(本地广播只能通过代码中注册,不要在xml里注册)

LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(this);BroadcastDemo broadcastDemo = new BroadcastDemo();IntentFilter intentFilter = new IntentFilter();intentFilter.addAction("com,mangoer.boradcast");broadcastManager.registerReceiver(broadcastDemo,intentFilter);

取消注册也不一样

broadcastManager.unregisterReceiver(broadcastDemo);

两者发送广播方式也不一样

本地广播是

Intent intent = new Intent();intent.setAction("com,mangoer.boradcast");broadcastManager.sendBroadcast(intent);
而非本地广播是

Intent intent = new Intent();intent.setAction("com,mangoer.boradcast");sendBroadcast(intent);


看对比



发送方式

1.sendBroadcast(Intent intent);

参数intent:携带Action标识信息(与注册广播是的IntentFilter中的action要相同)且可以携带数据

使用Context.sendBroadcast发送,如果依次注册了三个广播接收者A/B/C,那么接受顺序是C/B/A,即后注册的总是先收到广播

使用LocalBroadcastManager.sendBroadcast发送,如果依次注册了三个广播接收者A/B/C,那么接受顺序是A/B/C,即先注册的总是先收到广播


2.sendBroadcast(Intent intent, String receiverPermission)

参数intent:携带Action标识信息(与注册广播是的IntentFilter中的action要相同)且可以携带数据

参数receiverPermission:字面意思是指定广播接收者的权限,也就是说你的BroadcastReceiver注册的时候要添加了这个权限,后续才能收到这个广播


未完待续。。。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 一紧张就血压高怎么办 不在上班时间在单位受伤怎么办 生活过得太压抑怎么办 高低床踏板断了怎么办 高低床 孩子摔下来怎么办 小孩子太小高低床爬梯怎么办 辞职后单位不发工资怎么办 买了个上下床搬家怎么办 爸妈不让学日语怎么办 把共享单车弄坏了怎么办 裙子沾到单车油怎么办? 外穿的短裤卷边怎么办 蹬完单车膝盖疼怎么办 夏天骑摩托太热怎么办 半框眼镜线断了怎么办 镜片从镜框脱了怎么办 干活累的肩膀痛怎么办 干活累的肩膀疼怎么办 骑山地车手腕和脖子疼怎么办? 布艺沙发坐软了怎么办 篮球护臂太大了怎么办 豪爵摩托车没电怎么办 西装裤裤脚太宽怎么办 全棉衣服有异味怎么办 羊剪绒毛领褪色怎么办 篮球鞋买大2码怎么办 新鞋穿着脚累怎么办 买了国产乔丹怎么办 跑完1000米吐了怎么办 翻毛鞋脚染色了怎么办 翻毛皮被染色了怎么办 防鹿皮绒鞋染色怎么办 亚瑟士跑鞋挤脚怎么办 亚瑟士跑鞋很紧怎么办 飞线鞋面破了怎么办 新袜子穿了很滑怎么办 鞋底硬脚底板疼怎么办 新鞋前面太硬怎么办 新鞋子鞋底太硬怎么办 不到一米八的身高想扣篮怎么办 鞋胶粘在鞋面上怎么办