安卓四大组件之—BroadcastReceiver详解

一 概述

BroadcastReceiver是安卓四大组件之一,它是一个全局的监听器,它能够接收安卓系统、App内以及其它App发出的广播。安卓的广播采用观察者模式,基于消息的 订阅 / 发布 事件模型,因此广播的发送者和接收者完全解耦,易于使用和扩展。

二 BroadcastReceiver使用流程

  1. 自定义BroadcastReceiver
    我们要想使用BroadcastReceiver需要先定义个类继承BroadcastReceiver然后实现其public void onReceive(Context context, Intent intent)方法。

广播接收器接收到相应的广播后会自动回调onReceive方法,该方法运行在主线程中,因此不宜做耗时操作。
一般情况下在onReceive方法中会涉及到与其它组件的交互,比如打开Service、启动Activity等。

代码示例

public class MyReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        //获得意图(intent)中传递过来的action
        String action = intent.getAction();
        Log.e(getClass().getSimpleName(),"action:"+action);
        //可以在意图(intent)中使用bundle传递参数
        Bundle extras = intent.getExtras();
        String id = extras.getString("id");
        String name = extras.getString("name");
        Log.e(getClass().getSimpleName(),"id:"+id+",name:"+name);
    }
}
  1. BroadcastReceiver注册
    我们自定义了BroadcastReceiver之后,还需要注册一下才能收到对应的广播消息。广播的注册有两种方式分别为:静态注册和动态注册。
  • 静态注册
    静态注册的方式是直接在xml文件中注册,示例如下:
<receiver
    //广播接收者的类名路径
    android:name=".MyReceiver"
    //发送广播的app应该具有的权限
    android:permission="String"
    //广播是否可用,为true时系统才会初始化该广播
    android:enabled="true"
    //是否能接受其他app发出的广播,当有intent-filter时默认为true
    android:exported="true">
    <intent-filter>
        //接受到的广播类型,可以使用系统已有的广播类型也可以自定义
        <action android:name="com.jrmf360.sendrp"/>
    </intent-filter>
</receiver>

当app启动时系统会自动实例化该广播类,并注册到系统中

  • 动态注册
    动态注册就是在代码中注册,这种方式比较灵活,当用到广播时才去注册且可以反注册,广播不会一直存在,节约内存。看个示例:
@Override
protected void onResume() {
    super.onResume();
    broadcastReceiver = new MyBroadcastReceiver();
    //创建意图过滤器并添加Action
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction("com.jrmf360.sendrp");
    //注册BroadcastReceiver
    registerReceiver(broadcastReceiver,intentFilter);
}


@Override
protected void onPause() {
    super.onPause();
    unregisterReceiver(broadcastReceiver);
}


class MyBroadcastReceiver extends BroadcastReceiver{

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        Log.e(getClass().getSimpleName(),"action:"+action);
        Bundle extras = intent.getExtras();
        String id = extras.getString("id");
        String name = extras.getString("name");
        Log.e(getClass().getSimpleName(),"id:"+id+",name:"+name);
    }
}

在onResume方法中注册BroadcastReceiver在onPause方法中取消注册BroadcastReceiver。注册和反注册必须成对出现以免造成内存泄漏。

  1. 广播种类
    广播分为普通广播、有序广播、系统广播和App内广播,具体说明如下:
  • 普通广播
    开发者自定义的广播,发送广播如下:
//创建意图并设置Action
Intent intent = new Intent();
//只有当广播的接收Action于此匹配才能接收到该广播
intent.setAction("com.jrmf360.sendrp");
//使用bundle传递参数
Bundle bundle = new Bundle();
bundle.putString("id","123456");
bundle.putString("name","zhangsan");
intent.putExtras(bundle);
//发送广播
sendBroadcast(intent);
  • 有序广播
    发送出去的广播按照先后顺序被广播接收者接收,有序是针对广播接收者而言的。
    接收规则:
    按照Priority属性值从大-小排序
    如果Priority属性值相同动态广播优先
    高优先级的广播可以更改广播中的数据然后通过public final void setResult (int code, String data, Bundle extras)方法把更改后的结果传递给下一级广播;高优先级的广播还可以通过调用abortBroadcast()方法拦截广播,后续所有的广播接收者都将收不到该广播。
  • 系统广播
    安卓系统内置了很多系统广播,只要涉及到手机的基本操作基本都会发出对应的广播,例如:开关机、亮屏以及音量变化等。每个广播都有Intent - Filter(包括具体的action),安卓中一些内置的Action如下:
