--一、广播接受者的定义和实质
--二、广播接受者的例子(拦截特定号码的短信)。
--三、广播的静态注册和动态注册
--四、自定义广播的种类以及他们的特点
--五、广播的优先级和anr
--六、广播的数据携带
--七、自定义广播的例子
--八.只能动态注册的广播
--九.一些注意的内容
简介:
BroadCastReceiver即广播接受者是android四大组件之一,使用频率不太高,但是在项目中会用到,一般是用来接受系统发出的广播,例如开机广播,电量变化广播等等,从而根据我们项目的业务逻辑做一些处理。
一、BroadCastReceiver(广播接受者)的概念和实质
1.广播的概念
在android中广播接受者和广播是一对,所以说广播和广播接受者一起说。
广播:android系统在运行的时候会产生很多事件,比如说接到电话,给别人打电话,接受短信,发送短信,开机,关机,电量变化等,系统为了让其他应用知道发生了这个事件就会发送一个广播,只要其他应用接收到这个广播,就会知道发生了什么事情,当然我们也可以在应用中根据自己的实际业务的需要发送自定义广播,也就是说广播的种类为:系统广播,自定义广播。
BroadCastReceiver(广播接受者):用来接受系统或者其他应用发出的广播,然后根据自己的业务需求做一些定制化的处理(比如对广播携带的数据进行处理或者接收到广播之后做一些事情等等),但是前提是必须要注册。
2.广播的实质
在android中不管是系统广播还是自定义广播都是通过intent发送出来的,当广播发送出来之后,android系统会遍历手机中所有应用的清单文件,寻找和这个intent匹配的intent-filter,当然intent—filter所在的标签必须是receiver,如果能够匹配上,那么android系统就行这个广播接收者的onreceiver()方法;如果没有那么就什么也不做。
3.注意:
BroadCastReceiver(广播接受者)不是context的子类。
二广播接受者的例子
1.短信拦器
1.1该短信拦截器的实质:
首先手机上的短信应用只是一个接受系统发出的广播和像系统发广播的一个app,当android系统在接受到短信的时候,会发出一条广播,该广播中携带了发短信的电话号码以及短信的内容,然后短信应用接受这个广播,将广播中的内容提取出来,按照一定的样式将短信展示出来。而我们做的拦截特定号码的短信的app,就是在短信应用接受到这个广播之前,通过一定的方法首先接收到这个短信的广播,然后将广播中携带的信息提出出来,进行判断,如果是要拦截的号码,那就直接将这个广播中断,否则,不做处理。
1.2.步骤:
<1>在项目中创建一个类继承BroadCastReceiver,实现其抽象方法onReceive()。
public class SmsBroadCastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//获取广播中的短信数组对象
Bundle smss = intent.getExtras();
Object[] pdus = (Object[]) smss.get("pdus");
//遍历获取到短信数组对象,创建相应的SmsMessage对象。
for (Object object:pdus){
SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) object);
//获取到对方的电话号码
String phoneNumber = smsMessage.getOriginatingAddress();
//获取到短信内容
String messageBody = smsMessage.getMessageBody();
//如果是特定的电话号码,就直接阻断。
if("123456".equals(phoneNumber)){
Log.d("hwd",phoneNumber);
Log.d("hwd",messageBody);
abortBroadcast();
}
}
}
}
<2>在清单文件中注册这个类,并且为该类设置合适的intent-filter(只有在清单文件中进行注册,系统才能找到该类,然后对intent-filter进行匹配)。
<receiver android:name=".SmsBroadCastReceiver">
<intent-filter android:priority="50000">
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
注意priority,这是设置这个广播的优先级,优先级越高,越能早收到广播,而系统的优先级一般为-1000到1000,所以说我们只要设置高于1000即可,其最大值为Int.MAX(2147483637)
<3>添加该应用要添加的权限。
在android中凡是涉及到隐私和需要掏钱的,都需要添加权限。
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
2.IP拨号器(掌握)
2.1 原理:
接收拨打电话的广播,修改广播内携带的电话号码,
2.2步骤
<1> 创建一个类继承BroadcastReceiver类,并实现其抽象方法onReceive(Context context, Intent intent)
public class CallReceiver extends BroadcastReceiver {
//当广播接收者接收到广播时,此方法会调用
@Override
public void onReceive(Context context, Intent intent) {
//拿到用户拨打的号码
String number = getResultData();
//修改广播内的号码
setResultData("17951" + number);
}
}
<2> 在清单文件中通过receiver来注册这个广播,并且通过intent-filter(意图过滤器)来指定要接受的广播。
<receiver android:name="com.itheima.ipdialer.CallReceiver">
<intent-filter >
<action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
</intent-filter>
</receiver>
<3> 接收打电话广播需要权限(当触犯用户的隐私或者要收费的时候,一般都会设计到权限问题。)
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
2.3 注意点
即使广播接收者的进程没有启动,当系统发送的广播可以被该接收者接收时,系统会自动启动该接收者所在的进程
注意:这个注册的广播接受者并没有声明自己的优先级,但是已经实现了我们的目标,是因为系统的打电话这个应用是结果接受者,即是最后一个收到广播的广播接受者,
那么当我们想要禁止用户给某个电话号码打电话的时候,应该如何做呢?
由于打电话是该广播的的结果接受者,所以说我们并不能阻止这个结果接受者接收这个广播,但是我们可以修改这个广播携带的数据,当我们将这个数据修改为空的时候,就可以达到我们的目的。
3监听SD卡状态(掌握)
* 清单文件中定义广播接收者接收的类型,监听SD卡常见的三种状态,所以广播接收者需要接收三种广播
<receiver android:name="com.itheima.sdcradlistener.SDCardReceiver">
<intent-filter >
<action android:name="android.intent.action.MEDIA_MOUNTED"/>
<action android:name="android.intent.action.MEDIA_UNMOUNTED"/>
<action android:name="android.intent.action.MEDIA_REMOVED"/>
<data android:scheme="file"/>-------注意一定要写,为了和广播进行匹配。
</intent-filter>
</receiver>
* 广播接收者的定义
public class SDCardReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 区分接收到的是哪个广播
String action = intent.getAction();
if(action.equals("android.intent.action.MEDIA_MOUNTED")){
System.out.println("sd卡就绪");
}
else if(action.equals("android.intent.action.MEDIA_UNMOUNTED")){
System.out.println("sd卡未就绪");
}
else if(action.equals("android.intent.action.MEDIA_REMOVED")){
System.out.println("sd卡被拔出");
}
}
}
4 勒索软件(掌握)
- 接收开机广播,在广播接收者中启动勒索的Activity
- 清单文件中配置接收开机广播
<receiver android:name="com.itheima.lesuo.BootReceiver">
<intent-filter >
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
- 定义广播接收者
@Override
public void onReceive(Context context, Intent intent) {
//开机的时候就启动勒索软件
Intent it = new Intent(context, MainActivity.class);
context.startActivity(it);
}
- 权限
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
- 以上代码还不能启动MainActivity,因为广播接收者的启动,并不会创建任务栈,那么没有任务栈,就无法启动activity
- 手动设置创建新任务栈的flag
//创建一个新的任务栈,
it.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
5 监听应用的安装、卸载、更新(熟悉)
*原理:应用在安装卸载更新时,系统会发送广播,广播里会携带应用的包名
- 清单文件定义广播接收者接收的类型,因为要监听应用的三个动作,所以需要接收三种广播
<receiver android:name="com.itheima.app.AppReceiver">
<intent-filter >
<action android:name="android.intent.action.PACKAGE_ADDED"/>
<action android:name="android.intent.action.PACKAGE_REPLACED"/>
<action android:name="android.intent.action.PACKAGE_REMOVED"/>
<data android:scheme="package"/> -----注意,需要特别配置
</intent-filter>
</receiver>
- 广播接收者的定义
public void onReceive(Context context, Intent intent) {
//区分接收到的是哪种广播
String action = intent.getAction();
//获取广播中包含的应用包名
Uri uri = intent.getData();
if(android.intent.action.PACKAGE_ADDED.equals(action))){
System.out.println(uri + "被安装了");
}
else if(android.intent.action.PACKAGE_REPLACED.equals(action)){
System.out.println(uri + "被更新了");
}
else if(android.intent.action.PACKAGE_REMOVED.equals(action))){
System.out.println(uri + "被卸载了");
}
}
三、广播接受者的静态注册和动态注册
在android中,广播接受者只有注册过后,才能收到相应的广播,而广播接受者的注册方式有两种,分别为:静态注册和动态注册。
1、静态注册
1.1所谓的静态注册:在项目的清单文件中,在application的标签包裹下,和activity的注册一样,都可以配置独立的intent-filter,只不过其标签名字有activity换成receiver。如下:
<receiver android:name=".SmsBroadCastReceiver">
<intent-filter android:priority="50000">
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
1.2静态注册注意的东西:
<1>android4.0之后,广播接收者所在的应用必须要启动一次,该广播接收者才会生效,当广播接收者生效之后,那么不管这个应用是否在打开的情况下,之后有关相应的广播发出来,那么其就会自动启动,并且接受这个广播。(1>什么叫做启动一次?当应用部署到手机上的时候,一般此时都会直接打开这个app,这个就已经是启动了,如果在真实的环境中,一般都是部署上去,有时候是不直接打开这个应用的,此时就不算启动,需要用户点击app图标,启动app,才算启动一次;2>如果app版本更新了,是否还需要在用户在启动一次?如果应用已经在手机了,且被用户手机启动过了,此时我们进行app的版本更新,那么此时是不需要用户再次启动app,广播接收者就会起作用的。)
<2>android4.0之后,如果广播接收者所在的应用被用户在用户管理界面手动停止了,那么这个广播接收者就不会再接受广播了,除非用户再次手动启动该应用(即点击图标启动该app)。
2、动态注册
2.1动态注册的概念
动态注册就是在代码中进行注册,此时广播接受者的生命周期和其所在的android组件的生命周期是一样的,比如在actiivty,我们在oncreate()方法中注册广播接受者,然后在ondestroy()方法中注销广播接受者,这样广播接受者的生命周期就是注册广播接收者和注销广播接收者之间的这段时间了。
2.2动态注册的例子:
这是拦截特定号码的短信的动态注册代码:
bt_one.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
IntentFilter intentFilter=new IntentFilter();
intentFilter.setPriority(5000);
intentFilter.addAction("android.provider.Telephony.SMS_RECEIVED");
//注册广播接收者
registerReceiver(myReceiver,intentFilter);
}
});
bt_two.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//注销广播接受者
unregisterReceiver(myReceiver);
}
});
另外在清淡文件中添加一个权限:
<uses-permission android:name="android.permission.RECEIVE_SMS" />
2.3动态注册使用的方法:
**广播接收者动态注册使用的方法:
registerReceiver(BroadcastReceiver receiver,IntentFilter filter):
---- 其中receiver即具体的广播接收者,filter则是用来匹配广播的intent的intent-filter.
registerReceiver(BroadcastReceiver receiver,IntentFilter filter,String broadcastPermisson,Handler scheduler)
---- 其中前两个参数和上一个方法的参数意思一样,而broadcastpermisson 则是接受这个广播需要的权限。
**广播接收者动态注销使用的方法:
ungisterReceiver(BroadcastReceiver receiver);
----其中receiver就是我们之前注册过的广播。
3.广播的权限问题和广播接收者的权限问题:
3.1广播的权限问题:
在发送自定义广播的时候,如果这个自定义广播有一个自定义权限,如果接受自定义广播的广播接受者(在另一个app中)是静态注册的,那么此时在发送广播的app的清单文件中不用对
该自定义权限做声明和使用,只需要在广播接受者所在的app的清单文件中对该自定义权限进行声明和使用即可;如果接受该自定义广播的广播接收者(在另一个app中)是动态注册的,
那么此时发送广播的app的清单文件中就要对该自定义权限做声明和使用,并且在广播接收者所在的app的清单文件中,同样要对该自定义权限做声明和使用,然后再动态注册该广播接
收者的时候,registerReceiver(BroadcastReceiver receiver, IntentFilter filter,String broadcastPermission, Handler scheduler)其中的broadcastPermission
可以写,也可以直接设置为null。如下:
<permission
android:name="qindong.heman.heilongjian"
android:protectionLevel="normal" />
<uses-permission android:name="qindong.heman.heilongjian" />
因为我们并不能确定广播接收者到底是使用动态注册还是静态注册,所以在发送自定义广播的时候,直接在发送广播的app的清单文件中对自定义权限做一个声明和使用。
3.2当发送自定义广播,该广播需要一个自定义权限的时候,此时接受自定义广播的广播接收者就再本app中,不需要其他第三方app接受该广播,那么此时需要再app的清单文件中声明和
使用这个自定义权限,如下:,然后当使用registerReceiver(BroadcastReceiver receiver, IntentFilter filter,String broadcastPermission, Handler scheduler)其中
的broadcastPermission可以写,也可以直接设置为null。
<permission
android:name="qindong.heman.heilongjian"
android:protectionLevel="normal" />
<uses-permission android:name="qindong.heman.heilongjian" />
3.3对于自定义权限的级别:
normal:低风险权限,只要申请了就可以使用(在AndroidManifest.xml中添加标签),安装时不需要用户确认;
dangerous:高风险权限,安装时需要用户的确认才可使用;
signature:只有当申请权限的应用程序的数字签名与声明此权限的应用程序的数字签名相同时(如果是申请系统权限,则需要与系统签名相同),才能将权限授给它;
signatureOrSystem:签名相同,或者申请权限的应用为系统应用(在system image中)。
3.2广播接收者的权限问题:
<1>当广播接收者为静态注册的时候:
当接受的广播为系统广播的时候,那么此时只需要在清单文件中使用<use permission>标签来说明使用哪个权限即可。
当接受的广播为自定义广播的时候,此时在清单文件中需要做两个动作:
1》声明这个权限,使用《permission name=“” protectionlevel=“”》来进行声明
2》使用这个权限,使用<uses-permission name="">来进行操作。
<2>当广播接收者为动态注册的时候:
我们在注册广播接收者的时候,使用 registerReceiver(BroadcastReceiver receiver,IntentFilter filter,String broadcastPermisson,Handler scheduler)方法,其中
参数可以不写 broadcastPermisson,其实动态注册的广播接收者和静态注册的广播接收者的处理方法是一样的。
当广播为系统广播的时候,那么此时只需要在清单文件中使用<use permission>标签来说明使用哪个权限即可。
当接受的广播为自定义广播的时候,此时在清单文件中需要做两个动作:
1》声明这个权限,使用《permission name=“” protectionlevel=“”》来进行声明
2》使用这个权限,使用<uses-permission name="">来进行操作。
四、自定义广播的种类以及他们的特点
自定义广播有三种:无序广播,有序广播,粘性广播。
1.无序广播
1.1概念: 所有跟广播的intent匹配的广播接收者都可以收到该广播,并且没有先后顺序(同时收到)
1.2特点:
1.该广播我们不能成功阻断拦截(在android阻断广播使用abortBroadcast(),该方法对这个广播不起作用)。
2.我们可以接收到广播中携带的信息,但是修改不了其信息,以便达到让别人收到的广播的信息是我们修改过的信息这一目的。
1.3如何发出无序广播:
有两种方式:
1.sendBroadcast( Intent intent) :发送一个无序广播,不需要特定的权限。
2.sendBroadcast( Intent intent, String receiverPermission)
第一个参数为:intent
第二个参数为:接受这个广播需要的权限。
2.有序广播
2.1有序广播概念:所有跟广播的intent匹配的广播接收者都可以收到该广播,但是会按照广播接受者的优先级来决定接受的先后顺序。
2.2有序广播特点:
<1>.该广播我们可以成功拦截阻断(使用abortBroadcast()进行阻断广播)。
<2>.广播接收者在接收到该广播之后,可以修改其携带的信息,然后达到让别人收到的广播的信息是我们修改过的信息这一目的。
<3>.有序广播我们可以设置一个结果广播接收者,结果广播接受者的有三个特点。
2.3有序广播之结果广播接收者的特点:
<1> 结果广播接收者是最后一个收到广播的,并且其不用注册(动态或者静态),只需要继承BroadcastReceiver()即可。
<2>abortBroadCast对于结果广播接受者不起作用,也就是说不管做什么操作,结果接受者都会收到
<3>我们在接收到广播之后,可以修改其广播中携带的数据,从而让广播接受者接收到的数据是我们要想传递给它的。
2.4如何发送出有序广播:
在andori中常用的是一下两个方法:
这里有另个方法:
1. sendOrderdBroadcast(Intent intent,String receiverPermission)
---intent即发送这个广播的intent;receiverPermission即接受这个广播所需要的用户权限。
2. sendOrderdBroadcast(Intent intent,String receiverPermission,BroadcastReceiver resultReceiver,Handler schehandler,int initCode,String initData,Bundle initExtras);
---前两者和上边方法一样;schehandler结果接收者的一个回调,可以置为null;initCode,广播中直接携带的int型数据;initData,广 播中直接携带的string型数据;initExtras,广播中直接携带的Bundle型数据。
2.粘性广播
这个不常用。
五、广播的优先级和anr
1.广播的优先级
首先明确一点广播接收者的优先级只是对有序广播其作用,对于无需广播是没有作用的,在android的系统应用中,其广播接收者设置的优先 的范围为-1000~1000,优先级设置的越大,该广播接收者越早接收到广播,而广播接收者优先级的最大值是可以设置为int.max即2147483637。
2.广播接收者和anr
广播接收者正在接收到其相应的广播之后,会执行其onReceiver()方法,如果这个方法在10秒内没有执行结束的话,那么就会出现anr,另外在这个方法执行完毕之后,这个广播接收者的实例就会销毁;注意不要在广播接收者的onreceiver()方法中执行什么耗时操作,或者直接创建一个子线程来处理业务,正确的处理方法是通过intent启动一个activity或者server,然后在这个activity或者server中处理业务。
3.广播接收者的优先级:
3.1 静态注册 的广播接收者按照优先级来进行排序,谁的优先级高谁就先接受到广播。
3.2当动态注册和静态注册的广播接收者在一块的时候,谁的优先级高,谁先收到;当动态广播和静态广播的优先级一样的时候,且当广播发送的时候,动态广播也已经注册了,此时是动态广播先收到广播。
六、广播的数据携带
在广播中我们可以将数据封装在intent中,也可以将数据封装在广播中。
1将数据封装在广播中:
getResultData()--获取string型数据
getResultCode()-----获取int型数据
getResultExtras()---获取bundle型数据
修改广播中携带的数据:
setResultData()--设置string型数据
setResultCode()-----设置int型数据
setResultExtras()---设置bundle型数据
2.将数据封装在intent中,那就直接按照intent中去数据的方式进行下去酒醒了。
七、自定义广播的例子
一下是国家给每个学生发补助的过程,依次经过的机构为:教育局--学校--个人--结果接受者,这四个均是结果接受者。
位置: F:\project\AndroidProject\BoradcastReview\GiveoutSubsidyOneApplication
F:\project\AndroidProject\BoradcastReview\GiveoutSubsidy
6.1这是第一个app中的代码。
//这是发送广播的代码
bt_send_broadcast.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent();
intent.setAction("pudong.com.cn");//这是为intent配置的action
//“a.b.c”这是接受该广播需要的权限。
sendOrderedBroadcast(intent,"a.b.c",new BroadcastReceiver(){
@Override
public void onReceive(Context context, Intent intent) {
String resultData = getResultData();
Log.d("hwd","结果接收者:"+resultData);
}
},null,0,"这是国家发给教育局的每一个人补助,10000元",null);
}
});
6.2这是第二个app中的代码。
EducationBureauReceiver.class
public class EducationBureauReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String resultData = getResultData();
Log.d("hwd","教育局收到:"+resultData);
setResultData("国家发给每个人的补助,8000元");
}
}
SchoolReceiver.class
public class SchoolReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String resultData = getResultData();
Log.d("hwd","学校收到:"+resultData);
setResultData("国家发给每个人的补助,6000元");
}
}
StudentReceiver.class
public class StudentReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String resultData = getResultData();
Log.d("hwd","个人收到:"+resultData);
}
}
6.3这是第二个app中的清单文件。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.weilai.broadcastreceiverapplication">
//这是注册并使用接受这个广播的权限
<permission android:name="a.b.c" android:protectionLevel="normal"/>
<uses-permission android:name="a.b.c"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".EducationBureauReceiver">
<intent-filter android:priority="10000">
<action android:name="pudong.com.cn"/>
</intent-filter>
</receiver>
<receiver android:name=".SchoolReceiver">
<intent-filter android:priority="8000">
<action android:name="pudong.com.cn"/>
</intent-filter>
</receiver>
<receiver android:name=".StudentReceiver">
<intent-filter android:priority="6000">
<action android:name="pudong.com.cn"/>
</intent-filter>
</receiver>
</application>
</manifest>
八.只能动态注册的广播
8.1 到目前为止,我发现的在android中只能动态注册的广播,分别是:
android.intent.action.SCREEN_ON 屏幕的开启
android.intent.action.SCREEN_OFF 屏幕的关闭
android.intent.action.BATTERY_CHANGED 手机电量发生改变
android.intent.action.CONFIGURATION_CHANGED 系统配置改变
android.intent.action.TIME_TICK 系统时间的改变
8.2 原因:
1调高系统的效率,时间,电量,屏幕的唤醒与关闭,以及系统配置的改变,都是android的基本事件,如果使用静态注册的方式来注册广播,那么在系统发出这个广播之后,app的广播接受者就会一直收到这个广播,进行处理,这样系统要处理的事情就太多了,这样会降低系统的效率,而比如短信,电话,sd卡,package包,开机广播这些广播相对来说系统发出这个广播的频率较低,不像手机时间,手机电量,屏幕的关闭月和开启等广播,系统发出的较为频繁。所以五类广播在Intent中都设置了Intent.FLAG_RECEIVER_REGISTERED_ONLY,所以如果要接收,必须动态注册广播接收器,
九、一些注意的内容
1.广播的优先级对无需广播有用吗?
是有用的,当广播为无需广播的时候,此时它对应的广播接受者,依旧是按照广播的优先级进行接受广播的,只不过其内容不能发生改变而已。
<1>当广播接收者都为静态注册的时候,此时谁的优先级高谁先收到;当这些广播接收者的优先级都一样的时候,此时比较的是广播接收者在清单文件中的位置,谁靠前,谁就先收到。
<2>当广播接收者有静态注册和动态注册两种的时候,此时不管广播接收者的优先级,动态广播先收到。