前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >笔记:BroadcastReceiver的运行过程

笔记:BroadcastReceiver的运行过程

作者头像
用户1172465
发布2018-01-08 14:29:21
8460
发布2018-01-08 14:29:21
举报
文章被收录于专栏:everhadeverhad

广播概述

  • 广播用来在组件之间传递消息,可以是同进程或跨进程。
  • 广播机制是基于发布订阅的事件驱动模型,使用上比Binder通信(跨进程接口回调)更低耦合、简单。
  • ActivityManagerService(简称AMS)作为广播消息发布订阅的注册中心,广播接收器(Broadcast Receiver,简称Receiver)以静态或动态方式注册到AMS。
  • 广播底层实现就是Binder,包括使用Binder进行跨进程回调接口注册,发送广播时使用Binder异步通信发送广播给接收者所在进程。
  • 继承Context的Service或Activity组件可以发送有序或无需广播到AMS。
  • AMS把消息发送给接收此广播类型的Receiver。
  • 有序广播根据Receiver优先级被接收,动态注册的先收到消息,而无需广播同时发送给所有Receiver。
  • 广播的生命周期:动态注册的广播组件其生命周期和其使用者关联。静态注册的广播,每次收到广播时一个Receiver被创建,在主线程中执行其onReceive()方法,方法返回后,Receiver组件即等待销毁。
  • 因为onReceive在主线程执行,所以耗时操作会引起ANR。
  • 耗时操作应该启动一个Service去执行,不能是bindService()这样的,因为bindService()本身是和Service进行通信的方式,而不是增加Receiver存活时间的方式。onReceive()本身的执行就应该很短。startService()保证一个耗时操作放在Service中得已运行更长的时间得到执行。

NOTE: 使用Broadcast完成组件间的事件通知,在跨进程的情况下,比使用Binder进行跨进程接口回调要简单且更加低耦合。

案例

下面以在MyActivity中注册MyReceiver为例,MyReceiver接收Action为“com.hxw.bot.broadcast.ACTION”。

代码语言:javascript
复制
BroadcastReceiver receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {

    }
};
IntentFilter filter = new IntentFilter("com.hxw.bot.broadcast.ACTION");
registerReceiver(receiver, filter);

一个filter可以拦截多个Action。

广播注册过程

1. ContextWrapper.registerReceiver

2. ContextImpl.registerReceiver

