文档章节

Android广播机制分析

穿着鞋子的蜗牛
 穿着鞋子的蜗牛
发布于 2015/05/04 16:59
字数 2259
阅读 85
收藏 2

1.1. 广播简介


Android 广播与生活中的广播概念不同,它是指系统中产生事件后的通知。Android 广播不关心接收者是否收到处理或者如何处理广播,可以说是一种单向的通知。
Android 通过 BraodcastReceiver 来监听系统发出的广播,不同的 BraodcastReceiver 通过设置不同的 fliter 来区分监听广播的类型。有些广播的监听需要相应的权限。

1.2. 注册广播


BraodcastReceiver 必须经过注册才能具有监听功能,注册的方式有两种:

1.2.1. 静态注册


通过在 AndroidManifest.xml 中注册对应的 receiver,并设置要监听的 action

其中 MyReceiver 为继承 BroadcastReceiver 的类,重写了 onReceiver 方法,并在 onReceiver方法中对广播进行处理。标签设置过滤器,接收指定 action 广播。静态注册方式的特点:不管改应用程序是否处于活动状态,都会进行监听。action 可以为自定义 action,也可以是系统自带的 action。

1.2.2. 动态注册

动态注册是在程序中对 BroadcastReceiver 完成监听注册

MyReceiver receiver = new MyReceiver();
//创建过滤器,并指定 action,使之用于接收同 action 的广播
IntentFilter filter = new IntentFilter("MyReceiver_Action");
//注册广播接收器
registerReceiver(receiver, filter);
在 activity 中注册广播,必须在 activity 结束时注销广播,一般在 onStart 中注册
BroadcastReceiver,在 onStop 中取消 BroadcastReceiver,广播接收器跟随 Activity 的生命周期
//注销广播接收器
unregisterReceiver(receiver);

1.3. 生命周期


BroadcastReceiver 生命周期只有 8 秒左右,如果在 onReceive()内做超过 8 秒内的事情,就会报ANR(Application Not Response)程序无响应的错误信息。它的生命周期为从回调 onReceive()方法开始到该方法返回结果后结束。因此,如果要在收到广播后做耗时操作,最好放到 service 中去做,子线程也不好,当子线程未结束的时候,BroadcastReceiver 已经销毁。

1.4. 发送广播


1.4.1. Android 中发送广播的三种形式

广播被分为三种不同的类型:“普通广播(Normal broadcasts)”、“有序广播(Ordered broadcasts)”和“粘性广播(Sticky broadcast)”。普通广播是完全异步的,可以在同一时刻(逻辑上)被所有广播接收者接收到,消息传递的效率比较高,但缺点是:接收者不能将处理结果传递给下一个接收者,并且无法终止广播 Intent 的传播;然而有序广播是按照接收者声明的优先级别(声明在intent-filter 元素的 android:priority 属性中,数越大优先级别越高,取值范围:-1000 到 1000(其实最大可以为 int 最大值即:2147483647)。也可以调用 IntentFilter 对象的 setPriority()进行设置),被接收者依次接收广播。如:A 的级别高于 B,B 的级别高于 C,那么,广播先传给 A,再传给 B,最后传给 C。A 得到广播后,可以往广播里存入数据,当广播传给 B 时,B 可以从广播中得到 A 存入的数据。Context.sendBroadcast()发送的是普通广播,所有订阅者都有机会获得并进行处理。Context.sendOrderedBroadcast()发送的是有序广播,系统会根据接收者声明的优先级别按顺序逐个执行接收者,前面的接收者有权终止广播。BroadcastReceiver.abortBroadcast()如果广播被前面的接收者终止,后面的接收者就再也无法获取到广播。对于有序广播,前面的接收者可以将处理结果存放进广播 Intent,然后传给下一个接收者。Context.sendStickyBroadcast()是发送粘性广播,使用这个 api需要权限 android.Manifest.permission.BROADCAST_STICKY,粘性广播的特点是 Intent 会一直保留到广播事件结束,而这种广播也没有所谓的 8 秒限制,即上文所讲,如果 onReceive 方法执行时间太长,超过 8 秒的时候系统会将这个广播置为可以干掉的 candidate,一旦系统资源不够的时候,就会干掉这个广播而让它不执行。

1.5. 广播优先级


1.5.1. 基本原则