系统操作 action
网络变化 android.net.conn.CONNECTIVITY_CHANGE
充电时或电量发生变化 Intent.ACTION_BATTERY_CHANGED
屏幕被关闭 Intent.ACTION_SCREEN_OFF
屏幕被打开 Intent.ACTION_SCREEN_ON
系统启动完成后(仅广播一次) Intent.ACTION_BOOT_COMPLETED
  • 应用内广播
    当我们使用自定义广播时可能会出现一些问题:
    其它App发出和我们广播接收者相同的Action导致我们不断地收到广播并处理;
    其它App定义和我们相同的广播接收者导致我们的广播信息暴露。
    要解决以上问题我们就需要使用到应用内广播,所谓应用内广播保证了我们App发出的广播在其它App内的广播接收者收不到并且其它App发出的广播我们App内的广播接收者不接收。App内广播的实现有两种方式:
    方式一:
    在注册广播时android:exported="false"这样就接收不到其它App发出的广播了,如下:
<receiver
    //广播接收者的类名路径
    android:name=".MyReceiver"
    //发送广播的app应该具有的权限
    android:permission="com.jrmf360.permission.send"
    //广播是否可用,为true时系统才会初始化该广播
    android:enabled="true"
    //是否能接受其他app发出的广播,当有intent-filter时默认为true
    android:exported="false">
    <intent-filter>
        //接受到的广播类型,可以使用系统已有的广播类型也可以自定义
        <action android:name="com.jrmf360.sendrp"/>
    </intent-filter>
</receiver>

单单接收不到其它App发出的广播还不能满足我们的要求,还需要我们自己发出的广播其它App接收不到,这就需要在发送广播的时候设置包名:

Intent intent = new Intent();
intent.setAction("com.jrmf360.sendrp");
Bundle bundle = new Bundle();
bundle.putString("id","123456");
bundle.putString("name","zhangsan");
intent.putExtras(bundle);
//设置包名
intent.setPackage("应用包名");
sendBroadcast(intent);

方式二:
使用LocalBroadcastManager也可以实现应用内广播,需要注意的是只有动态注册的广播才能使用LocalBroadcastManager并且广播的注册,发送和反注册都要使用LocalBroadcastManager.getInstance(this)的单例对象去完成。示例如下:

@Override
protected void onResume() {
    super.onResume();
    broadcastReceiver = new MyBroadcastReceiver();
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction("com.jrmf360.sendrp");
    LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver,intentFilter);
}


@Override
protected void onPause() {
    super.onPause();
    LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
}


@Override
public void onClick(View v) {
    int id = v.getId();
    if (id == R.id.btn_sendBroadcast){
        Intent intent = new Intent();
        intent.setAction("com.jrmf360.sendrp");
        Bundle bundle = new Bundle();
        bundle.putString("id","123456");
        bundle.putString("name","zhangsan");
        intent.putExtras(bundle);
        sendBroadcast(intent);
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
    }
}
  1. 广播的权限
    广播的权限分为广播发送者要求广播接收者具有的权限和广播接收者要求广播发送者具有的权限。
  • 广播接收者要求广播发送者具有的权限
    在我们在xml文件中注册广播接收者的时候有这样一行属性android:permission="String" 我们可以在此添加对应的权限,这就要求发出广播的App必须拥有此权限,下面演示一下流程:
    先在xml中声明广播接收者并添加权限
<receiver
    android:name=".MyReceiver2"
    //添加自定义权限
    android:permission="com.jrmf360.permission.send2"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="com.jrmf360.sendrp"/>
    </intent-filter>
</receiver>

然后我们需要在xml文件中声明该权限

<!--权限声明-->
<permission android:name="com.jrmf360.permission.send2"/>

最后广播发送者所在的App需要在xml中注册该权限

<uses-permission android:name="com.jrmf360.permission.send2"/>
  • 广播发送者要求广播接收者具有的权限
    首先调用发送广播方法时我们需要传入对应的权限
sendBroadcast(intent,"com.jrmf360.rp");

接着在xml文件中声明该权限

<permission android:name="com.jrmf360.rp"/>

最后广播接收者所在的App需要在xml中注册该权限

<!--权限注册-->
<uses-permission android:name="com.jrmf360.rp"/>

广播的权限是对广播接收者随意接收广播的限制和保护。

总结:广播可以跨线程、进程和App通信,我们在开发过程中可以很方便的利用它来传递消息。但我们也不能滥用广播可能会导致接收消息的地方过多不好维护而且广播接收者相对是个比较重的组件,比如线程间通信我们可以通过Handler解决,没有必要使用广播。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,383评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,522评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,852评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,621评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,741评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,929评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,076评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,803评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,265评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,582评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,716评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,395评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,039评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,027评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,488评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,612评论 2 350

推荐阅读更多精彩内容