Android四大组件学习之BroadcastReceiver

广播的相关概念

BroadcastReceiver也就是“广播接收者”的意思,顾名思义,它就是用来接收来自系统和应用中的广播。
在Android系统中,广播体现在方方面面,例如当开机完成后系统会产生一条广播,接收到这条广播就能实现开机启动服务的功能;当网络状态改变时系统会产生一条广播,接收到这条广播就能及时地做出提示和保存数据等操作;当电池电量改变时,系统会产生一条广播,接收到这条广播就能在电量低时告知用户及时保存进度等。
虽然同属Android的四大组件,BroadcastReceiver也有自己独立的声明周期,但是和Activity、Service又不同。当在系统注册一个BroadcastReceiver之后,每次系统以一个Intent的形式发布Broadcast的时候,系统都会创建与之对应的BroadcastReceiver广播接收者实例,并自动触发它的onReceive()方法,当onReceive()方法被执行完成之后,BroadcastReceiver的实例就会被销毁。虽然它独自享用一个单独的进程,但也不是没有限制的,如果BroadcastReceiver.onReceive()方法不能在10秒内执行完成,Android系统就会认为该BroadcastReceiver对象无响应,然后弹出ANR(Application No Response)对话框,所以不要在BroadcastReceiver.onReceive()方法内执行一些耗时的操作。所以在BroadCast中不宜做一些耗时的操作。

Broadcast的注册方法

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

    <receiver android:name=".MyReceiver">
    <intent-filter>
    <action android:name="android.intent.action.TEST_BROADCAST"/>
    <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
    </receiver>
    通过定义Receiver的action,只要是android.intent.action.XXX这个地址的广播,MyReceiver都能够接收得到,这种方式的注册是常驻型的,也就是说当应用关闭后,如果有广播信息传来,MyReceiver也会被系统调用而自动运行。

  • 动态注册
    动态注册需要在代码中动态的指定广播地址并注册,通常我们是在Activity或Service注册一个广播。

    MyReceiver receiver = new MyReceiver();

    IntentFilter filter = new IntentFilter();
    filter.addAction("android.intent.action.TEST_BROADCAST");

    registerReceiver(receiver, filter);

registerReceiver是android.content.ContextWrapper类中的方法,Activity和Service都继承了ContextWrapper,所以可以直接调用。在实际应用中,我们在Activity或Service中注册了一个BroadcastReceiver,当这个Activity或Service被销毁时如果没有解除注册,系统会报一个异常,
因此我们通常需要在onDestroy方法里释放广播。
注册完广播接收器后,我们就可以发送一条广播了:

 Intent intent = new Intent("android.intent.action.TEST_BROADCAST");  
    intent.putExtra("msg", "hello receiver.");  
    sendBroadcast(intent);  
}

这样,action为TEST_BROADCAST的接收器都可以收到这条广播。

广播的种类

上面的例子只是一个接收者来接收广播,如果有多个接收者都注册了相同的广播地址,这就要看广播的种类来安排接受顺序了。

  • 普通广播(Normal Broadcast)
    Normal Broadcast,它是完全异步的,也就是说,在逻辑上,当一个Broadcast被发出之后,所有的与之匹配的BroadcastReceiver都同时接收到Broadcast。优点是传递效率比较高,但是也有缺点,就是一个BroadcastReceiver不能影响其他响应这条Broadcast的BroadcastReceiver。

  • 有序广播
    Ordered Broadcast,它是同步执行的,也就是说有序广播的接收器将会按照预先声明的优先级依次接受Broadcast,是链式结构,优先级越高(-1000~1000),越先被执行。因为是顺序执行,所有优先级高的接收器,可以把执行结果传入下一个接收器中,也可以终止Broadcast的传播(通过abortBroadcast()方法),一旦Broadcast的传播被终止,优先级低于它的接收器就不会再接收到这条Broadcast了。
    在接受有序广播时,BroadCastReceiver需要设置优先级, 即 android:priority="886" 属性,这个属性的范围在-1000到1000,数值越大,优先级越高。
    然后使用sendOrderedBroadcast方法来发送有序广播,这个方法需要一个权限参数,如果为null则表示不要求接收者声明指定的权限,如果不为null,则表示接收者若要接收此广播,需声明指定权限。