接收无序广播的接收器接收到广播的顺序是有序的(由优先级决定顺序)
接收无序广播的接收器也一样可以设置优先级的
动态注册广播优先级高于静态注册广播
同等优先级的动态接收器,先注册的先接收
同等优先级的静态接收器,接收广播的顺序与 String[] java.io.File.list()顺序一致
Ps:这里有一点需要注意的是,同等优先级的静态接收器的接收顺序具有不确定性,原因就是File.list()的方法返回的顺序具有不确定性,如果需要查看某接收器的接收顺序,最好是试验大量的 apk 名

1.5.2. ordered 广播


假设有如下优先级的 5 个接收器
1.动态 A(优先级=1)
2.动态 B(优先级=2)
3.动态 C(优先级=2)
4.静态 D(优先级=1)
5.静态 E(优先级=2)
并且 B 先于 C 注册
那么实际接收顺序应为
B C E A D
也就是说,如果静态接收器的优先级高于动态接收器的优先级,那么还是静态接收器先接收到广播(比如接收 SMS 广播)

1.5.3. 非 ordered 广播


动态接收器高优先级>动态接收器低优先级>静态接收器高优先级>静态接收器低优先级

1.6. 只能动态接受广播源码分析


有些广播,我们无法用静态接收器接收,比如 ACTION_SCREEN_ON,当屏幕被点亮的时候系统发送此广播。

1. void com.android.server.PowerManagerService.initInThread()
2. Java 代码
3. void initInThread() {
4. ??
5. mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON);
6. mScreenOnIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
7. mScreenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF);
8. mScreenOffIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
9. ??
10. }


Intent 中都设置了 Intent.FLAG_RECEIVER_REGISTERED_ONLY,所以,如果要接收,必须动态注册广播接收器 ACTION_SCREEN_OFF 也是如此。

1.6.1. 关于 FLAG_RECEIVER_REGISTERED_ONLY 的说明


public static final int
FLAG_RECEIVER_REGISTERED_ONLY
Added in API level 1
If set, when sending a broadcast only registered receivers will be called -- no
BroadcastReceiver components will be launched.
Constant Value: 1073741824 (0x40000000)
ACTION_BATTERY_CHANGED(电池电量发生变化的时候,系统发送此广播)该广播就是这样
void com.android.server.BatteryService.sendIntent()

Java 代码
1. private final void sendIntent() {
2. //
3. Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
4. intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
5.
Pack up the values and broadcast them to everyone
6.
7. }


1.7. 广播注册过程分析


1.7.1. 静态注册 Receiver 的流程

静态 receiver 的注册是由 PackageManagerService 开机的时候负责初始 PMS 在开机的时候会对系统一些目录逐个扫描,解析 apk 文件。静态广播接收器就是在 PMS 做这件事情的时候顺便处理的。
PMS 会解析 apk 的 manifest 文件,查找这里注册的 receiver,然后加载到内存中。
PMS 初始化扫描目录的顺序:
system/framework
system/app
vendor/app
data/appd
rm/app-private
我们看到了 PMS 如何在初始化的时候如何解析 manifest 并把其中的 element 存放到内存中的其中receiver 保存到了 owner 的成员变量 receivers 中,owner 的类型是
android.content.pm.PackageParser.Package 也就是说 scanPackageLI 返回结果就是已经包含了manifest 信息的 Package 对象。

1.7.2. 动态注册 Receiver 的流程


动态注册最终会调用到 AMS 中的 registerReceiver 函数,最终所有动态注册的 receiver 都保存到 AMS 的成员变量 mReceiverResolver 中。静态广播和动态广播如何注册的,我们已经全部分析完了。静态广播是 PackageManagerService负责,保存到其成员变量 mReceivers 中,动态广播是 ActivityManagerService 负责,保存到其成员变量 mReceiverResolver 中。

1.8.1. 分析


Context 中的 sendBroadCast 函数的实现是在 ContextImpl 中,和发送广播相关的有如下六个函数:

void android.app.ContextImpl.sendBroadcast(Intent intent)
void android.app.ContextImpl.sendBroadcast(Intent intent, String receiverPermission)
void android.app.ContextImpl.sendOrderedBroadcast(Intent intent, String receiverPermission)
void android.app.ContextImpl.sendOrderedBroadcast(Intent intent, String receiverPermission,
BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData,
Bundle initialExtras)
void android.app.ContextImpl.sendStickyBroadcast(Intent intent)
void android.app.ContextImpl.sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver
resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle
initialExtras)

