笔记:BroadcastReceiver的运行过程

广播概述

  • 广播用来在组件之间传递消息,可以是同进程或跨进程。
  • 广播机制是基于发布订阅的事件驱动模型,使用上比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”。

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

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:

private final HashMap<Context, HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>> mReceivers
        = new HashMap<Context, HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();

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

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

3. ActivityManagerProxy.registerReceiver

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。

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:

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。

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更新进度:

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

@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

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():

mRemote.transact(SCHEDULE_REGISTERED_RECEIVER_TRANSACTION, data, null,
               IBinder.FLAG_ONEWAY);

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

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

ApplicationThread.scheduleRegisteredReceiver

// 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

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粘性广播 接收者可以接收到对应类型的它注册前的最后一个广播。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Ryan Miao

centos7查看系统版本,查看机器位数x86-64

前言 由于不经常使用linux,每当使用的时候就是安装软件,安装软件的时候就要选择安装包平台,是32位的还是64位的。这时候突然发现不知道怎么查,于是百度。虽然...

3745
来自专栏石瞳禅的互联网实验室

解决Android AVD启动报错问题

好不容易从ADT Bundle转为Android Studio的开发环境,一路荆棘,现在又遇到了模拟器的问题,本来直接用真机调试程序会更快些,但是为了模拟多种系...

1371
来自专栏开发之途

在 Android 设备上搭建 Web 服务器

一般而言,Android 应用在请求数据时都是以 Get 或 Post 等方式向远程服务器发起请求,那你有没有想过其实我们也可以在 Android 设备上搭建一...

8403
来自专栏移动开发之家

Android插件化快速入门与实例解析(VirtualApk)

集成一个第三方相册功能,只需集成一个插件APK到项目中,无需集成额外代码,并且支持随时更新相册功能,无需发布版本更新,无需AndroidManifest中声明四...

672
来自专栏散尽浮华

Centos下DNS+NamedManager高可用部署方案完整记录

之前说到了NamedManager单机版的配置,下面说下DNS+NamedManager双机高可用的配置方案:

8317
来自专栏高爽的专栏

JNI(一):初识JNI

       通过JNI实现一个Hello world。        1. 在Java类中声明native方法,NativeTest.java。 packag...

2010
来自专栏Android开发实战

Android启动过程分析(图+文)

①点击桌面App图标,Launcher进程采用Binder IPC向system_server进程发起startActivity请求;

1594
来自专栏非著名程序员

Android热修复实践应用—AndFix

一直关注App的热修复的技术发展,之前做的应用也没用使用到什么热修复开源框架。在App的热修复框架没有流行之前,做的应用上线后发现一个小小的Bug,就要马上发一...

2086
来自专栏IT技术精选文摘

Spring Boot应用监控实战

1323
来自专栏小白课代表

Windows 10 让人不爽的骚操作之二!

有没有一丝丝的别扭?倒不是嫌弃这几个文件夹不好看或者不好用,单单这7个文件夹就让人受不了好叭(╯▽╰)

2803

扫码关注云+社区

领取腾讯云代金券