考察内容:
- 广播的注册原理
- 广播的发送原理
- 广播的接收原理
(重点对比静态广播和动态广播在这三点原理的区别)
静态广播的注册
- 静态广播是在AndroidManifest.xml
- 动态广播是在代码中注册的
...
//Android在启动的时候会启动PackageManagerService服务,
//这个服务会去扫描已经安装的APK,解析里面的AndroidManifest文件
else if (tagName.equals("receiver")) {
//当解析到TAG=="receiver"时,将去解析xml中的配置,并生成一个Activity对象
//这个Activity并非应用端的那个Activity,对于PKMS来说它就是一个Component:
//class Activity extends Component<ActivityIntentInfo> {...}
//Component表示它是一个应用组件
Activity a = parseActivity(owner, res, parser, attrs, flags, ...);
//把a加入到receivers列表里面,静态广播就是这时候被注册到PKMS里的,当需要的时候再从PKMS中查找
//receivers是 List<Activity> receivers
owner.receivers.add(a);
}
广播的发送
这里只讨论发送普通广播,不讨论order和sdicky广播
应用端:
@Override
public void sendBroadcast(Intent intent) {
ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(), intent, ...)
}
AMS端:
int broadcastIntent(IApplicationThread caller, Intent intent, ...) {
...
int res = broadcastIntentLocked(callerApp, ...);
return res;
}
int broadcastIntentLocked(ProcessRecord callerApp, ...) {
//根据intent找到有哪些receiver可以接收这个广播
//先找静态注册的receiver
List receivers = collectReceiverComponents(intent, resolvedType, ...);
//然后找动态注册的receiver
List registeredReceivers = mReceiverResolver.queryIntent(intent, ...);
if (!ordered && registeredReceivers.size() > 0) {
//处理动态receiver,加到并行分发队列
}
//给没有处理完的动态receiver,跟静态receiver合并到一起
if ((receivers != null && receivers.size() > 0)) {
//处理剩下的receiver, 加到串行分发队列
}
}
//如何处理合并receiver列表的:
private final int broadcstIntentLocked(ProcessRecord callerApp, ...) {
...
if ((receivers != null && receiver.size() > 0)) {
//Broadcast有两个Queue:一个处理紧急任务另一个处理普通任务,这里返回的是处理普通任务的Queue
BroadcastQueue queue = broadcastQueueForIntent(intent);
//new 一个BroadcastRecord,再把它加到BroadcastQueue中
BroadcastRecord r = new BroadcastRecord(queu, intent, ...);
//BroadcastQueue中有两个列表:一个是用来并行分发的另一个用来串行分发的
//这里把BroadcastRecord加到串行分发的列表中
queue.enqueueOrderedBroadcastLocked(r);
//准备分发广播
queu.scheduleBroadcastsLocked();
}
return ActivityManager.BROADCAST_SUCCESS;
}
final void processNextBroadcast(boolean fromMsg) {
//流程:
//先给并行分发的广播分发完,然后接下来分发串行广播
//如果有pending的广播,就先直接返回,这个广播在等待应用进程启动
//如果当前广播分发超时了,就废弃这个广播,处理下一个广播
//如果没有超时,并且正在分发中,就先返回,什么也不做
//如果当前的广播已经分发完一个receiver了,就继续分发下一个receiver
//如果这个receiver是动态注册的receiver,就直接分发
//如果这个receiver是静态注册的receiver,就先看进程启动没有
//如果启动进程启动了,就直接分发
//没启动的话就先启动进程,然后给广播标记为pending
//进程启动后attachApplication时继续处理这个pending的广播
//代码:
下面分段分析
}
代码分析1:
do {
//取出第一个BroadcastReceiver
r = mOrderedBroadcasts.get(0);
//看是否已经超时:r.dispatchTime表示广播刚开始分发的时间
//mTimeoutPeriod是超时时长:60秒
if (now > r.dispatchTime + (2*mTimeoutPeriod*numReeivers)) {
//做些广播超时的善后工作
broadcastTimeoutLocked(flase);
//设置强制回收标记
forceReceive = true;
//设置状态为IDLE
r.state = BroadcastRecord.IDLE;
}
if (r.state != BroadcastRecord.IDLE) {
//如果状态不是IDLE表示广播还在分发,现在什么都别干,继续等待
return;
}
if (r.receivers == null || r.nextReceiver >= numReceivers || forceReceive) {
cancelBroadcastTimeoutLocked();
//把广播从列表中删除————废弃这个广播,以后再也不处理了
mOrderedBroadcasts.remove(0);
//继续处理下一个广播
continue;
}
}while (r == null);
final void broadcastTimeoutLocked(boolean fromMsg) {
BroadcastRecord r = mOrderedBroadcasts.get(0);
Object curReceiver = r.receivers.get(r.nextReceiver-1);
//找到当前分发的receiver对应的进程
if (mPendingBroadcast == r) {
//表示超时是因为等待进程启动导致的
mPendingBroadcast = null;
}
//把当前超时的广播的状态全部重置
finishReceiverLocked(r, r.resultCode, ...);
//再次调度广播的处理
scheduleBroadcastsLocked();
//显示ANR的diaglog
mHandler.post(new AppNotResponding(app, anrMessage));
}
代码分析2:
//能走到这里说明当前广播没有超时,并且当前是IDLE状态
//IDLE表示当前没有receiver在分发,可以继续分发下一个receiver
//先获取receiver的index
int recldx = r.nextReceiver++;
//然后设置广播的时间戳
r.receiverTime = SystemClock.uptimeMillis();
if (recldx == 0) {
r.dispatchTime = r.receiverTime;
r.dispatchClockTime = System.currentTimeMillis();
}
//看是否有超时消息
if (!mPendingBroadcastTimeoutMessage) {
//如果没有超时消息,就给它发一个超时消息
long timeoutTime = r.receiverTime + mTimeoutPeriod;
//其实是post了一个delayRunnable,如果超时就会执行runnable
setBroadcastTimeoutLocked(timeoutTime);
}
//根据index从BroadcastReceiver的Receiver列表中取出下一个要分发的receiver
final Object nextReceiver = r.receivers.get(recldx);
if(nextReceiver instanceof BroadcastFilter) {
//如果是动态receiver,就直接发送,因为动态注册的receiver它的进程肯定是已经启动
BroadcastFilter filter = (BroadcastFilter)nextReceiver;
//动态receiver的分发,见后面分析
deliverToRegisteredReceiverLocked(r, filter, r.ordered);
return;
}
//静态receiver
r.state = BroadcastRecord.APP_RECEIVE;
ProcessRecord app = mService.getProcessRecordLocked(targetProcess, ...);
if (app != null && app.thread != null) {
//如果进程已经启动,则处理当前广播
//静态receiver的分发,见后面分析
processCurBroadcastLocked(r, app);
return;
}
//如果进程没有启动,则启动进程
r.curApp = mService.startProcessLocked(targetProcess, ...);
//设置mPendingBroadcast,等待进程启动完再处理
mPendingBroadcast = r;
mPendingBroadcastRecvIndex = recldx;
广播的接收
动态Receiver的分发
AMS端
//把广播分发到动态注册的receiver
void deliverToRegisteredReceiverLocked(BroadcastRecord r, ...) {
...
performReceiveLocked(filter.receiverList.app, ...);
if (ordered) {
//串行分发的动态receiver分发完后会把状态置成该值
r.state = BroadcastRecord.CALL_DONE_RECEIVE;
}
}
void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver, ...) {
//通过applicationThread调用应用端的scheduleRegisteredReceiver函数
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode, ...);
}
应用端
广播分发到动态注册的receiver
//先拿到BroadcastReceiver
final BroadcastReceiver receiver = mReceiver;
final boolean ordered = mOrdered;
final Intent intent = mCurIntent;
mCurIntent = null;
receiver.setPendingResult(this);
//然后调用它的onReceive
receiver.onReceive(mContext, intent);
if (receiver.getPendingResult() != null) {
//向AMS报告:广播已经在receiver分发完了
finish();
}
void finish() {
//调用AMS的finishReceiver函数,AMS就知道当前的receiver分发完了,它就可以接着分发下一个receiver了
IActivityManager mgr = ActivityManagerNative.getDefault();
mgr.finishReceiver(...);
}
finishReceiver在AMS端的处理
void finishReceiver(IBinder who, int resultCode, ...) {
boolean doNext = false;
//who是BroadcastReceiver对应的binder对象——IIntentReceiver
//先根据who找到对应的BroadcastRecord
BroadcastRecord r = queue.getMatchingOrderedReceiver(who);
//重置当前广播状态(前面广播超时时有提到过)
//返回值很重要,它将决定会不会继续处理下一个receiver
//对于静态receiver和串行分发的动态receiver,返回值应该是true
doNext = r.queue.finishReceiverLocked(r, resultCode, ...);
if (doNext) {
//把广播分发到下一个receiver
r.queue.processNextBroadcast(false);
}
}
public boolean finishReceiverLocked(BroadcastRecord r, ...) {
final int state = r.state;
...
return state == BroadcastRecord.APP_RECEIVE //静态receiver处理完之后会把状态置成该值
|| state == braodcastRecord.CALL_DONE_RECEIVE; //串行分发的动态receiver处理完之后会把状态置成该值
}
静态receiver的分发
AMS端
void processCurBroadcastLocked(BroadcastRecord r, ...) {
r.receiver = app.thread.asBinder();
r.curApp = app;
app.curReceiver = r;
...
//通过applicationThread调用应用端的schedulReceiver函数
//注意,对于动态receiver调用的是scheduleRegisteredReceiver
app.thread.schedulReceiver(new Intent(r.intent), r.curReceiver, ...);
}
应用端
void scheduleReceiver(Intent intent, ActivityInfo info, ...) {
//封装一个消息,然后转到主线程处理
ReceiverData r = new ReceiverData(intent, resultCode, data, ...);
r.into = info;
r.compatInfo = compatInfo;
sendMessage(H.RECEIVER, r);
}
主线程中的处理:
private void handleReceiver(ReceiverData data) {
//先拿到LoadedApk
LoadedApk packageInfo = getPackageInfoNoCkeck(...);
//加载BroadcastReceiver的类,并调用newInstance创建一个BroadcastReceiver对象
BroadcastReceiver receier;
receiver = cl.loadClass(component).newInstance();
//准备好application
Application app = packageInfo.makeApplication(flase, ...);
ContextImpl context = app.getBaseContext();
receiver.setPendingResult(data);
//传入的context其实是以applicationo为mBase的一个ContextWrapper
receiver.onReceiv(context.getReceiverResrictedContext(), data.intent);
if (receiver.getPendingResult() != null) {
data.finish();
}
}
静态广播在分发的时候如果进程不在会先启动进程,进程启动后是如何处理的?
应用进程启动后向AMS报告的函数:
boolean attachApplicationLocked(IApplicationThread thread, ...) {
...
sendPendingBroadcastLocked(app);
...
}
boolean sendPendingBroadcastLocked(ProcessRecord app) {
for (BroadcastQueue queue: mBroadcastQueues) {
//处理pending的broadcast
queu.sendPendingBroadcastLocked(app);
}
}
boolean sendPendingBroadcastsLocked(ProcessRecord app) {
final BroadcastRecord br = mPendingBroadcast;
if (br != null && br.curApp.pid == app.pid) {
mPendingBroadcast = null;
//把广播分发到静态receiver,前面已经分析过
processCurBroadcastLocked(br, app);
}
}
回归: 说说静态广播的注册和收发原理
- 静态广播是怎么注册的?
a)在AndroidManifest里配置好广播
b)Anroid系统启动的时候PKMS会扫描应用的AndroidManifest配置,把里面的广播解析出来并注册到PKMS中 - 静态广播是串行分发的
a)如果进程没有启动要先启动进程
b)如果分发超时就会废弃这个广播 - 静态广播的生命周期及上下文
a)广播分发到应用之后,应用会在主线程创建一个广播对象并执行onReceived()
b)但并没有给它创建上下文
c)onReceived中的Context并不是application的Context,而是以application为mBase的ContextWrapper