使用Android手机的时候,我们的手机管家中经常会出现开机自启动某某app,那么对于这个某某APP来说,他是怎么知道系统什么时候开机的呢?还有,系统短信怎么知道收到了短信?以及屏幕点亮与关闭、应用卸载与安装等等。

这就讲到了Android四大组件之一:BroadcastReceiver,翻译是广播接收者。意思就是接收广播用的。他可以接收到系统开机完成的广播,以及系统电量不足的广播,以及系统收到短信的广播,等等。我们收到广播后就可以做我们想做的事了。

现实中使用广播时,有发送广播的电台,接收广播的收音机以及广播传递的媒介电磁波。而在Android中的广播机制与现实中一样,发送广播的是Broadcast,接收广播的BroadcastReceiver及广播之间传递数据的Intent

注册BroadcastReceiver接收广播

  • 继承BroadcastReceiver
    这是一个抽象类,public abstract class BroadcastReceiver {
  • 实现抽象方法public abstract void onReceive(Context context, Intent intent);
    • 当收到注册的广播时,onReceive方法会被调用。
    • context是上下文,Intent就是广播携带的数据。
public class MyBroadcastReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();//获取到收到的广播的名称Log.e("hui", "收到的广播的Action是:"+action);}
}
  • 注册BroadcastReceiver,作为四大组件之一,当然需要注册。BroadcastReceiver有两种注册方式:

    • 静态注册(在AndroidManifest.xml清单文件中注册)
    • 动态注册(在代码中注册)

广播接收者静态注册方式

当我们需要一直接收某种广播时,可以使用静态注册方式。

监听手机打电话为例子。

<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/><receiver android:name=".MyBroadcastReceiver"><intent-filter><action android:name="android.intent.action.NEW_OUTGOING_CALL" /></intent-filter>
</receiver>

上面的receiver 表示这个MyBroadcastReceiver是广播接收者。action表示要监听的广播类型,这里的表示开机完成的广播。 因为监听用户的电话状态属于侵犯用户隐私,所以需要添加android.permission.PROCESS_OUTGOING_CALLS权限。
下图是接收打电话广播:

实战开机自启动APP:链接

广播接收者动态注册方式

当我们不需要一直接收某种广播时,可以使用动态注册广播接收者的方式。

监听屏幕点亮与关闭为例子。

public class MainActivity extends Activity {private MyBroadcastReceiver receiver ;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);registerMyReceiver();//在activity创建的时候进行注册监听}private void registerMyReceiver() {receiver = new MyBroadcastReceiver();IntentFilter filter = new IntentFilter();//创建IntentFilter对象filter.addAction(Intent.ACTION_SCREEN_OFF);//IntentFilter对象中添加要接收的关屏广播filter.addAction(Intent.ACTION_SCREEN_ON);//添加点亮屏幕广播registerReceiver(receiver, filter);}private void unRegisterMyReceiver(){if(receiver != null){unregisterReceiver(receiver);//反注册广播,也就是注销广播接收者,使其不起作用}}
}

下图是接收屏幕点亮与关闭广播:

下图是退出APP的状况:

可以看到,退出APP后,接收打电话广播任然起作用,但是接收屏幕点亮与关闭的广播却没效果。为什么呢?
看下述差异:

实战短信验证码自动填入:链接在这

广播接收者静态注册方式与静态注册方式差异

  • 静态注册
    静态注册依附于清单文件,只要APP启动过一次,所静态注册的广播就会生效,无论当前的APP处于停止使用还是正在使用状态。只要相应的广播事件发生,系统就会遍历所有的清单文件,通知相应的广播接收者接收广播,然后调用广播接收者的onReceiver方法。

  • 动态注册
    动态注册方式依赖于所注册的组件,当APP关闭后,组件对象都不在了动态注册的代码都不存在了,所动态注册监听的action自然不在生效。

  • 静态注册的广播传播速度要远远慢于动态注册的广播。

对广播接收者同时使用静态与动态注册

上面例子中MyBroadcastReceiver使用静态注册监听用户打电话,使用动态注册监听用户屏幕点亮与关闭。
所以,监听到屏幕的开关只有在APP运行的状态才可以,但是监听打电话的状态无论此时app是否在运行,都可以监听到。
需要注意:动态注册的广播的优先级大于静态注册的广播。至于这个是为什么呢?额(⊙o⊙)…谷歌写的源代码的时候先对动态广播进行处理然后在对静态广播进行处理。后面我们了解到广播的优先级后会实例证明的。

