详解 Android 中 BroadcastReceiver

要创建自己的BroadcastReceiver对象,我们需要继承android.content.BroadcastReceiver,并实现其onReceive方法。

下面我们就创建一个名为MyReceiver广播接收者:

[java]  view plain copy

  1. package com.scott.receiver;

  2. import android.content.BroadcastReceiver;

  3. import android.content.Context;

  4. import android.content.Intent;

  5. import android.util.Log;

  6. public class MyReceiver extends BroadcastReceiver {

  7. private static final String TAG = “MyReceiver”;

  8. @Override

  9. public void onReceive(Context context, Intent intent) {

  10. String msg = intent.getStringExtra(“msg”);

  11. Log.i(TAG, msg);

  12. }

  13. }

在onReceive方法内,我们可以获取随广播而来的Intent中的数据,这非常重要,就像无线电一样,包含很多有用的信息。

在创建完我们的BroadcastReceiver之后,还不能够使它进入工作状态,我们需要为它注册一个指定的广播地址。没有注册广播地址的BroadcastReceiver就像一个缺少选台按钮的收音机,虽然功能俱备,但也无法收到电台的信号。下面我们就来介绍一下如何为BroadcastReceiver注册广播地址。

静态注册

========

静态注册是在AndroidManifest.xml文件中配置的,我们就来为MyReceiver注册一个广播地址:

[html]  view plain copy

  1. <receiver android:name=“.MyReceiver”>

  2. <intent-filter>

  3. <action android:name=“android.intent.action.MY_BROADCAST”/>

  4. <category android:name=“android.intent.category.DEFAULT” />

  5. </intent-filter>

  6. </receiver>

配置了以上信息之后,只要是android.intent.action.MY_BROADCAST这个地址的广播,MyReceiver都能够接收的到。注意,这种方式的注册是常驻型的,也就是说当应用关闭后,如果有广播信息传来,MyReceiver也会被系统调用而自动运行。

动态注册

========

动态注册需要在代码中动态的指定广播地址并注册,通常我们是在Activity或Service注册一个广播,下面我们就来看一下注册的代码:

[java]  view plain copy

  1. MyReceiver receiver = new MyReceiver();

  2. IntentFilter filter = new IntentFilter();

  3. filter.addAction(“android.intent.action.MY_BROADCAST”);

  4. registerReceiver(receiver, filter);

注意,registerReceiver是android.content.ContextWrapper类中的方法,Activity和Service都继承了ContextWrapper,所以可以直接调用。在实际应用中,我们在Activity或Service中注册了一个BroadcastReceiver,当这个Activity或Service被销毁时如果没有解除注册,系统会报一个异常,提示我们是否忘记解除注册了。所以,记得在特定的地方执行解除注册操作:

[java]  view plain copy

  1. @Override

  2. protected void onDestroy() {

  3. super.onDestroy();

  4. unregisterReceiver(receiver);

  5. }

执行这样行代码就可以解决问题了。注意,这种注册方式与静态注册相反,不是常驻型的,也就是说广播会跟随程序的生命周期。

我们可以根据以上任意一种方法完成注册,当注册完成之后,这个接收者就可以正常工作了。我们可以用以下方式向其发送一条广播:

[java]  view plain copy

  1. public void send(View view) {

  2. Intent intent = new Intent(“android.intent.action.MY_BROADCAST”);

  3. intent.putExtra(“msg”, “hello receiver.”);

  4. sendBroadcast(intent);

  5. }

注意,sendBroadcast也是android.content.ContextWrapper类中的方法,它可以将一个指定地址和参数信息的Intent对象以广播的形式发送出去。

点击发送按钮,执行send方法,控制台打印如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

看到这样的打印信息,表明我们的广播已经发出去了,并且被MyReceiver准确无误的接收到了。

上面的例子只是一个接收者来接收广播,如果有多个接收者都注册了相同的广播地址,又会是什么情况呢,能同时接收到同一条广播吗,相互之间会不会有干扰呢?这就涉及到普通广播和有序广播的概念了。

