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

考察内容:

  • 广播的注册原理
  • 广播的发送原理
  • 广播的接收原理

广播的注册

注册广播在应用端的实现

 @Override
 public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
   return registerReceiver(receiver, filter, null, null);
 }

@Override
Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, ...) {
  return registerReceiverInternal(receiver, getUserId(), filter, ...);
}

Intent registerReceiverInternal(BroadcastReceiver receiver, ...) {
  //IIntentReceiver是一个binder对象,是可以跨进程传递的
  IIntentReceiver rd = null;
  scheduler = mMainThread.getHandler();
  rd = mPackageInfo.getReceiverDispatcher(receiver, context, scheduler, ...);
  //把IIntentReceiver对象传递到AMS中
  return ActivityManagerNative.getDefault().registerReceiver(rd, filter, ...);
}

IIntentReceiver getReceiverDispatcher(BroadcastReceiver r, ...) {
  receiverDispatcher rd = null;
  ArrayMap<BroadcastReceiver, ReceiverDispatcher> map = null;
  map = mReceivers.get(context);
  //为BroadcastReceiver new了一个ReceiverDispatcher对象
  rd = new ReceiverDispatcher(r, context, handler, ...);
  //然后put到map中
  map.put(r, rd);
  return rd.getIIntentReceiver();
}
  • 一个Context和一个BroadcastReceiver可以构成一个二元组,这个二元组会对应一个IIntentReceiver对象
  • 同一个Context注册不同的BroadcastReceiver就会对应不同的IIntentReceiver
  • 同一个BroadcastReceiver在不同的Activity中注册,也会对应不同的IIntentReceiver

ReceiverDispatcher的构造函数:

ReceiverDispatch(BroadcastReceiver receiver, Context context, ...) {
  //生成IInerReceiver对象
  mIIntentReceiver = new IInerReceiver(this, !registerd);
  //保存了BroadcastReceiver的引用
  mReceiver = receiver;
}

final static class InnerReceiver extends IIntentReceiver.Stub {
  //IInerReceiver就是IIntentReceiver,是一个binder对象,
  //这个对象持有ReceiverDispatcher的弱引用
  final WeakReference<ReceiverDispatcher> mDispatcher;

  IInerReceiver(ReceiverDispatcher rd, boolean strong) {
    mDispatcher = new WeakReference<ReceiverDispatcher>(rd);
  }
}

引用连:

  • AMS持有IIntentReceiver的引用
  • IIntentReceiver持有ReceiverDispatcher的弱引用
  • ReceiverDispatcher持有BroadcastReceiver的引用

AMS调用IIntentReceiver对象的函数将通过引用连最终调用到BroadcastReceiver的onReceived()函数

注册广播在AMS是如何处理的

public Intent registerReceiver(IApplicationThread caller, ...) {
  //mRegisteredReceivers是一个Map:  Map<IBinder, ReceiverList>
  //其中key为一个binder对象,也就是从应用端传过来的IIntentReceiver
  //value是一个ReceiverList,这个名字似乎没取好,叫FilterList更合适
  //因为它是一个List<BroadcastFilter>,BroadcastFilter继承了IntentFilter
  //一个IIntentReceiver可能对应多个IntentFilter,例如:
  //在一个Activity中先用一个IntentFilter注册了一个BroadcastReceiver
  //然后用另一个IntentFilter对注册同一个BroadcastReceiver
  //这样在AMS端一个IIntentReceiver就会对应两个IntentFilter
  ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
  if (rl == null) {
    rl = new ReceiverList(receiver, ...);
    rl.app.receivers.add(rl);
    mRegisteredReceivers.put(receiver.asBinder(), rl);
  }
  BroadcastFiler bf = new BroadcastFilter(filter, rl, ...);
  rl.add(bf);
  //mReceiverResolver是一个IntentResolver,用来解析Intent————看它是否匹配
  mReceiverResolver.addFilter(bf);
}

广播的发送

下面只讨论普通广播,不讨论order广播和sticky广播

应用端:

@Override
public void sendBroadcast(Intent intent) {
  ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(), intent, ...);
}

int broadcastIntent(IApplicationThread caller, Intent intent, ...) {
  ...
  //AMS端执行
  broadcastIntentLocked(callerApp, ...);
}

int broadcastIntentLocked(ProcesRecord callerApp, ...) {
  ...
  //首先根据Intent找到匹配的Receiver列表
  registeredReceivers = mReceiverResolver.queryIntent(intent, ...);

  int NR = registeredReceivers.size();
  //ordered表示是否为有序广播,动态广播默认是无序的,因此ordered为false
  if (!ordered && NR > 0) {
    //AMS中有两个BroadcastQueue:一个是分发紧急任务的,另一个是普通任务的
    //此处应该是得到分发普通任务的Queue
    final BroadcastQueue queue = broadcastQueueForIntent(intent);
    //创建一个BroadcastRecord,它包含Receiver列表
    BroadcastRecord r = new BroadcastRecord(queue, intent, registeredReceivers, ...);
    //把BroadcastRecord加到BroadcastQueue中
    //BroadcastQueue里其实是有两个列表的:一个是并行分发的,另一个是串行分发的
    //动态广播默认是并行分发的,因此这里把BroadcastRecord加到并行分发列表中
    queue.enqueueParallelBroadcastLocked(r);
    //准备分发广播
    queue.scheduleBroadcastLocked();
    registeredReceivers = null;
    NR = 0;
  }
  ...
}