代码语言:javascript
复制
LoadedApk mPackageInfo;
ActivityThread mMainThread;
...
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
        String broadcastPermission, Handler scheduler) {
    return registerReceiverInternal(receiver, filter, broadcastPermission,
            scheduler, getOuterContext());
}
...
private Intent registerReceiverInternal(BroadcastReceiver receiver,
        IntentFilter filter, String broadcastPermission,
        Handler scheduler, Context context) {
    IIntentReceiver rd = null;
    if (receiver != null) {
        if (mPackageInfo != null && context != null) {
            if (scheduler == null) {
                scheduler = mMainThread.getHandler();
            }
            rd = mPackageInfo.getReceiverDispatcher(
                receiver, context, scheduler,
                mMainThread.getInstrumentation(), true);
        } else {
            ...
        }
    }
    try {
        return ActivityManagerNative.getDefault().registerReceiver(
                mMainThread.getApplicationThread(),
                rd, filter, broadcastPermission);
    ...
}

mMainThread.getHandler()返回一个当前进程的主线程上的Handler, scheduler用来将AMS回调rd方法从Binder线程池的线程中转到主线程中进行。

LoadedApk.getReceiverDispatcher()将MyReceiver包装成一个IIntentReceiver rd,rd类型是InnerReceiver(继承自IIntentReceiver.Stub)——一个Binder对象,发送给AMS其代理,完成“广播接收器”回调接口注册。

参数context为ContextImpl.getOuterContext(),它返回MyActivity对象。也就是注册MyReceiver的Context对象,MyReceiver和MyActivity关联。

LoadedApk类有一个字段mReceivers:

代码语言:javascript
复制
private final HashMap<Context, HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>> mReceivers
        = new HashMap<Context, HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();

它以Receiver关联的Context对象(也就是执行注册的context对象)作为key,存储了对应context注册的所有的BroadcastReceiver对象。

代码语言:javascript
复制
class ReceiverDispatcher {
  final IIntentReceiver.Stub mIIntentReceiver; // 作为跨进程接口实例的Binder对象
  final BroadcastReceiver mReceiver; // MyReceiver
  final Context mContext; // MyActivity对象
  final Handler mActivityThread; // 主线程的handler
  ...
}

3. ActivityManagerProxy.registerReceiver

代码语言:javascript
复制
public Intent registerReceiver(IApplicationThread caller,
            IIntentReceiver receiver,
            IntentFilter filter, String perm) throws RemoteException

将参数写入Parcel data,然后向AMS发起进程间通信REGISTER_RECEIVER_TRANSACTION。

4. AMS.registerReceiver

AMS.registerReceiver()响应REGISTER_RECEIVER_TRANSACTION。

代码语言:javascript
复制
ActivityManagerService {
  /**
    * Keeps track of all IIntentReceivers that have been registered for
    * broadcasts.  Hash keys are the receiver IBinder, hash value is
    * a ReceiverList.
    */
   final HashMap mRegisteredReceivers = new HashMap();

   public Intent registerReceiver(IApplicationThread caller,
            IIntentReceiver receiver, IntentFilter filter,
            String requiredPermission) throws RemoteException {
       ...
       ReceiverList rl
                = (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
       ...
       BroadcastFilter bf = new BroadcastFilter(filter, rl, permission);
       rl.add(bf);
       mReceiverResolver.addFilter(bf);
   }
}

参数receiver: AMS收到的“广播接收器”是MyReceiver对应的InnerReceiver的BinderProxy。

AMS使用BroadcastFilter记录已经注册的Receiver:

代码语言:javascript
复制
class BroadcastFilter extends IntentFilter {
  // Back-pointer to the list this filter is in.
  final ReceiverList receiverList;
  final String requiredPermission;

  BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,
          String _requiredPermission) {
      super(_filter);
      receiverList = _receiverList;
      requiredPermission = _requiredPermission;
  }
}

BroadcastFilter保存了关联的filter、receiverList。

代码语言:javascript
复制
class ReceiverList extends ArrayList<BroadcastFilter>
        implements IBinder.DeathRecipient {
    final ActivityManagerService owner;
    public final IIntentReceiver receiver;
    ...
}

ReceiverList记录了某个Receiver——IIntentReceiver对象所接收的所有广播。 因为一个MyReceiver这样的对象可以用来同时接收多种广播类型。

AMS最后使用mReceiverResolver来根据发送的广播对应的IntentFilter找到合适的receiver并 调用。

广播发送过程

在Service或Activity中,这里假设是MyService中,通知MyActivity更新进度:

代码语言:javascript
复制
int progress = 1;
...

Intent intent = new Intent("com.hxw.bot.broadcast.ACTION");
intent.putExtra("progress", progress);

sendBroadcast(intent);

阶段1:发送广播消息给AMS

广播发送者,即Activity或Service组件,将一个特点类型的广播发送给AMS。 这里是MyActivity,发送Action为"com.hxw.bot.broadcast.ACTION"的广播。

Step0:Service.sendBroadcast

广播使用intent对象描述,其Action即广播的类型,intent也可以携带必要的数据。

Step1:ContextWrapper.sendBroadcast

Step2:ContextImpl.sendBroadcast

代码语言:javascript
复制
@Override
public void sendBroadcast(Intent intent) {
    String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
    try {
        ActivityManagerNative.getDefault().broadcastIntent(
            mMainThread.getApplicationThread(), intent, resolvedType, null,
            Activity.RESULT_OK, null, null, null, false, false);
    } catch (RemoteException e) {
    }
}

Step3:ActivityManagerProxy.broadcastIntent

打包参数,向AMS发起进程间通信: mRemote.transact(BROADCAST_INTENT_TRANSACTION, data, reply, 0);

阶段2:AMS找到接收者,通知其线程的消息队列处理广播

AMS收到一个广播后,找到与这个广播对应的接收者,将它们添加到广播调度队列。然后向创建AMS的线程的消息队列发送一个类型为BROADCAST_INTENT_MSG的消息。对于广播发送者来说,一个广播发送完成了。

AMS.unbroadcastIntent:同步和消息队列?

广播发送者向AMS发起的BROADCAST_INTENT_TRANSACTION操作是同步RPC,响应方法应该尽快返回。 对于Binder-IPC通信,AMS.unbroadcastIntent()的执行是在Binder线程中的,Binder线程一般也应该尽快执行完毕。 一个进程对应的Binder线程不止一个,所以AMS.unbroadcastIntent()是同步的,它将将广播的处理转为AMS被创建时的线程中消息队列对消息的处理,自身不执行广播的发送。

广播的发送是异步的,发送者不会等待AMS实际将广播发送给接收者操作完成。

阶段3:AMS消息队列处理BROADCAST_INTENT_MSG

当AMS所运行线程的消息队列中BROADCAST_INTENT_MSG消息被处理时,AMS从广播调度队列中找到需要接收此广播的广播接收者,并将对应的广播发送给它们所运行在的应用程序进程。

AMS.performReceiveLocked

代码语言:javascript
复制
static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
        Intent intent, int resultCode, String data, Bundle extras,
        boolean ordered, boolean sticky) throws RemoteException {
    // Send the intent to the receiver asynchronously using one-way binder calls.
    if (app != null && app.thread != null) {
        // If we have an app thread, do the call through that so it is
        // correctly ordered with other one-way calls.
        app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                data, extras, ordered, sticky);
    } else {
        receiver.performReceive(intent, resultCode, data, extras, ordered, sticky);
    }
}