系统常见的广播:

  • android.intent.action.TIME_SET:系统时间被修改。
  • android.intent.action.DATE_CHANGED:系统日期被修改。
  • android.intent.action.BOOT_COMPLETED:系统启动完成。
  • android.intent.action.BATTERY_CHANGED:设备电量改变。
  • android.intent.action.BATTERY_LOW:设备电量低。
  • android.intent.action.ACTION_POWER_CONNECTED:设备连接电源。
  • android.intent.action.ACTION_POWER_DISCONNECTED:设备断开电源。
  • android.provider.Telephony.SMS_RECEIVED:系统收到短信。
  • android.intent.action.NEW_OUTGOING_CALL:拨打电话。

广播使用示例

从上面列举的一些动作会发布的Broadcast,可以找到,当系统接收到一条短信的时候,会发布一个“android.provider.Telephony.SMS_RECEIVED”的Broadcast,之前已经介绍过了,一般系统Broadcast都是有序广播,如果不被高优先级的BroadcastReceiver停止传递,会按照优先级顺序传递下去。

而在这个示例中,通过监听接收短信的广播,当其内容有黑名单中的关键字的话,则阻止Broadcast继续传播,并使用Toast提示,否则正常提示短信信息。从上面列举的一些动作会发布的Broadcast,可以找到,当系统接收到一条短信的时候,会发布一个“android.provider.Telephony.SMS_RECEIVED”的Broadcast,一般系统Broadcast都是有序广播,如果不被高优先级的BroadcastReceiver停止传递,会按照优先级顺序传递下去。

而在这个示例中,通过监听接收短信的广播,当其内容有黑名单中的关键字的话,则阻止Broadcast继续传播,并使用Toast提示,否则正常提示短信信息。

public class MessageBroadcast extends BroadcastReceiver {

    private final String[] blackKeyWord = new String[] { "Message1", "Message2",
            "Message3" };
    @Override
    public void onReceive(Context context, Intent intent) {
        // 判断当前接收到的Broadcast是否是收到短信的action
        if (intent.getAction()
                .equals("android.provider.Telephony.SMS_RECEIVED")) {
            StringBuilder sb = new StringBuilder();
            // 获取Broadcast传递的数据
            Bundle bundle = intent.getExtras();
            if (bundle != null) {
                Object[] pdus = (Object[]) bundle.get("pdus");
                for (Object p : pdus) {
                    byte[] pud = (byte[]) p;
                    // 声明一个SmsMessage,用于解析短信的byte[]数组
                    SmsMessage message = SmsMessage.createFromPdu(pud);
                    boolean flag = false;
                    for (String str : blackKeyWord) {
                        if (message.getMessageBody().contains(str) ) {
                            // 发现黑名单关键字,则标记为true
                            flag = true;
                            break;
                        }
                    }
                    if (flag) {
                        sb.append("发件人:\n");
                        sb.append(message.getOriginatingAddress());
                        sb.append("\n发送时间:\n");
                        Date date = new Date(message.getTimestampMillis());
                        SimpleDateFormat format = new SimpleDateFormat(
                                "yyyy-MM-dd HH:mm:ss");
                        sb.append(format.format(date));
                        sb.append("\n短信内容:\n");
                        sb.append(message.getMessageBody());
                        Toast.makeText(context, sb.toString(),
                                Toast.LENGTH_SHORT).show();
                    else{
                        // 如果存在黑名单关键字内容,停止Broadcast传播
                        abortBroadcast();
                    }
                        
                    }
                }
            }
        }
    }
}

AndroidManifest.xml:

<receiver android:name="cn.bgxt.Broadcastdemo.MessageWarn.MessageBroadcast">
<!-- 设置优先级,短信优先级为0,大于0即可 -->
<intent-filter android:priority="200">
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>

下一篇:
Android四大组件学习之Service

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

推荐阅读更多精彩内容