Broadcast Receiver总结


这是四大组件的第一篇(其他还没整理好:) ),之前有个习惯,就是把一些笔记记在书上,但是随着书越来越多,翻阅的时候比较麻烦,尤其是一段时间不用之后,想要翻阅某个知识点太费劲,这里就打算统一整理在一起,方便查看。

其中有很多内容参照了网上的博文,但是时间比较久,忘记出处了。另外就是参照Android developer的相关文档,加上自己的理解,如果有错误,欢迎指出啊。

应用场景

  • 同一APP内部的同一组件内的消息通信(单个或多个线程之间)

  • 同一APP内部的不同组件间的消息通信(单个进程)

  • 同一APP内部的具有不同进程的多个组件间的消息通信(多个进程)

  • 不同APP组件间的消息通信(多个进程)

  • Android系统与APP之间的消息通信(系统广播)

分类

1. 普通广播

<pre>
public abstract void sendBroadcast (Intent intent, String receiverPermission)

public abstract void sendBroadcast (Intent intent)
</pre>
所有receiver都是无序的,各receiver往往同时运行。相对来说更为高效,但各receiver无法终止广播或使用其他广播执行的结果。

2. 有序广播

<pre>
public abstract void sendOrderedBroadcast (Intent intent, String receiverPermission)

public abstract void sendOrderedBroadcast (Intent intent,
String receiverPermission,
BroadcastReceiver finalResultReceiver,
Handler scheduler,
int initialCode,
String initialData,
Bundle initialExtras)
</pre>

finalResultReceiver作为最末尾的receiver,可以得到前面一系列receiver的处理结果。通常应该提供自定义的receiver。

scheduler一般设为null,说明使用context的主线程。

initialCode一般设为RESULT_OK,作为resultCode的初始值。

initialData一般设为null。作为resultData的初始值。

initialExtras一般设为null,作为resultExtras的初始值。

通过Context.sendOrderedBroadcast发送,receiver会按照android:priority所指定的优先级由大到小依次执行(android:priority范围为-1000~1000,数值越大优先级越高,默认优先级为0),由于是有序传播,可以实现如下效果:

  • 终止传播:通过调用abortBroadcast,之后的receiver将接收不到该广播

  • 向后继者传递数据:在onReceive中可以调用setResult、setResultCode、setResultData/setResultExtras设置信息,后继者可以通过getResultCode、getResultData、getResultExtra来获取信息。sendOrderedBroadcast中的参数initialCode、initialData、initialExtras提供了初始值。

注意:当静态注册与动态注册使用了相同的优先级(priority)时,动态注册的receiver处于更优先的位置。

安全方面的考虑

1. 隐患

  • 其他APP可能会针对性的发出与当前APP中intent-filter相匹配的广播,导致当前APP不断受到广播并处理。

  • 其他APP可能注册与当前APP一致的intent-filter用于接收广播,从而截获了广播的具体信息

2. 措施

  • 如果receiver是用于同一APP内部的,则直接将其exported设为false

  • 在发送广播时,使用含有permission的版本

  • 在动态注册receiver时,使用含有permission的版本

  • 在静态注册receiver时,在xml中增加permission字段

  • 在发送广播时,可以指定receiver的包名。通过intent.setPackage(packageName)

3. 更便捷的方式——使用LocalBroadcastManager

  • 获取单例
    <pre>
    static LocalBroadcastManager getInstance(Context context);
    </pre>

  • 注册
    <pre>
    void registerReceiver(BroadcastReceiver receiver, IntentFilter filter);
    </pre>

  • 注销
    <pre>
    void unregisterReceiver(BroadcastReceiver receiver);
    </pre>

  • 发送广播
    <pre>
    boolean sendBroadcast(Intent intent);
    </pre>

  • 发送广播(同步发送,会阻塞直至所有相关receiver执行完毕onReceive并返回)
    <pre>
    void sendBroadcastSync(Intent intent);
    </pre>

注册方式

1. 静态注册

通过xml文件的形式进行注册,此种方式注册的receiver会在APP运行期间一直存在,当APP被kill后就接收不到了。此种方式更为常用。

通过静态注册方式注册的receiver,其onReceive(Context context, Intent intent)中的context为ReceiverRestrictedContext(Context含有的bindService和registerReceiver函数被禁用)

注意:当BroadcastReceiver作为内部类被实现,同时又使用了静态注册的方式,那么该内部类必须声明为“public static

注意:从4.0开始,需要至少启动一次APP后,静态注册的receiver才算注册完毕

2. 动态注册

通过代码的方式进行注册,BroadcastReceiver可实现为内部类或一般的外部类。

一般情况下,可以在onResume注册receiver,在onPause中注销receiver(少数情况下,也可以在onCreate中注册,在onDestory中注销)

通过动态注册方式注册的receiver,其onReceive(Context context, Intent intent)中的context为Activity的Context。

3. LocalBroadcastManager的动态注册

如果广播只是在APP内部进行收发,那么更高效和安全的方式是使用LocalBroadcastManager。这种方式只能够进行动态注册。

采用此种方式注册的receiver,其onReceive(Context context, Intent intent)中的context为Application的Context。

生命周期

receiver的生命周期

receiver对象只有在onReceive的调用期间是有效的,是“活跃”的,一旦从onReceive中返回,那么系统将认为该对象已经结束,变为“不活跃”状态。

任何需要异步的操作都不应该放在onReceive中,因为当异步操作结束时,receiver可能已经处于“不活跃”状态,不能保证对象是否还存在。

注意:不能够在onReceive中显示对话框,可行的替代方案是使用NotificationManager相关功能。