普通广播(Normal Broadcast)

==========================

普通广播对于多个接收者来说是完全异步的,通常每个接收者都无需等待即可以接收到广播,接收者相互之间不会有影响。对于这种广播,接收者无法终止广播,即无法阻止其他接收者的接收动作。

为了验证以上论断,我们新建三个BroadcastReceiver,演示一下这个过程,FirstReceiver、SecondReceiver和ThirdReceiver的代码如下:

[java]  view plain copy

  1. package com.scott.receiver;

  2. import android.content.BroadcastReceiver;

  3. import android.content.Context;

  4. import android.content.Intent;

  5. import android.util.Log;

  6. public class FirstReceiver extends BroadcastReceiver {

  7. private static final String TAG = “NormalBroadcast”;

  8. @Override

  9. public void onReceive(Context context, Intent intent) {

  10. String msg = intent.getStringExtra(“msg”);

  11. Log.i(TAG, "FirstReceiver: " + msg);

  12. }

  13. }

[java]  view plain copy

  1. public class SecondReceiver extends BroadcastReceiver {

  2. private static final String TAG = “NormalBroadcast”;

  3. @Override

  4. public void onReceive(Context context, Intent intent) {

  5. String msg = intent.getStringExtra(“msg”);

  6. Log.i(TAG, "SecondReceiver: " + msg);

  7. }

  8. }

[java]  view plain copy

  1. public class ThirdReceiver extends BroadcastReceiver {

  2. private static final String TAG = “NormalBroadcast”;

  3. @Override

  4. public void onReceive(Context context, Intent intent) {

  5. String msg = intent.getStringExtra(“msg”);

  6. Log.i(TAG, "ThirdReceiver: " + msg);

  7. }

  8. }

然后再次点击发送按钮,发送一条广播,控制台打印如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

看来这三个接收者都接收到这条广播了,我们稍微修改一下三个接收者,在onReceive方法的最后一行添加以下代码,试图终止广播:

[java]  view plain copy

  1. abortBroadcast();

再次点击发送按钮,我们会发现,控制台中三个接收者仍然都打印了自己的日志,表明接收者并不能终止广播。

有序广播(Ordered Broadcast)

===========================

有序广播比较特殊,它每次只发送到优先级较高的接收者那里,然后由优先级高的接受者再传播到优先级低的接收者那里,优先级高的接收者有能力终止这个广播。

为了演示有序广播的流程,我们修改一下上面三个接收者的代码,如下:

[java]  view plain copy

  1. package com.scott.receiver;

  2. import android.content.BroadcastReceiver;

  3. import android.content.Context;

  4. import android.content.Intent;

  5. import android.os.Bundle;

  6. import android.util.Log;

  7. public class FirstReceiver extends BroadcastReceiver {

  8. private static final String TAG = “OrderedBroadcast”;

  9. @Override

  10. public void onReceive(Context context, Intent intent) {

  11. String msg = intent.getStringExtra(“msg”);

  12. Log.i(TAG, "FirstReceiver: " + msg);

  13. Bundle bundle = new Bundle();

  14. bundle.putString(“msg”, msg + “@FirstReceiver”);

  15. setResultExtras(bundle);

  16. }

  17. }

[java]  view plain copy

  1. public class SecondReceiver extends BroadcastReceiver {

  2. private static final String TAG = “OrderedBroadcast”;

  3. @Override

  4. public void onReceive(Context context, Intent intent) {

  5. String msg = getResultExtras(true).getString(“msg”);

  6. Log.i(TAG, "SecondReceiver: " + msg);

  7. Bundle bundle = new Bundle();

  8. bundle.putString(“msg”, msg + “@SecondReceiver”);

  9. setResultExtras(bundle);

  10. }

  11. }