BroadcastReceiver分类

广播的发送,可以分为有序广播无序广播本地广播以及sticky广播

有序广播

有序广播 是一种分先后广播接收器的广播,广播接收者的优先级越高,越先接收广播。优先级高的广播先收到广播,收到广播后可以修改广播的内容,也可以拦截广播不让广播向下传递。
就像皇上通知知府每人赏金100两,知府通知知县每人赏金100两,最后才是农民知道了赏金的事,一旦知府或者知县不告诉下级赏金的的事,那么农民就不知道赏金的事了,这就是有序广播的拦截广播;当然知府或者知县也可以向下级通知只有赏金10两的事,这就是有序广播的修改广播内容。

无序广播

无序广播 指所有与之匹配的广播接收者都能收到广播,没有先后顺序,直到没有广播接收者接收广播为止才会停止广播的传递。
就像皇上贴告示,昭告天下每人赏金100两银子一样,那么所有的农民都知道了这件事,没有先后之分,当农民直到了钱的事之后这件事就算了结了。

前文讲过,有广播发送时,系统会遍历全部APP的receiver。如果想使得本APP的广播不被外界的广播所干扰,可以在receiver节点添加android:exported=”false”属性 ,这样系统遍历全部APP清单文件的广播接收者时不会对本receiver进行判断及处理。
这个值为FALSE表示不予其他APP相交互。

本地广播

与有序和无序广播的全局广播(任何一方发出广播本手机的任何一个程序都能收到对应的广播)相比,本地广播是局部的广播基于本程序的广播,其他的程序无法收到这个广播。本地广播就类似当地的知县单独给农民发一两银子,只有当地人才知道,其他的人不知道。这个广播是API 21的V4包中新增的,用来保证广播是独家私有的。
这种广播是安全的,外界不会干扰他,广播也不会被其他进程所收到。

sticky广播

sticky粘性的意思。这种广播一般不会终止,只要有符合条件的广播接收者能接收广播,那么就会发送给他广播。永远不会终止发送广播,除非某个广播接收者告诉它不要再发送广播了。

发送自定义广播

实例演练:创建两个广播接收者:ZhiFuReceiver/ZhiXianReceiver

创建:

public class ZhiXianReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Log.d("hui",  "ZhiXianReceiver = " + intent.getStringExtra("qian"));//取出广播中携带的数据,因为我存数据的时候是intent.putExtra("qian", "100");存入的。遵循如何存如何取得原则取数据}
}public class ZhiFuReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Log.d("hui",  "ZhiFuReceiver = " + intent.getStringExtra("qian"));}
}

清单文件如下配置:

        <receiver android:name=".ZhiFuReceiver"><intent-filter ><action android:name="my.broadcast.faqian"/>//自定义的广播接收者接收的广播名称</intent-filter></receiver><receiver android:name=".ZhiXianReceiver"><intent-filter  ><action android:name="my.broadcast.faqian"/></intent-filter></receiver> 

发送无序广播

