【Android话题-5.4应用相关】说说静态广播的注册和收发原理

考察内容:

  • 广播的注册原理
  • 广播的发送原理
  • 广播的接收原理
    (重点对比静态广播和动态广播在这三点原理的区别)

静态广播的注册

  • 静态广播是在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
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值