AMS发送给目标进程广播时,采用异步进程间通信方式。 发送给一个Binder对象的所有异步事务都保存在一个异步事务队列中,其中的事务每次只处理一个,就是队列头部的异步事务。所以,AMS发送给同一个应用程序进程的所有广播都可以被按照发送顺序来串行地接收和处理。

ApplicationThreadProxy.scheduleRegisteredReceiver():

代码语言:javascript
复制
mRemote.transact(SCHEDULE_REGISTERED_RECEIVER_TRANSACTION, data, null,
               IBinder.FLAG_ONEWAY);

阶段4:接收者进程在主线程消息队列中响应广播

广播接收者所运行在的应用程序进程收到AMS发送的广播后,并不是直接将收到的广播分发给MyReceiver处理,而将广播封装为一个消息,发送到主线程的消息队列中。消息被处理时,应用程序进程在主线程中将消息所描述的广播发送给相应的广播接收者MyReceiver。

ApplicationThread.scheduleRegisteredReceiver

代码语言:javascript
复制
// This function exists to make sure all receiver dispatching is
// correctly ordered, since these are one-way calls and the binder driver
// applies transaction ordering per object for such calls.
public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
        int resultCode, String dataStr, Bundle extras, boolean ordered,
        boolean sticky) throws RemoteException {
    receiver.performReceive(intent, resultCode, dataStr, extras, ordered, sticky);
}

参数receiver就是注册过程提供的InnerReceiver(见LoadedApk.java)对象。

接收者所在线程(见ActivityThread.java)将intent所表示的广播封装为一个消息(android.os.Message),然后发送到主线程消息队列中。

Args.run

代码语言:javascript
复制
final class LoadedApk {
  ...
  static final class ReceiverDispatcher {
    final IIntentReceiver.Stub mIIntentReceiver; // 作为跨进程接口实例的Binder对象
    final BroadcastReceiver mReceiver; // MyReceiver
    final Context mContext; // MyActivity对象
    final Handler mActivityThread; // 主线程的handler
    ...

    final static class InnerReceiver extends IIntentReceiver.Stub {
            final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;
            final LoadedApk.ReceiverDispatcher mStrongRef;
            ...
    }

    final class Args implements Runnable {
      private Intent mCurIntent;
      ...
      public void run() {
        IActivityManager mgr = ActivityManagerNative.getDefault();
        ...
        BroadcastReceiver receiver = mReceiver;
        ...
        receiver.onReceive(mContext, intent);
        ...
        mgr.finishReceiver(mIIntentReceiver,
                                   mCurCode, mCurData, mCurMap, false);
      }
    }
  }
}

这里,主线程执行Args.run()方法,得到关联的MyReceiver调用其onReceive()。 如果当前广播是有序广播,那么onReceive()执行完毕后调用mgr.finishReceiver() 通知AMS将广播传递给下一个接收者。

补充

  • Binder线程池
  • Binder异步通信
  • sticky粘性广播 接收者可以接收到对应类型的它注册前的最后一个广播。
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-01-03 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 广播概述
  • 案例
  • 广播注册过程
    • 1. ContextWrapper.registerReceiver
      • 2. ContextImpl.registerReceiver
        • 3. ActivityManagerProxy.registerReceiver
          • 4. AMS.registerReceiver
          • 广播发送过程
            • 阶段1:发送广播消息给AMS
              • Step0:Service.sendBroadcast
              • Step1:ContextWrapper.sendBroadcast
              • Step2:ContextImpl.sendBroadcast
              • Step3:ActivityManagerProxy.broadcastIntent
            • 阶段2:AMS找到接收者,通知其线程的消息队列处理广播
              • AMS.unbroadcastIntent:同步和消息队列?
            • 阶段3:AMS消息队列处理BROADCAST_INTENT_MSG
              • AMS.performReceiveLocked
                • 阶段4:接收者进程在主线程消息队列中响应广播
                  • ApplicationThread.scheduleRegisteredReceiver
                  • Args.run
              • 补充
              相关产品与服务
              消息队列 CMQ 版
              消息队列 CMQ 版(TDMQ for CMQ,简称 TDMQ CMQ 版)是一款分布式高可用的消息队列服务,它能够提供可靠的,基于消息的异步通信机制,能够将分布式部署的不同应用(或同一应用的不同组件)中的信息传递,存储在可靠有效的 CMQ 队列中,防止消息丢失。TDMQ CMQ 版支持多进程同时读写,收发互不干扰,无需各应用或组件始终处于运行状态。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档