1.功能详解
1.广播分类
1).Normal Broadcast:普通广播
此处将普通广播界定为:开发者自己定义的intent,以context.sendBroadcast_"AsUser"(intent, ...)形式。具体可以使用的方法有:
sendBroadcast(intent)/sendBroadcast(intent, receiverPermission)/sendBroadcastAsUser
(intent, userHandler)/sendBroadcastAsUser(intent, userHandler,receiverPermission)。
普通广播会被注册了的相应的感兴趣(intent-filter匹配)接收,且顺序是无序的。如果发送广播时有相应的权限要求,
BroadCastReceiver如果想要接收此广播,也需要有相应的权限。
2).System Broadcast: 系统广播
Android系统中内置了多个系统广播,只要涉及到手机的基本操作,基本上都会发出相应的系统广播。如:开启启动,网络状态改变,拍照,
屏幕关闭与开启,点亮不足等等。每个系统广播都具有特定的intent-filter,其中主要包括具体的action,系统广播发出后,
将被相应的BroadcastReceiver接收。系统广播在系统内部当特定事件发生时,有系统自动发出。
3)Ordered broadcast:有序广播
有序广播的有序广播中的“有序”是针对广播接收者而言的,指的是发送出去的广播被BroadcastReceiver按照先后循序接收。
同一时刻只会有一个广播接收器能够接收到这条广播
有序广播的定义过程与普通广播无异,只是其的主要发送方式变为:sendOrderedBroadcast(intent, receiverPermission, ...)。
对于有序广播,其主要特点总结如下:
1>多个具当前已经注册且有效的BroadcastReceiver接收有序广播时,是按照先后顺序接收的,先后顺序判定标准遵循为:
当前系统中所有有效的动态注册和静态注册的BroadcastReceiver按照priority属性值从大到小排序,对于具有相同的priority的动态广播
和静态广播,动态广播会排在前面。
2>先接收的BroadcastReceiver可以对此有序广播进行截断,使后面的BroadcastReceiver不再接收到此广播,也可以对广播进行修改,
使后面的BroadcastReceiver接收到广播后解析得到错误的参数值。当然,一般情况下,不建议对有序广播进行此类操作,
尤其是针对系统中的有序广播。
4)Sticky Broadcast:粘性广播(在 android 5.0/api 21中deprecated,不再推荐使用,相应的还有粘性有序广播,同样已经deprecated)。
既然已经deprecated,此处不再多做总结。
5)Local Broadcast:App应用内广播(此处的App应用以App应用进程为界)
由前文阐述可知,Android中的广播可以跨进程甚至跨App直接通信,且注册是exported对于有intent-filter的情况下默认值是true,
由此将可能出现安全隐患如下:
1.其他App可能会针对性的发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收到广播并处理;
2.其他App可以注册与当前App一致的intent-filter用于接收广播,获取广播具体信息。
无论哪种情形,这些安全隐患都确实是存在的。由此,最常见的增加安全性的方案是:
1.对于同一App内部发送和接收广播,将exported属性人为设置成false,使得非本App内部发出的此广播不被接收;
2.在广播发送和接收时,都增加上相应的permission,用于权限验证;
3.发送广播时,指定特定广播接收器所在的包名,具体是通过intent.setPackage(packageName)指定在,这样此广播将只会发送到此包中的
App内与之相匹配的有效广播接收器中。
App应用内广播可以理解成一种局部广播的形式,广播的发送者和接收者都同属于一个App。实际的业务需求中,App应用内广播可能需要用到。
同时,之所以使用应用内广播时,而不是使用全局广播的形式,更多的考虑到的是Android广播机制中的安全性问题。
系统提供LocalBroadcastManager 来发送应用内广播.
对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册的ContextReceiver才有可能接收到
(静态注册或其他方式动态注册的ContextReceiver是接收不到的)。用法如下
//注册应用内广播接收器
localBroadcastManager = LocalBroadcastManager.getInstance(this);
localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);
//取消注册应用内广播接收器
localBroadcastManager.unregisterReceiver(mBroadcastReceiver);
//发送应用内广播
localBroadcastManager.sendBroadcast(intent);
2.广播的变化
1).Android5.0/API level 21开始粘滞广播和有序粘滞广播过期,以后不再建议使用;
2).”静态注册的广播接收器即使app已经退出,主要有相应的广播发出,依然可以接收到,但此种描述自Android 3.1开始有可能不再成立“
Android 3.1开始系统在Intent与广播相关的flag增加了参数,分别是FLAG_INCLUDE_STOPPED_PACKAGES
和FLAG_EXCLUDE_STOPPED_PACKAGES。
FLAG_INCLUDE_STOPPED_PACKAGES:包含已经停止的包(停止:即包所在的进程已经退出)
FLAG_EXCLUDE_STOPPED_PACKAGES:不包含已经停止的包
主要原因如下:
自Android3.1开始,系统本身则增加了对所有app当前是否处于运行状态的跟踪。在发送广播时,不管是什么广播类型,系统默认直接增加了值
为FLAG_EXCLUDE_STOPPED_PACKAGES的flag,导致即使是静态注册的广播接收器,对于其所在进程已经退出的app,同样无法接收到广播。
由此,对于系统广播,由于是系统内部直接发出,无法更改此intent flag值,因此,3.1开始对于静态注册的接收系统广播的BroadcastReceiver,
如果App进程已经退出,将不能接收到广播。
但是对于自定义的广播,可以通过复写此flag为FLAG_INCLUDE_STOPPED_PACKAGES,使得静态注册的BroadcastReceiver,
即使所在App进程已经退出,也能能接收到广播,并会启动应用进程,但此时的BroadcastReceiver是重新新建的
2.注册源代码解读
1.ContextImpl.registerReceiverInternal
首先registerReceiver会执行到ContextWrapper的registerReceiver方法.转而又执行到ContextImpl的registerReceiverInternal方法里.我们从这里开始看起
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context) {
IIntentReceiver rd = null;
if (receiver != null) {
if (mPackageInfo != null && context != null) {
//为空表示默认为主线程
//AMS并不是直接给广播接收者发送广播的,当广播到达应用程序进程的时候,
//会被封装成一个Message,然后push到主线程消息队列中,然后才会给接
//收者处理,你也可以指定一个处理的Handler,将onReceive()调度在非主线程执行。
if (scheduler == null) {
//1/mMainThread就是ActivityThread,是我们主线程的处理类.这个scheduler就是主线程的Handler, mH对象.
scheduler = mMainThread.getHandler();
}
//2.这里的IIntentReceiver rd是对BroadcastReceiver 的一个转化.这个IIntentReceiver 继承Binder,
可以夸进程传输到ActivityManagerService进程.
rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
} else {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = new LoadedApk.ReceiverDispatcher(
receiver, context, scheduler, null, true).getIIntentReceiver();
}
}
try {
//3.这里就调用到了ActivityManagerService的registerReceiver方法.这是IPC过程.ActivityManagerNative.getDefault()
就是ActivityManagerService的本地代理
return ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission, userId);
} catch (RemoteException e) {
return null;
}
}
2.LoadedApk.getReceiverDispatcher
这是接着1.2的方法看,,LoadedApk这个类包含了当前加载的apk的主要的信息,其中成员变量mReceivers表就记录了所有动态注册的receiver。
ReceiverDispatcher把BroadcastReceiver和InnerReceiver联系起来.他们是一一对应的.
InnerReceiver的作用就是类似BroadcastReceiver在ams端的代理.InnerReceiver会把远程Ams的处理方式转发回来.通过消息队列交给BroadcastReceiver处理.而ReceiverDispatcher是把这两个类联系起来的.
private final ArrayMap> mReceivers
= new ArrayMap>();
public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
Context context, Handler handler,
Instrumentation instrumentation, boolean registered) {
synchronized (mReceivers) {
LoadedApk.ReceiverDispatcher rd = null;
ArrayMap map = null;
if (registered) {
map = mReceivers.get(context);//1. 这是上边定义的map集合.以每个Context为键.
if (map != null) {
rd = map.get(r);
}
}
if (rd == null) {
//2.ReceiverDispatcher为空就新建一个,他里边包含InnerReceiver内部类,
rd = new ReceiverDispatcher(r, context, handler,
instrumentation, registered);
if (registered) {
if (map == null) {
//3.这里看出.BroadcastReceiver(广播接收者)和ReceiverDispatcher(广播分发者)以及InnerReceiver是一一对应的.
InnerReceiver传输到Ams进程,每个广播接收者对应一个广播分发者, 当AMS向app发送广播时会调用到app进程的广播分发者,
然后再将广播以message形式post到app的主线程,来执行onReceive()方法。
map = new ArrayMap();
mReceivers.put(context, map);
}
map.put(r, rd);
}
} else {
rd.validate(context, handler);
}
rd.mForgotten = false;
return rd.getIIntentReceiver();
}
}
3.ActivityManagerService.registerReceiver
这里就是远程的处理过程,主要是对调用者进程的检查.粘性广播的保存.再把普通广播存储起来,把广播对应的IntentFilter 转化为BroadcastFilter 存储起来.以便发送广播后进行查找
这里有几个重要的变量讲解一下.
final HashMap
这个mRegisteredReceivers 里的key IBinder 就是之前的InnerReceiver,他继承自IIntentReceiver.Stub.也就是本地BroadcastReceiver在Ams的代理.用InnerReceiver.asBinder()作为key.value则是这个广播接收者对应的BroadcastFilter集合.而BroadcastFilter则是对IntentFilter的封装.
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
enforceNotIsolatedCaller("registerReceiver");
int callingUid; // Uid是用户ID Android中每个程序都有一个Uid
int callingPid; //pid是进程id,每一个不同的程序都能有一个UId,但是一个应用里面可以有多个PId
synchronized(this) {
ProcessRecord callerApp = null;
if (caller != null) { //1.由caller获取当前进程对象
callerApp = getRecordForAppLocked(caller);
//2. 进程还没创建,直接抛出异常
if (callerApp == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when registering receiver " + receiver);
}
if (callerApp.info.uid != Process.SYSTEM_UID &&
!callerApp.pkgList.containsKey(callerPackage) &&
!"android".equals(callerPackage)) {
throw new SecurityException("Given caller package " + callerPackage
+ " is not running in process " + callerApp);
}
callingUid = callerApp.info.uid;
callingPid = callerApp.pid;
} else {
callerPackage = null;
callingUid = Binder.getCallingUid();
callingPid = Binder.getCallingPid();
}
userId = this.handleIncomingUser(callingPid, callingUid, userId,
true, ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
List allSticky = null;
//3.从actions中,先把粘性广播帅选出来,放进stickyIntents中
Iterator actions = filter.actionsIterator();
if (actions != null) {
while (actions.hasNext()) {
String action = (String)actions.next();
allSticky = getStickiesLocked(action, filter, allSticky,
UserHandle.USER_ALL);
allSticky = getStickiesLocked(action, filter, allSticky,
UserHandle.getUserId(callingUid));
}
} else {
allSticky = getStickiesLocked(null, filter, allSticky,
UserHandle.USER_ALL);
allSticky = getStickiesLocked(null, filter, allSticky,
UserHandle.getUserId(callingUid));
}
// The first sticky in the list is returned directly back to
// the client.
Intent sticky = allSticky != null ? (Intent)allSticky.get(0) : null;
if (receiver == null) {
return sticky;
}
//4.mRegisteredReceivers表存了所有动态注册的广播接收者对应的IntentFilter的集合,
//由receiver作为key,获取到ReceiverList,为什么是ReceiverList,
//而不是一个Receiver呢,因为一个BroadcastReceiver可能用来接收多个广播,会有多个
IntentFilter
ReceiverList rl
= (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);
if (rl.app != null) {
//5.rl.app就是上文创建的 ProcessRecord callerApp,这里的操作就是把ReceiverList保存到进程的receivers中
rl.app.receivers.add(rl);
} else {
try {
receiver.asBinder().linkToDeath(rl, 0);
} catch (RemoteException e) {
return sticky;
}
rl.linkedToDeath = true;
}
//这里把receiver和他对应的intentFilter建立了联系
mRegisteredReceivers.put(receiver.asBinder(), rl);
} else if (rl.uid != callingUid) {
throw new IllegalArgumentException(
"Receiver requested to register for uid " + callingUid
+ " was previously registered for uid " + rl.uid);
}
//6.把IntentFilter封装成BroadcastFilter,并添加到接收者队列,这只适用于动态注册的广播.
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId);
rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadast");
}
//把BroadCast添加给解析器.
mReceiverResolver.addFilter(bf);
//如果是粘性广播,创建BroadcastRecord,并添加到
//BroadcastQueue的并行广播队列(mParallelBroadcasts),这里不解读了.粘性广播已经废弃了.
if (allSticky != null) {
ArrayList receivers = new ArrayList();
receivers.add(bf);
int N = allSticky.size();
for (int i=0; i