可以分为 3 组:1 普通广播;2 Ordered 广播;3 Sticky 广播。不论哪种,最后都会由 AMS 处理,如果是非 ordered 广播,那么 mParallelBroadcasts 将存储所有动态接收器,然后合并的时候,mParallelBroadcasts 设置为 null,所以不会合并到 receivers 中,如果是 ordered 广播,那么mParallelBroadcasts 将合并到 receivers 中,然后,不管是哪种广播,最后都是调用scheduleBroadcastsLocked 继续处理,最终到 processNextBroadcast 函数上。广播是否有序,即通过 Boolean 变量 ordered 进行设置。


1.8.2. 总结


发送过程情况分为两种(scheduleBroadcastsLocked),ordered 广播和非 ordered 广播、非ordered 广播。先处理动接收器,然后处理静态接收器 ordered 广播同时处理动态接收器和静态接收器先将动态接收器与静态接收器合并,保持着与优先级相同的顺序,优先级高的在前面,否则顺序不变。静态接收器与动态接收器优先级相同的话,动态接收器在前。


本文转载自:http://www.2cto.com/kf/201408/325326.html

穿着鞋子的蜗牛
粉丝 0
博文 12
码字总数 0
作品 0
朝阳
私信 提问
从源码角度看Android_O_AMS新特性

简介 Android O上以后对AMS的广播与服务做了严格的限制,静态注册的广播不再能够无所欲为的接收隐式的广播,后台的应用进程也不再能够调用startService了。Android 作出这样的限制主要是为了...

_OUTMAN_
2018/09/28
0
0
从代码层次分析比如打电话的时候,电源管理机制怎么工作的。

从代码层次分析比如打电话的时候,电源管理机制怎么工作的,涉及android的一些机制,比如说它的广播系统,Activity,涉及到打电话的场景 打电话时候底层产生的信号, PowerManagerService怎样...

郑思远
2012/09/12
160
0
BroadcastReceiver的源码分析

android提供了广播机制,通过BroadcastReceiver可以在不同的进程间传递消息。类似于观察者模式,A应用通过注册广播表示A对消息subject感兴趣,当B应用发出subject类型的消息的时候,A应用就能...

JasmineBen
2018/05/18
0
0
Android引入广播机制的用意。单线程模型Message、Handler、Message Que.

注册广播有几种方式,这些方式有何优缺点?请谈谈Android引入广播机制的用意。 Android 的广播机制 在 Android 里面有各种各样的广播,比如电池的使用状态,电话的接收和短信的接收都会产生一...

一别经年
2014/03/15
0
0
android broadcastReceiver生命周期及两种应用

一:Android 广播的生命周期 一个广播接收者有一个回调方法:void onReceive(Context curContext, Intent broadcastMsg)。当一个广播消息到达接收者时,Android调用它的onReceive()方法并传递...

gavin_jin
2012/02/17
0
4

没有更多内容

加载失败,请刷新页面

加载更多

通过微服务来正确实施SOA

对于组织来说,能够构建、发展和扩展大型应用程序是至关重要的, 但所涉及的挑战使其成为一项艰巨的任务。正因为如此, 微服务凭借能够将单个组件拆分成围绕特定业务功能的独立服务,已成为构建...

Linux就该这么学
13分钟前
2
0
从 Spark 到 Kubernetes — MaxCompute 的云原生开源生态实践之路

2019年5月14日,喜提浙江省科学技术进步一等奖的 MaxCompute 是阿里巴巴自研的 EB 级大数据计算平台。该平台依托阿里云飞天基础架构,是阿里巴巴在10年前做飞天系统的三大件之分布式计算部分...

阿里云官方博客
16分钟前
1
0
使用python来操作redis用法详解

1、redis连接 redis提供两个类Redis和StrictRedis用于实现Redis的命令,StrictRedis用于实现大部分官方的命令,并使用官方的语法和命令,Redis是StrictRedis的子类,用于向后兼容旧版本的red...

dragon_tech
16分钟前
2
0
给研发工程师的代码质量利器 | SOFAChannel#5 直播整理

> SOFA:Channel,有趣实用的分布式架构频道。 > > 本文根据 SOFAChannel#5 直播分享整理,主题:给研发工程师的代码质量利器 —— 自动化测试框架 SOFAActs。 > > 回顾视频以及 PPT 查看地址...

SOFAStack
18分钟前
1
0
段错误总结

https://blog.csdn.net/e_road_by_u/article/details/61415732 一、段错误是什么 一句话来说,段错误是指访问的内存超出了系统给这个程序所设定的内存空间,例如访问了不存在的内存地址、访问...

悲催的古灵武士
20分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部