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

    (本文使用Atom编写)

  • 相关阅读:
    比较.NET程序集(DLL或EXE)是否相同
    [转] JavaScript数组去重(12种方法)
    [转] js网络请求跨域问题汇总(携带cookie)
    [转] JS中arr.forEach()如何跳出循环
    [转] vue前端异常监控sentry实践
    [转] vue父组件触发子组件事件
    [转] vue 自定义组件使用v-model
    [转] Nginx配置性能优化
    [转] linux 查找文本过滤grep正则表达式命令详解用法
    [转] Nginx配置中的location、root、alias
  • 原文地址:https://www.cnblogs.com/everhad/p/6246802.html
Copyright © 2020-2023  润新知