public void sendCustomBroadcast(View view){Intent intent = new Intent("my.broadcast.faqian");//action是my.broadcast.faqian,清单文件中的action与之一致方可收到广播intent.putExtra("qian", "100");//广播中携带的数据sendBroadcast(intent);//发送无序广播} 

虽然这里打印顺序有先后但是这个先后顺序是无意义的,总体来看还是无序的。

发送有序广播

发送方式一:

public void sendCustomBroadcast(View view){Intent intent = new Intent("my.broadcast.faqian");//action是my.broadcast.faqian,清单文件中的action与之一致方可收到广播intent.putExtra("qian", "100");//广播中携带的数据 /*** sendOrderedBroadcast(Intent intent, String receiverPermission);*/sendOrderedBroadcast(intent, null);//发送有序广播}

清单文件配置

<receiver android:name=".ZhiFuReceiver"><intent-filter android:priority="100">//设置优先级,为整数,越大优先级越高<action android:name="my.broadcast.faqian"/></intent-filter></receiver><receiver android:name=".ZhiXianReceiver"><intent-filter   android:priority="200" ><action android:name="my.broadcast.faqian"/></intent-filter></receiver>

ZhiXianReceiver优先级大于ZhiFuReceiver优先级,故ZhiXianReceiver先收到广播。

发送方式二:

sendOrderedBroadcast的另一个重载方法如下。

sendOrderedBroadcast(Intent intent,//封装了action及其他数据String receiverPermission, //广播接收者需要的权限BroadcastReceiver resultReceiver,//Handler scheduler,int initialCode,String initialData,Bundle initialExtras);

参数解释:

  • intent 封装了action及其他数据
  • receiverPermission, //广播接收者需要的权限
  • resultReceiver 有序广播是支持拦截的,一旦被拦截可以修改广播中数据甚至直接终止广播,这个resultReceiver表示无论当广播传播结束以后我任然会受到广播。(下面会有栗子演示)
  • initialCode 发送广播的时候默认携带的数据
  • initialData 发送广播的时候默认携带的数据
  • initialExtras 发送广播的时候默认携带的数据

实例:
将上面例子中的发送广播的方法修改如下

 public void sendCustomBroadcast(View view){Intent intent = new Intent("my.broadcast.faqian");//action是my.broadcast.faqian,清单文件中的action与之一致方可收到广播Bundle bundle = new Bundle();bundle.putString("qian","100");//广播中携带的bundle数据 sendOrderedBroadcast(intent,null, //permission为nullnew ZhiFuReceiver(), //这里的new ZhiFuReceiver()为最终的广播接收者,也就是说无论他曾经有没有收到广播都会再次收到广播。null,666,//initCode"我是initialData",//initDatabundle);//bundle//以上所有入参都会携带在广播中,如何取出呢?}

接收广播

public class ZhiXianReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {/***********获取数据*************/int initCode = getResultCode();//获取传递过来的initCodeString initData = getResultData();//获取传递过来的initDataBundle initBundle = getResultExtras(true);//获取传递过来的BundleLog.d("hui",  "ZhiXianReceiver = " +"initCode = "+initCode +" ,initdata = " +initData +" ,bundle = " +initBundle.getString("qian"));}
}public class ZhiFuReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {/***********获取数据*************/int initCode = getResultCode();//获取传递过来的initCodeString initData = getResultData();//获取传递过来的initDataBundle initBundle = getResultExtras(true);//获取传递过来的BundleLog.d("hui",  "ZhiFuReceiver = " +"initCode = "+initCode +" ,initdata = " +initData +" ,bundle = " +initBundle.getString("qian"));}
}

结果:

ZhiXianReceiver = initCode = 666 ,initdata = 我是initialData ,bundle = 100
ZhiFuReceiver = initCode = 666 ,initdata = 我是initialData ,bundle = 100
ZhiFuReceiver = initCode = 666 ,initdata = 我是initialData ,bundle = 100

有一点需要说明,这里ZhiFuReceiver 收到了两次数据。为什么呢?ZhiXianReceiver 得优先级大于ZhiFuReceiver ,同时ZhiXianReceiver 未拦截广播,所以会先ZhiXianReceiver 一次后ZhiFuReceiver 一次,而发送广播的时候声明了ZhiFuReceiver 为最终接受者,所以无论他曾经有没有收到广播都会再次收到广播。

图示:

下面我们看看拦截后会有什么效果。

有序广播的拦截与修改数据

拦截广播

将上面例子中的ZhiXianReceiver 添加一行拦截广播的代码,看看结果。

public class ZhiXianReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {/***********获取数据*************/int initCode = getResultCode();//获取传递过来的initCodeString initData = getResultData();//获取传递过来的initDataBundle initBundle = getResultExtras(true);//获取传递过来的BundleLog.d("hui",  "ZhiXianReceiver = " +"initCode = "+initCode +" ,initdata = " +initData +" ,bundle = " +initBundle.getString("qian"));abortBroadcast();//拦截广播,广播被终止,以后不会有其他广播接收者再收到广播了。}
}

这里abortBroadcast()拦截了有序广播,不是说每人能再收到广播了么?为什么ZhiFuReceiver 还能收到广播呢?这是因为ZhiFuReceiver 是广播的最终接受者,广播从优先级高的广播接收者优先接收,一层一层向优先级较低的传送。当广播被拦截后,广播部分的层层发送这里链路发送完毕,但是有最终广播接收者,故最终广播接收者会收到最后的广播。故ZhiFuReceiver 会收到广播。

下图理解:

修改广播中内容

public class ZhiXianReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {/***********获取数据*************/int initCode = getResultCode();//获取传递过来的initCodeString initData = getResultData();//获取传递过来的initDataBundle initBundle = getResultExtras(true);//获取传递过来的BundleLog.d("hui",  "ZhiXianReceiver = " +"initCode = "+initCode +" ,initdata = " +initData +" ,bundle = " +initBundle.getString("qian"));/**************修改数据****************/setResultCode(8989);//修改initCodesetResultData("ZhiXianReceiver修改了数据"); //修改initData//修改bundle数据Bundle bundle = new Bundle();bundle.putString("qian", "10");setResultExtras(bundle);}
}

上面例子中我把几个广播接收者都写在一个APP中了,如果把每个广播接收者分别放在不同的app中一样都能收到广播(如果广播不被拦截)。

如果我只想发送的广播给我自己APP种的广播接收到,可以使用本地广播,这种广播是安全的,外界不会干扰他,广播也不会被其他进程所收到。

发送本地广播

本地广播的使用是写在代码中的,因为本地广播发送广播时是直接在代码中注册的广播中进行匹配从而调用其onReceiver的。
简单看下源码:

 public void sendBroadcastSync(Intent intent) {if (sendBroadcast(intent)) {executePendingBroadcasts();}}private void executePendingBroadcasts() {while (true) {BroadcastRecord[] brs = null;synchronized (mReceivers) {final int N = mPendingBroadcasts.size();if (N <= 0) {return;}brs = new BroadcastRecord[N];mPendingBroadcasts.toArray(brs);mPendingBroadcasts.clear();}for (int i=0; i<brs.length; i++) {BroadcastRecord br = brs[i];for (int j=0; j<br.receivers.size(); j++) {//在这里直接调用其onReceiver方法了br.receivers.get(j).receiver.onReceive(mAppContext, br.intent);}}}}

使用localBroadcastManager.registerReceiver( myBroadCastReceiver, intentFilter)注册:

    /*** 本地广播接收者进行注册,必须在代码中注册,清单文件注册是无效的*/public void registerMyAPPReceiver(View view) {//创建广播接收者MyBroadCastReceiver myBroadCastReceiver = new MyBroadCastReceiver();MyBroadcastReceiver2 myBroadCastReceiver2 = new MyBroadcastReceiver2();//封装要接收的广播类型IntentFilter intentFilter = new IntentFilter();intentFilter.addAction("my.broadcast.faqian2");//拿到LocalBroadcastManager对象,对固定的Receiver进行注册,成为本地广播接收者LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(MainActivity.this);localBroadcastManager.registerReceiver( myBroadCastReceiver, intentFilter);localBroadcastManager.registerReceiver(myBroadCastReceiver2, intentFilter);}

注意:
1. registerReceiver注册一个广播接收者可以多次执行,比如:我把‘ocalBroadcastManager.registerReceiver( myBroadCastReceiver, intentFilter);’写两遍,那么myBroadCastReceiver的onReceiver会被调用两次,不建议这样写。
2 . 本地广播不能拦截
3. registerReceiver对应的还有unregisterReceiver(receiver)

  /*** 发送本地广播* @param view*/public void sendMyAPPBroadcat(View view){Intent intent = new Intent("my.broadcast.faqian2");//action是my.broadcast.faqian,清单文件中的action与之一致方可收到广播Bundle bundle = new Bundle();bundle.putString("qian","100");//广播中携带的bundle数据intent.putExtra("bundle_data", bundle);//使用LocalBroadcastManager发送广播LocalBroadcastManager.getInstance(MainActivity.this).sendBroadcastSync(intent);//发送}

发送sticky广播

添加权限:

<user-permission android:name="android.permission.BROADCAST_STICKY"/>

发送

context.sendStickyBroadcast()

停止使用

context.removeStickyBroadcast()

如有错误,不吝赐教啊

累死了,北京时间:2016.12.5 凌晨 0:27

BroadcastReceiver详解以及应用相关推荐

  1. Android面试基础之BroadcastReceiver详解(斗帝养成系列四)

    斗帝养成 大斗师,一至九星,斗气铠甲,斗气外放,聚气化固态,呈菱形.别说自己尽力了,那只是自欺欺人的假话. Android面试基础之Activity详解(斗帝养成系列一) Android面试基础之Se ...

  2. BroadcastReceiver详解

    详解 2014-08-20 19:42 13492人阅读 评论(8) 收藏 举报 分类: 5.andriod开发(148) 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 前言 ...

  3. android广播intent原理,Android中BroadcastReceiver详解

    BroadcastReceiver是什么? Android app可以发送广播也可以接收系统或者其它app发送的广播,是发送/订阅的设计模式.这些广播被发送当重要的事件发生的时候.例如,安卓系统发送广 ...

  4. Android中广播接收者BroadcastReceiver详解

    1. 接收系统的广播步骤 (1)  新建一个类继承BroadcastReceiver 以监听sd卡状态的广播接收者为例 1 public class SdCardBroadcastReceiver e ...

  5. android 静态注册wifi广播,Android中BroadcastReceiver详解

    BroadcastReceiver是什么? Android app可以发送广播也可以接收系统或者其它app发送的广播,是发送/订阅的设计模式.这些广播被发送当重要的事件发生的时候.例如,安卓系统发送广 ...

  6. Android BroadcastReceiver详解

    1. 什么是广播 BroadcastReceiver是android四大组件之一,四大组件一般是需要在Manifest.xml清单文件注册,但广播可以在代码中动态注册. 每一个应用都可以注册去接收系统 ...

  7. Android四大组件之BroadcastReceiver详解

    1. 广播机制工作原理 ? 1.1 首先在需要发送信息的地方,把要发送的信息和用于过滤的信息(如Action.Category)装入一个Intent对象 1.2 然后通过调用 Context.send ...

  8. BroadcastReceiver应用详解

    BroadcastReceiver详解 广播的概念 Android:系统在产生某个事件时发送广播,应用程序使用广播接收者接收这个广播,就知道系统产生了什么事件. Android系统在运行的过程中,会产 ...

  9. Android面试基础之ContentProvider详解(斗帝养成系列三)

    斗帝养成 斗师,一至九星,斗气纱衣,聚气化液态. 我匆忙了一生,我却留不下任何东西. Android面试基础之Activity详解(斗帝养成系列一) Android面试基础之Service详解(斗帝养 ...

最新文章

  1. 如何调试程序的后台作业
  2. map怎么转化dto_阿里面试题:为什么Map桶中个数超过8才转为红黑树
  3. 预定义变量$_SERVER
  4. 一次java线程死锁的定位
  5. 句句真研—每日长难句打卡Day10
  6. TFS配置过程中的错误
  7. ActiveMQ官方文档翻译-内嵌消息中间件
  8. java菜鸟教程100_JAVA/JSP学习系列之一
  9. pyecharts可视化画3D动态地球仪,世界地图,中国地图。
  10. 商户如何接入微信支付
  11. Java图片处理:ico格式转PNG/JPG等格式
  12. 学习Python必备的8本书
  13. @Value 注解用法
  14. AM5728-Uboot SPL 代码走读
  15. mysql 连接慢安全狗_服务器安全狗端口安全策略导致微信小程序慢解决办法
  16. Word 文档转变成网页并显示
  17. IOS苹果内购 PHP后端验证票据
  18. 知乎上40个有趣回复,很精辟!
  19. R语言大全(后续更新和优化结构)
  20. win10找不到wifi网络_当WiFi和4G网络齐飞,你的手机恐怕撑不到回家充电了…

热门文章

  1. gis等时圈怎么做_【干货分享】如何一键生成等时圈?
  2. 在linux中启动mysql,在linux中启动mysql服务的命令
  3. UiPath PDF数据提取
  4. python编写一个将十进制转换为二进制的函数
  5. (1)C#与.NET Framework、FCL与CLR
  6. NC客开单据 单据模板初始化后 集团下无模板
  7. php 数字 字母组合,php随机生成数字字母组合的方法
  8. mysql创建表shop_Mysql创建数据库
  9. java拍卖结束_java二手图书拍卖网站
  10. 把显示器挂在机箱侧面板上_电脑机箱侧面的弹性弹簧卡扣有哪些用途?