BroadcastReceiver 详细解析

广播是一种广泛运用的在应用程序之间传输信息的机制,主要用来监听系统或者应用发出的广播信息,然后根据广播信息作为相应的逻辑处理,也可以用来传输少量、频率低的数据。

在实现开机启动服务和网络状态改变、电量变化、短信和来电时通过接收系统的广播让应用程序作出相应的处理。

BroadcastReceiver 自身并不实现图形用户界面,但是当它收到某个通知后, BroadcastReceiver 可以通过启动 Service 、启动 Activity 或是 NotificationMananger 提醒用户。

使用广播的注意事项

当系统或应用发出广播时,将会扫描系统中的所有广播接收者,通过 action 匹配将广播发送给相应的接收者,接收者收到广播后将会产生一个广播接收者的实例,执行其中的 onReceiver() 这个方法;特别需要注意的是这个实例的生命周期只有10秒,如果10秒内没执行结束 onReceiver() ,系统将会报错。在 onReceiver() 执行完毕之后,该实例将会被销毁,所以不要在 onReceiver() 中执行耗时操作,也不要在里面创建子线程处理业务(因为可能子线程没处理完,接收者就被回收了,那么子线程也会跟着被回收掉);正确的处理方法就是通过 intent 调用 Activity 或者 Service 处理业务。

BroadcastReceiver的注册

BroadcastReceiver 的注册方式有且只有两种,一种是静态注册(推荐使用),另外一种是动态注册,广播接收者在注册后就开始监听系统或者应用之间发送的广播消息。

下面通过例子看一下两种注册方式,先定义一个接收短信的 BroadcastReceiver 类:

public class MyBroadcastReceiver extends BroadcastReceiver {
    // action 名称
    String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED" ;
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals( SMS_RECEIVED )) {
            // 一个receiver可以接收多个action的,即可以有
            // 多个intent-filter,需要在 onReceive
            // 里面对 intent.getAction(action name) 进行判断。
            ...
        }
    }
}
  • 静态注册

    在 AndroidManifest.xml 的 application 里面定义 receiver 并设置要接收的action。

    < receiver android:name = ".MyBroadcastReceiver" > 
        < intent-filter android:priority = "777" >       
            <action android:name = "android.provider.Telephony.SMS_RECEIVED" />
        </ intent-filter > 
    </ receiver >
    

    这里的 priority 取值是 -1000 到 1000 ,值越大优先级越高,同时注意加上系统接收短信的限权。

    静态注册的广播接收者是一个常驻在系统中的全局监听器,当你在应用中配置了一个静态的 BroadcastReceiver ,安装了应用后而无论应用是否处于运行状态,广播接收者都是已经常驻在系统中了。同时应用里的所有 receiver 都在清单文件里面,方便查看。要销毁掉静态注册的广播接收者,可以通过调用 PackageManager 将 Receiver 禁用。

  • 动态注册

    在 Activity 中声明 BroadcastReceiver 的扩展对象,在 onResume 中注册,onPause 中卸载.

    public class MainActivity extends Activity {
        MyBroadcastReceiver receiver;
    
        @Override
        protected void onResume() {
            // 动态注册广播 (代码执行到这才会开始监听广播消息,并对广播消息作为相应的处理)
            receiver = new MyBroadcastReceiver();
            IntentFilter intentFilter = new IntentFilter( "android.provider.Telephony.SMS_RECEIVED" );
            registerReceiver( receiver , intentFilter); 
            super.onResume();
        }
    
        @Override
        protected void onPause() { 
            // 撤销注册 (撤销注册后广播接收者将不会再监听系统的广播消息)
            unregisterReceiver(receiver);
            super.onPause();
        }
    }
    
  • 静态注册和动态注册的区别

    1. 静态注册的广播接收者一经安装就常驻在系统之中,不需要重新启动唤醒接收者;动态注册的广播接收者随着应用的生命周期,由 registerReceiver 开始监听,由 unregisterReceiver 撤销监听,如果应用退出后,没有撤销已经注册的接收者应用应用将会报错。

    2. 当广播接收者通过 intent 启动一个 activity 或者 service 时,如果 intent 中无法匹配到相应的组件。动态注册的广播接收者将会导致应用报错,而静态注册的广播接收者将不会有任何报错,因为自从应用安装完成后,广播接收者跟应用已经脱离了关系。

发送广播主要类型

  • 普通广播

    普通广播是完全异步的,可以在同一时刻(逻辑上)被所有接收者接收到,所有满足条件的 BroadcastReceiver 都会随机地执行其 onReceive() 方法。

    同级别接收是先后是随机的;级别低的收到广播;消息传递的效率比较高,并且无法中断广播的传播。

    Intent intent = new Intent("android.provider.Telephony.SMS_RECEIVED"); 
    //通过intent传递少量数据
    intent.putExtra("data", "finch"); 
    // 发送普通广播
    sendBroadcast(Intent);
    
  • 有序广播

    有序广播通过 Context.sendOrderedBroadcast() 来发送,所有的广播接收器优先级依次执行,广播接收器的优先级通过 receiver 的 intent-filter 中的 android:priority 属性来设置,数值越大优先级越高。

    当广播接收器接收到广播后,可以使用 setResult() 函数来结果传给下一个广播接收器接收,然后通过 getResult() 函数来取得上个广播接收器接收返回的结果。

    当广播接收器接收到广播后,也可以用 abortBroadcast() 函数来让系统拦截下来该广播,并将该广播丢弃,使该广播不再传送到别的广播接收器接收。

  • 本地广播

    在 API21 的 Support v4 包中新增本地广播,也就是 LocalBroadcastManager 。由于之前的广播都是全局的,所有应用程序都可以接收到,这样就会带来安全隐患,所以我们使用 LocalBroadcastManager 只发送给自己应用内的信息广播,限制在进程内使用。

    它的用法很简单,只需要把调用 context 的 sendBroadcast、registerReceiver、unregisterReceiver 的地方换为 LocalBroadcastManager.getInstance(Context context)中对应的函数即可。

    这里创建广播的过程和普通广播是一样的过程,这里就不过多介绍了

  • 系统广播

    当然系统中也会有很多自带的广播,当符合一定条件时,系统会发送一些定义好的广播,比如:重启、充电、来电电话等等。我们可以通过action属性来监听我们的系统广播,创建广播的过程和普通广播是一样的过程,这里就不过多介绍了。

    常用的广播action属性有:

    • 屏幕被关闭之后的广播:Intent.ACTION_SCREEN_OFF

    • 屏幕被打开之后的广播:Intent.ACTION_SCREEN_ON

    • 充电状态,或者电池的电量发生变化:Intent.ACTION_BATTERY_CHANGED

    • 关闭或打开飞行模式时的广播:Intent.ACTION_AIRPLANE_MODE_CHANGED

    • 表示电池电量低:Intent.ACTION_BATTERY_LOW

    • 表示电池电量充足,即电池电量饱满时会发出广播:Intent.ACTION_BATTERY_OKAY

    • 按下照相时的拍照按键(硬件按键)时发出的广播:Intent.ACTION_CAMERA_BUTTON

    值得注意的是,随着系统版本的提升,很多系统广播已经不再被支持,如果你需要使用哪个系统广播,最好看看最新版本的支持情况。

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

推荐阅读更多精彩内容