[java]  view plain copy

  1. public class ThirdReceiver extends BroadcastReceiver {

  2. private static final String TAG = “OrderedBroadcast”;

  3. @Override

  4. public void onReceive(Context context, Intent intent) {

  5. String msg = getResultExtras(true).getString(“msg”);

  6. Log.i(TAG, "ThirdReceiver: " + msg);

  7. }

  8. }

我们注意到,在FirstReceiver和SecondReceiver中最后都使用了setResultExtras方法将一个Bundle对象设置为结果集对象,传递到下一个接收者那里,这样以来,优先级低的接收者可以用getResultExtras获取到最新的经过处理的信息集合。

代码改完之后,我们需要为三个接收者注册广播地址,我们修改一下AndroidMainfest.xml文件:

[html]  view plain copy

  1. <receiver android:name=“.FirstReceiver”>

  2. <intent-filter android:priority=“1000”>

  3. <action android:name=“android.intent.action.MY_BROADCAST”/>

  4. <category android:name=“android.intent.category.DEFAULT” />

  5. </intent-filter>

  6. </receiver>

  7. <receiver android:name=“.SecondReceiver”>

  8. <intent-filter android:priority=“999”>

  9. <action android:name=“android.intent.action.MY_BROADCAST”/>

  10. <category android:name=“android.intent.category.DEFAULT” />

  11. </intent-filter>

  12. </receiver>

  13. <receiver android:name=“.ThirdReceiver”>

  14. <intent-filter android:priority=“998”>

  15. <action android:name=“android.intent.action.MY_BROADCAST”/>

  16. <category android:name=“android.intent.category.DEFAULT” />

  17. </intent-filter>

  18. </receiver>

我们看到,现在这三个接收者的多了一个android:priority属性,并且依次减小。这个属性的范围在-1000到1000,数值越大,优先级越高。

现在,修改一下发送广播的代码,如下:

[java]  view plain copy

  1. public void send(View view) {

  2. Intent intent = new Intent(“android.intent.action.MY_BROADCAST”);

  3. intent.putExtra(“msg”, “hello receiver.”);

  4. sendOrderedBroadcast(intent, “scott.permission.MY_BROADCAST_PERMISSION”);

  5. }

注意,使用sendOrderedBroadcast方法发送有序广播时,需要一个权限参数,如果为null则表示不要求接收者声明指定的权限,如果不为null,则表示接收者若要接收此广播,需声明指定权限。这样做是从安全角度考虑的,例如系统的短信就是有序广播的形式,一个应用可能是具有拦截垃圾短信的功能,当短信到来时它可以先接受到短信广播,必要时终止广播传递,这样的软件就必须声明接收短信的权限。

所以我们在AndroidMainfest.xml中定义一个权限:

[html]  view plain copy

  1. <permission android:protectionLevel=“normal”

  2. android:name=“scott.permission.MY_BROADCAST_PERMISSION” />

然后声明使用了此权限:

[html]  view plain copy

  1. <uses-permission android:name=“scott.permission.MY_BROADCAST_PERMISSION” />

然后我们点击发送按钮发送一条广播,控制台打印如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们看到接收是按照顺序的,第一个和第二个都在结果集中加入了自己的标记,并且向优先级低的接收者传递下去。

既然是顺序传递,试着终止这种传递,看一看效果如何,我们修改FirstReceiver的代码,在onReceive的最后一行添加以下代码:

[java]  view plain copy

  1. abortBroadcast();

然后再次运行程序,控制台打印如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

此次,只有第一个接收者执行了,其它两个都没能执行,因为广播被第一个接收者终止了。

上面就是BroadcastReceiver的介绍,下面我将会举几个常见的例子加深一下大家对广播的理解和应用:

1.开机启动服务

我们经常会有这样的应用场合,比如消息推送服务,需要实现开机启动的功能。要实现这个功能,我们就可以订阅系统“启动完成”这条广播,接收到这条广播后我们就可以启动自己的服务了。我们来看一下BootCompleteReceiver和MsgPushService的具体实现:

[java]  view plain copy

  1. package com.scott.receiver;

  2. import android.content.BroadcastReceiver;

  3. import android.content.Context;

  4. import android.content.Intent;

  5. import android.util.Log;

  6. public class BootCompleteReceiver extends BroadcastReceiver {

  7. private static final String TAG = “BootCompleteReceiver”;

  8. @Override

  9. public void onReceive(Context context, Intent intent) {

  10. Intent service = new Intent(context, MsgPushService.class);

  11. context.startService(service);

  12. Log.i(TAG, “Boot Complete. Starting MsgPushService…”);

  13. }

  14. }

[java]  view plain copy

  1. package com.scott.receiver;

  2. import android.app.Service;

  3. import android.content.Intent;

  4. import android.os.IBinder;

  5. import android.util.Log;

  6. public class MsgPushService extends Service {

  7. private static final String TAG = “MsgPushService”;

  8. @Override

  9. public void onCreate() {

  10. super.onCreate();

  11. Log.i(TAG, “onCreate called.”);

  12. }

  13. @Override

  14. public int onStartCommand(Intent intent, int flags, int startId) {

  15. Log.i(TAG, “onStartCommand called.”);

  16. return super.onStartCommand(intent, flags, startId);

  17. }

  18. @Override

  19. public IBinder onBind(Intent arg0) {

  20. return null;

  21. }

  22. }

然后我们需要在AndroidManifest.xml中配置相关信息:

[html]  view plain copy

  1.   
  2. <receiver android:name=“.BootCompleteReceiver”>

  3. <intent-filter>

先自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以扫码领取!!!!

作者2013年从java开发,转做Android开发,在小厂待过,也去过华为,OPPO等大厂待过,18年四月份进了阿里一直到现在。

参与过不少面试,也当面试官 面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长,而且极易碰到天花板技术停滞不前!

我整理了一份阿里P7级别的最系统的Android开发主流技术,特别适合有3-5年以上经验的小伙伴深入学习提升。

主要包括阿里,以及字节跳动,腾讯,华为,小米,等一线互联网公司主流架构技术。如果你想深入系统学习Android开发,成为一名合格的高级工程师,可以收藏一下这些Android进阶技术选型

我搜集整理过这几年阿里,以及腾讯,字节跳动,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。

Java语言与原理;
大厂,小厂。Android面试先看你熟不熟悉Java语言

高级UI与自定义view;
自定义view,Android开发的基本功。

性能调优;
数据结构算法,设计模式。都是这里面的关键基础和重点需要熟练的。

NDK开发;
未来的方向,高薪必会。

前沿技术;
组件化,热升级,热修复,框架设计

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

我在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多,CodeChina上可见;

当然,想要深入学习并掌握这些能力,并不简单。关于如何学习,做程序员这一行什么工作强度大家都懂,但是不管工作多忙,每周也要雷打不动的抽出 2 小时用来学习。

不出半年,你就能看出变化!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可免费领取!

-1711299555512)]

Java语言与原理;
大厂,小厂。Android面试先看你熟不熟悉Java语言

[外链图片转存中…(img-khlbDduT-1711299555512)]

高级UI与自定义view;
自定义view,Android开发的基本功。

[外链图片转存中…(img-zj3lcZ50-1711299555512)]

性能调优;
数据结构算法,设计模式。都是这里面的关键基础和重点需要熟练的。

[外链图片转存中…(img-xth07TuT-1711299555513)]

NDK开发;
未来的方向,高薪必会。

[外链图片转存中…(img-eWSJZsPN-1711299555513)]

前沿技术;
组件化,热升级,热修复,框架设计

[外链图片转存中…(img-iZ0EY6YV-1711299555513)]

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

我在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多,CodeChina上可见;

当然,想要深入学习并掌握这些能力,并不简单。关于如何学习,做程序员这一行什么工作强度大家都懂,但是不管工作多忙,每周也要雷打不动的抽出 2 小时用来学习。

不出半年,你就能看出变化!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可免费领取!

  • 23
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值