BroadcastQueue中处理广播分发的函数

final void processNextBroadcast(boolean fromMsg) {
  BroadcastRecord r;

  while (mParallelBroadcasts.size() > 0) {
    //依次取出并行分发列表中的BroadcastRecord
    r = mParallelBroadcasts.remove(0);
    final int N = r.receivers.size();
    for (int i = 0; i < N; i++) {
      //依次取得Receiver列表中的receiver
      BroadcastFilter target = r.receivers.get(i);
      //然后把广播分发给它
      //下面这个函数将调用performReceiveLocked(filter.receiverList.app, ...);
      deliverToRegisteredReceiverLocked(r, target, false);
    }
  }
  ...
}

performReceiveLocked函数进入广播的接收原理

广播的接收原理

AMS端:

void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver, ...) {
  if (app != null) {
    //AMS通过applicationThread调用应用端的scheduleRegisteredReceiver
    app.thread.scheduleRegisteredReceiver(receiver, intent, ...);
  } else {
    receiver.performReceive(intent, resultCode, data, ...);
  }
}

应用端

void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent, ...) {
  //注意:并没有转到主线程处理,而是直接调用performReceive函数
  receiver.performReceive(intent, resultCode, dataStr, ...);
}

应用端没有把调用转到主线程而是直接调用performReceive函数,那AMS为何不直接调用IntentReceiver的performReceive而要通过applicationThread来调用呢?答案是:为了把广播串行化!因为applicationThread的调用是一个oneway binder调用,所以AMS发起的对这个接口的所有的调用都会被自动 串行化。如果直接调用IntentReceiver的performReceive函数,虽然IntentReceiver也是oneway的,但它只是对这个接口串行化分发。但是AMS希望所有的广播在应用端都是串行化分发的,因此广播尽量从applicationThread走而不是从IntentReceiver走。

广播分发原理图:

在这里插入图片描述

  • 假如AMS要把intent分发到应用的五个receiver
  • 对AMS来说普通的动态广播是并行分发的,加上分发接口又是oneway的,因此对AMS来说调用是异步的——一口所把广播全分发出去了
  • 广播被分发到应用进程时,它分发起来却是同步的——自动把它串行化

IIntentReceiver的performReceive实现

public void performReceive(Intent intent, int resultCode, String data, ...) {
  //先找到ReceiverDispatcher
  ReceiverDispatcher rd = mDispatcher.get();
  rd.performReceive(intent, resultCode, data, extras, ...);
}

public void performReceive(Intent intent, int resultCode, ...) {
  //new 一个Args,Args实现了Runnable
  Args args = new Args(intent, resultCode, data, ...);
  //Post到主线程的Handler中,onReceive最终会在主线程调用
  mActivityThread.post(args);
}

class Args implements Runnable {
  
  public void run() {
    //Args是ReceiverDispatch的内部类,因此它可以拿到BroadcastReceiver的对象
    final BroadcastReceiver receiver = mReceiver;

    receiver.setPendingResult(this);
    //然后调用onReceive函数
    receiver.onReceive(mContext, intent);

    if (receiver.getPendingResult() != null) {
      finish();
    }
  }
}

//收发工作:
//对于普通的动态广播来说,AMS是一口气并行分发的,它不会管应用端处理完没有
//但是静态广播或者是带ORDER标记的动态广播,AMS是要串行化分发的:它要关注上一个广播执行完了没有,执行完了才会分发下一个广播
//finish()函数就是去通知AMS当前这个广播已经执行完了
//对于普通动态广播,其实finishe()什么都没做
public final void finish() {
  if (mType == TYPE_COMPONENT) {
    //静态广播
    final IActivityManager mgr = ActivityManagerNative.getDefault();
    if (QueuedWork.hasPendingWork()) {
      QueueWork.singleThreadExecutor().execute(new Runnable() {
        @Override public void run() {
          //调用sendFinished告诉AMS:当前广播已经执行完了
          sendFinished(mgr);
        }
      });
  }  else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
    //带ORDER标记的动态广播,也要向AMS报告
    IActivityManager mgr = ActivityManagerNative.getDefault();
    sendFinished(mgr);
  }
}

总结动态广播的注册和收发流程

在这里插入图片描述

  • 首先应用A向AMS注册广播,将生成一个binder对象,把这个binder和filter注册到AMS
  • 然后应用B发送广播,广播中带了一个intent
  • AMS会在所有注册了的Receiver里根据intent找到匹配的Receiver,然后开始分发
  • 对于普通的动态广播来说,AMS是并行分发的,但是 广播到达应用端之后变成了串行化分发
  • 通过binder对象找到对应的BroadcastReceiver然后执行它的onReceived函数

回归: 说说动态广播的注册和收发原理

  • 注册广播封装了一个binder对象到AMS
  • 通过广播intent找到匹配的receiver,然后分发
  • 普通动态广播在系统端 并行分发的,应用端串行分发
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值