注意:不能够在onReceive中绑定服务(bindService),可行的替代方案是通过startService发送命令。

process的生命周期

正在执行receiver中onReceive的代码的process被认为是“前台进程”,它会一直保持运行(除非遇到非常极端的内存方面的压力才会被kill,这一般不会出现)

一旦从onReceive中返回,receiver变为“不活跃”状态,它所在的process的重要性将取决于运行在该process中的其他组件。如果这个process没有其他组件在运行(比如一个APP,用户近期没有与之交互过),那么系统将认为这是一个“空process”,会在合适的时机kill掉它。

产生的问题:如果在onReceive中产生一个thread,然后返回,整个进程、包括新产生的thread,都不认定为“不活跃”的,存在被kill的危机。解决的方式是与service相结合,在onReceive中启动一个Service,让Service去做具体的工作,由于Service的存在,当前的process的重要性取决于Service的状态,只要Service执行的工作未完成,它就一直是“活跃”的,就不会被kill。

其他注意事项

如果希望在receiver中的onReceive中开启一个Activity,则必须增加FLAG_ACTIVITY_NEW_TASK标记。

onReceive必须在10秒钟内执行完毕,否则会产生ANR(Application Not Response)。如确实需要进行耗时的操作,可以通过启动一个Service的方式进行。

与广播相关的Intent的FLAG:

<pre>
FLAG_EXCLUDE_STOPPED_PACKAGES (不再通知process被终止的receiver,默认行为)

FLAG_INCLUDE_STOPPED_PACKAGES (仍然通知process被终止的receiver)
</pre>

从3.1开始,如果静态注册的APP退出后,不一定能够收到广播。

因为3.1开始系统增加了对APP是否处于运行状态的跟踪。在发送广播时,系统默认增加了FLAG_EXCLUDE_STOPPED_PACKAGES的flag,导致即使是静态注册的receiver,当其所在process退出后,同样无法接收到广播。

对于自定义的广播,可以修改这种行为,使静态注册的receiver在process被结束后依然可以收到广播,方法就是在intent中将FLAG_EXCLUDE_STOPPED_PACKAGES改写为FLAG_INCLUDE_STOPPED_PACKAGES。

对于系统广播,则无能为力了。

常见系统广播

ACTION_TIME_TICK

当前时间变化,每分钟广播一次。只能使用Context.registerReceiver()动态注册,静态注册无效。

值: "android.intent.action.TIME_TICK"

ACTION_TIME_CHANGED

系统时间被设置。

值: "android.intent.action.TIME_SET"

ACTION_TIMEZONE_CHANGED

时区被修改。带有extra:time-zone

值: "android.intent.action.TIMEZONE_CHANGED"

ACTION_BOOT_COMPLETED

系统启动完成。可用进行一些初始化工作,比如安装alarm等。

权限:RECEIVE_BOOT_COMPLETED

值: "android.intent.action.BOOT_COMPLETED"

ACTION_PACKAGE_ADDED

新的APP被安装。(新安装的APP不会受到此广播)

Data:新APP的包名

可能包含的Extras:

  • EXTRA_UID 新APP的UID.
  • EXTRA_REPLACING 是否是重装或升级。如果这个广播紧跟在ACTION_PACKAGE_REMOVED之后,并且作用的是同一个包,那么这个值为true

值: "android.intent.action.PACKAGE_ADDED"

ACTION_PACKAGE_CHANGED

已安装的APP被改动,比如禁用或使能了某个组件。

Data: 包名

Extras:

  • EXTRA_UID 包的UID
  • EXTRA_CHANGED_COMPONENT_NAME_LIST 包含了被修改的组件的类名(或包名本身)
  • EXTRA_DONT_KILL_APP 布尔量,是否覆盖重启APP的默认action(待确认)

值: "android.intent.action.PACKAGE_CHANGED"

ACTION_PACKAGE_REMOVED

已安装的APP被卸载。

Data:被卸载的APP的包名。

Extras:

  • EXTRA_UID 被卸载APP的uid
  • EXTRA_DATA_REMOVED 如果整个APP(包含代码和数据)被卸载,其值被设为true
  • EXTRA_REPLACING 是否是重装或升级。如果这个广播后面紧跟着ACTION_PACKAGE_ADDED之后,并且作用的是同一个包,那么这个值为true

值: "android.intent.action.PACKAGE_REMOVED"

ACTION_PACKAGE_RESTARTED

用户重启了这个APP,它的所有process被kill,所有与它相关的运行时状态被移除(包括process、alarm、notification等)。被重启的APP收不到这个广播。

Data:被重启APP的包名

Extras:

  • EXTRA_UID 被重启APP的uid

值: "android.intent.action.PACKAGE_RESTARTED"

ACTION_PACKAGE_DATA_CLEARED

用户清空了APP的数据。这需要发生在ACTION_PACKAGE_RESTARTED之前。在擦除该APP所有持久化数据之后,本广播被发出。被清空的APP收不到此广播。

Data:被清空数据的APP的包名

Extras:

  • EXTRA_UID APP的uid

值: "android.intent.action.PACKAGE_DATA_CLEARED"

ACTION_UID_REMOVED

UID被从系统移除。UID被以EXTRA_UID为键存储在extras中。

值: "android.intent.action.UID_REMOVED"

ACTION_BATTERY_CHANGED

粘性广播被声明为过时的,此处待验证。

ACTION_POWER_CONNECTED

连接外部电源。

值: "android.intent.action.ACTION_POWER_CONNECTED"

ACTION_POWER_DISCONNECTED

外部电源被移除。

值: "android.intent.action.ACTION_POWER_DISCONNECTED"

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

推荐阅读更多精彩内容