Android 广播 笔记

一、定义&作用、分类、使用场景

1.定义

Broadcast在Android系统中应用的非常广泛,BroadcastReceiver是四大组件之一,Android中我们要发送的广播内容是一个Intent,这个Intent中可以携带我们要传送的数据。Broadcast可以实现不同程序间的数据传输与共享,只要和发送广播action相同的广播接收者,都可以接收到这个广播,也就是说,发送一个广播可以被很多广播接收者接收,也就是说BroadCastReceiver可以通过监听其它应用程序发送的广播接收传递过来的信息进而实现进程间的通信

2.分类

2.1 从发送方式来说:

Normal Broadcast(普通广播):完全异步的,可以在同一时刻被所有接收者接收到,消息传递的效率比较高,并且无法中断广播的传播,通常调用sendBroadcast(Intent)(Intent, String)方法发送

System Broadcast(系统广播):发生各种事件时,系统自动发送

Ordered Broadcast(有序广播):发送有序广播后,广播接收者将按预先声明的优先级依次接收Broadcast.优先级高的先接收到广播,而在其onRecevier()执行过程中,广播不会传播到下一个接收者,此时当前的广播接收者可以abortBroadcast()来阻止广播继续向下传播。调用sendOrderedBroadcast(Intent, String)方法发送

'Sticky Broadcast'(粘性广播):sendStickyBroadcast()来发送该类型的广播消息,当粘性广播发送后,最后一个粘性广播会滞留在操作系统中,在粘性广播发送后的一段时间里,如果有新的符合广播的动态注册广播接收者,将会收到这个广播消息。对于静态注册的广播接收者来说,这个等同于普通广播。已弃用(API 21)

LocalBroadcastManager本地广播:只在自身App内传播,由LocalBroadcastManager完成

2.2 注册方式

广播的注册方式来分,分为以下2种:
a.静态注册:通过<receiver></receiver>的形式在AndroidManifest.xml中注册的广播;
b.动态注册:通过context. registerReceiver在程序中显示注册的广播;

2.3隐式/显示
与应用程序无直接关系的任何广播都是隐式广播,比如ACTION_PACKAGE_REPLACE是一个隐式广播,因为他会通知手机上每个安装的包。

同样,与您的应用程序直接相关的任何广播都是显示广播

3.场景

a..app全局监听:不同APP之间的组件之间的消息通信。在AndroidManifest中静态注册的广播接收器,一般我们在收到该消息后,需要做一些相应的动作,而这些动作与当前App的组件,比如Activity或者Service的是否运行无关。

b.不同app之间的组件之间消息通信。
比如:Activity或者Service中使用registerReceiver()动态注册的广播接收器。比如网络连接发生变化时,需要在当前Activity页面给用户一些UI 上的提示,或者将Service中的网络请求任务暂停。

二、使用方式

1.注册广播

静态注册:通过在AndroidManifest清单文件中用<receive>进行注册的,注册完成就一直运行,静态注册的广播即使Activity销毁了,甚至进程被杀死了,还是可以收到广播。

 <receiver
    android:name="com.xxxx.receiver.TestBroadcastReceiver"
    android:enabled="true"
    android:process=":push">
    <intent-filter android:priority="0x7fffffff">
        <!-- 系统广播: 开屏 -->
        <action android:name="android.intent.action.USER_PRESENT"/>
        <!-- 系统广播: 网络切换 -->
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
        <!-- 系统广播: 开机 -->
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
        <!-- 系统广播: USB插拔 -->
        <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
        <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
        <!-- 系统广播: 电池 -->
        <action android:name="android.intent.action.BATTERY_CHANGED"/>
    </intent-filter>
</receiver>

动态注册:跟随Activity的生命周期,是在代码中调用registerReceiver来进行注册的,会随着Actvity的销毁而销毁。

IntentFilter _Filter = new IntentFilter();
_Filter.setPriority(0x7fffffff);
//系统广播: 开屏
_Filter.addAction(Intent.ACTION_USER_PRESENT);
//系统广播: 开机
_Filter.addAction(Intent.ACTION_BOOT_COMPLETED);
//系统广播: USB插拔
_Filter.addAction(Intent.ACTION_POWER_CONNECTED);
_Filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
//系统广播: 电池信息变化
_Filter.addAction(Intent.ACTION_BATTERY_CHANGED);
//系统广播: 网络切换
_Filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
mContext.getApplicationContext().registerReceiver(mTestBroadcastReceiver, _Filter);
2.LocalBroadcastManager

LocalBroadcastManager是android support v4包里提供的一个组件,用来在应用内发送广播。

使用方法和和Broadcast基本一致,只需要在Broadcast相关的代码前加上LocalBroadcastManager.getInstance(context)就行了。

发送广播:

LocalBroadcastManager.getInstance(context).sendBroadcast(intent);

注册广播监听器:

LocalBroadcastManager.getInstance(context).registerReceiver(receiver, filter);

注销广播监听器:

LocalBroadcastManager.getInstance(context).unregisterReceiver(receiver);

相比于发送全局广播的sendBroadcast有很多优点:

(1)广播的数据不会离开本身的进程,所以不用担心泄露私人数据;

(2)其他应用程序不可能发广播给你的应用,所以不用担心有安全漏洞会被利用;

(3)相比于经过系统的全局广播更有效率。

与Handler进行比较:

通过线程内的通信用Handler会更方便,所以这种LocalBroadcastManager也是比较少用,不过相比于Handler,LocalBroadcastManager的优势在于如果在通过线程内,多个对象要收到消息,LocalBroadcastReceiver发一次,而Handler则要发多次。

三、实现机制原理

1.设计模式

Android中的广播使用了观察者模式,模型为基于消息的发布/订阅事件模型。

2.模型成员:

消息发布者(广播发布者)
消息订阅者(广播接收者)
消息中心(AMS,Activity Manager Service,一个Android系统中极其重要!的成分,以后我们会详细讲解)

发布订阅模式属于广义上的观察者模式
前者时最常用的一种观察者模式的实现,且从解耦和重用角度上看更优于典型的观察者模式
发布订阅模式加入消息中心,实现发布者和订阅者的解耦:
在观察者模式中,观察者需要直接订阅目标事件,在目标发出内容改变的事件后,直接接收事件并作出响应。
在发布订阅模式中,多了一个消息中心,一方面从发布者接收事件,另一方面向订阅者发布事件,订阅者需要从消息中心订阅事件。以此避免发布者和订阅者之间产生依赖关系。

3.实现流程

广播接收者BroadcastReceiver通过Binder机制向AMS(Activity Manager Service)进行注册;
广播发送者通过binder机制向AMS发送广播;
AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver
AMS将广播发送到上述符合条件的BroadcastReceiver相应的消息循环队列中
BroadcastReceiver通过消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法。
广播发送者和广播接收者的执行是异步的,发出去的广播不会关心有无接收者接收,也不确定接收者到底是何时才能接收到。

四、补充

1.BroadcastReceiver的生命周期

1.动态注册:存活周期是在Context.registerReceiver和Context.unregisterReceiver之间,BroadcastReceiver每次收到广播都是使用注册传入的对象处理的。

2.静态注册:进程在的情况下,receiver会正常收到广播,调用onReceive方法,生命周期只存活在onReveive函数中,此方法结束,BroadcastReceiver就销毁了。进程不存在时,广播相应的进程会被激活,Application.onCreate会被调用,再调用onReceive()

2.unregisterReceiver

Android中所有与观察者模式有关的设计中,一旦涉及到register,必定在相应的时机需要unregister,防止内存泄漏。因此,上例在onDestroy()回调需要unregisterReceiver(mBroadcastReceiver)。

3Android 7.0 更改

Android 7.0起,系统不再发送以下系统广播:
ACTION_NEW_PICTURE
ACTION_NEW_VIDEO
针对Android 7.0 (API级别24)和更高版本的应用程序必须通过registerReceiver()注册以下广播。在AndroidManifest中声明<receiver>起作用。
CONNECTIVITY_ACTION

4.Android 8.0

8.0起,应用无法在Manifest中注册大部分隐式系统广播(即,并非专门针对此应用的广播),此意也是在于降低随Android同时运行的应用增多,发生性能变差的几率。

解决:

优先使用动态注册Receiver的方式,能动态注册绝不使用Manifest注册

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.xxx.receiver");
TheReceiver receiver = new TheReceiver();
registerReceiver(receiver, intentFilter);

如果一定要Manifest注册,那么当发送广播的时候,指定广播接收者的包名,即发送显式广播

如果我们不想发显式广播(因为我们不知道有谁要收广播),对方又不能动态注册,只能静态注册(许多应用希望是被动唤醒),我们应该怎么办呢?
解决办法:
发送广播的时候携带intent.addFlags(0x01000000); 即能让广播突破隐式广播限制。

参见:https://www.jianshu.com/p/5283ebc225d5

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

推荐阅读更多精彩内容