美文网首页
从源码看广播之广播发送流程

从源码看广播之广播发送流程

作者: Marco黑八 | 来源:发表于2018-06-14 15:07 被阅读0次

    前言

    一直想梳理一下广播的注册和发送流程。有次面试我被问了许多关于广播的问题,很多问题都能从源码中找到答案,其中一些我也看过源码,但当时问的时候完全懵逼了。有些问题甚至没有深入考虑过,问的时候全靠猜。所以在此是做个梳理,也希望读者读完这篇文章,能帮助你解决以下问题。

    1、onReceive执行在哪个线程

    2、广播是如何发送的

    3、进程没启动的时候接收静态广播是否会启动进程

    PS:写关于源码的文章自己也比较头疼,不知道从何开始从何结束,源码复杂不说很多内容还交叉在一起。过于深入自己又感觉得不偿失,毕竟不是做framework的。所以对文章有任何意见或者建议,欢迎留言。


    广播是如何发送和接收的

    广播的发送主要分为两步。第一步是收集匹配的广播接收器,第二步就是给匹配的接收器发送广播。
    我们从activity#sendBroadcast来说说整个广播的发送流程。首先需要明白,调用sendBroadcast的时候,请求都被转发到了ContextImpl中。ContextWrapper其实是ContextImpl的包装类,这是一个装饰者设计模式。这里尽量不大范围贴源码了,我画了个类图。

    Context类图.png

    我们直接从ContextImpl开始看起,这里其实也只是把请求交给了ActivityManagerProxybroadcastIntent方法就是通过binder发起一个异步请求给AMS,而在AMS的broadcastIntent方法中又调用了broadcastIntentLocked方法。

    ContextImpl.class

        @Override
        public void sendBroadcast(Intent intent) {
            warnIfCallingFromSystemProcess();
            String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
            try {
                intent.prepareToLeaveProcess(this);
                ActivityManagerNative.getDefault().broadcastIntent(
                        mMainThread.getApplicationThread(), intent, resolvedType, null,
                        Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
                        getUserId());
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
    
    

    ActivityManagerProxy.class

        public int broadcastIntent(IApplicationThread caller,
                Intent intent, String resolvedType, IIntentReceiver resultTo,
                int resultCode, String resultData, Bundle map,
                String[] requiredPermissions, int appOp, Bundle options, boolean serialized,
                boolean sticky, int userId) throws RemoteException
        {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            data.writeInterfaceToken(IActivityManager.descriptor);
            data.writeStrongBinder(caller != null ? caller.asBinder() : null);
            intent.writeToParcel(data, 0);
            data.writeString(resolvedType);
            data.writeStrongBinder(resultTo != null ? resultTo.asBinder() : null);
            data.writeInt(resultCode);
            data.writeString(resultData);
            data.writeBundle(map);
            data.writeStringArray(requiredPermissions);
            data.writeInt(appOp);
            data.writeBundle(options);
            data.writeInt(serialized ? 1 : 0);
            data.writeInt(sticky ? 1 : 0);
            data.writeInt(userId);
            mRemote.transact(BROADCAST_INTENT_TRANSACTION, data, reply, 0);//通过binder调用broadcastIntent方法
            reply.readException();
            int res = reply.readInt();
            reply.recycle();
            data.recycle();
            return res;
        }
    

    可以看到这里通过binder调用了AMS的broadcastIntent方法。里面调用了broadcastIntentLocked,其中代码非常多。包括权限检查和一些系统广播的处理,这里就关注静态广播、动态广播的查询和发送。

    广播发送及动态广播接收器接收时序图.png

    广播接收器的收集

    Step1 静态广播接收器的查询

    在该方法中调用了collectReceiverComponents查询和intent匹配的静态广播。

    ActivityManagerService.class

    private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
                int callingUid, int[] users) {
                    //...
        List<ResolveInfo> newReceivers = AppGlobals.getPackageManager().queryIntentReceivers(intent, resolvedType, pmFlags, user).getList();
                   //...
        return receivers;
    }
    
    

    可以看到,这里通过PMS的queryIntentReceivers方法查询和intent匹配的广播。这也就说明,静态广播是由PMS管理的

    Step2 动态广播接收器的查询

    动态广播通过queryIntent查询

    ActivityManagerService.class

            if (intent.getComponent() == null) {
                if (userId == UserHandle.USER_ALL && callingUid == Process.SHELL_UID) {
                    // Query one target user at a time, excluding shell-restricted users
                    for (int i = 0; i < users.length; i++) {
                        if (mUserController.hasUserRestriction(
                                UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {
                            continue;
                        }
                        List<BroadcastFilter> registeredReceiversForUser =
                                mReceiverResolver.queryIntent(intent,
                                        resolvedType, false, users[I]);
                        if (registeredReceivers == null) {
                            registeredReceivers = registeredReceiversForUser;
                        } else if (registeredReceiversForUser != null) {
                            registeredReceivers.addAll(registeredReceiversForUser);
                        }
                    }
                } else {
                    registeredReceivers = mReceiverResolver.queryIntent(intent,
                            resolvedType, false, userId);
                }
            }
    
    

    广播的发送

    Step1 发送广播给动态广播接收器

    根据查询出的receivers构造一个BroadcastRecord,接着调用enqueueParallelBroadcastLocked将广播接收者添加到BroadcastQueue中。最后调用scheduleBroadcastsLocked,这个方法的逻辑主要是把广播发送到之前所收集的广播接收者当中。

    ActivityManagerService.class

    final BroadcastQueue queue = broadcastQueueForIntent(intent);
    BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
        callerPackage, callingPid, callingUid, resolvedType, requiredPermissions,
        appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData,
        resultExtras, ordered, sticky, false, userId);
        final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
        if (!replaced) {
            queue.enqueueParallelBroadcastLocked(r);
            queue.scheduleBroadcastsLocked();
        }
    

    scheduleBroadcastsLocked中发送了一个BROADCAST_INTENT_MSG消息。

    BroadcastQueue.class

    public void scheduleBroadcastsLocked() {
        if (mBroadcastsScheduled) {
            return;
        }
        mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
        mBroadcastsScheduled = true;
    }
    

    BroadcastQueue收到消息会调用processNextBroadcast方法。在该方法中会先发送无序广播。看一下发送无序广播的逻辑。其他类型的广播发送大同小异。

    BroadcastQueue.class

    // First, deliver any non-serialized broadcasts right away.
    while (mParallelBroadcasts.size() > 0) {
        r = mParallelBroadcasts.remove(0);
        r.dispatchTime = SystemClock.uptimeMillis();
        r.dispatchClockTime = System.currentTimeMillis();
        final int N = r.receivers.size();
        for (int i=0; i<N; i++) {
            Object target = r.receivers.get(i);
            deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
        }
        addBroadcastToHistoryLocked(r);
    }
    

    在其内部调用deliverToRegisteredReceiverLocked,该方法内部又通过performReceiveLocked把消息发送给特定的广播接收器。

    BroadcastQueue.class

    void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
                Intent intent, int resultCode, String data, Bundle extras,
                boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
        if (app != null) {
            if (app.thread != null) {
                //调用scheduleRegisteredReceiver发送广播到接收器
                app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                            data, extras, ordered, sticky,sendingUser,app.repProcState);
                //...
        } else {
            receiver.performReceive(intent, resultCode, data, extras, ordered,
                        sticky, sendingUser);
        }
    }
    
    

    这里的app.threadApplicationThread,一般都不为null。ApplicationThreadActivityThread的内部类。

    ActivityThread.class

    public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
                    int resultCode, String dataStr, Bundle extras, boolean ordered,
                    boolean sticky, int sendingUser, int processState) throws RemoteException {
        updateProcessState(processState, false);
        receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
                        sticky, sendingUser);
    }
    

    IIntentReceiver对象的performReceive会调用LoadedApk.ReceiverDispatcherperformReceive方法。在其内部会构建一个Args对象,该类实现的了Runnable接口,接着通过mActivityThread执行args

    LoadedApk.class#Args.class

    public void run() {
        final BroadcastReceiver receiver = mReceiver;
        final boolean ordered = mOrdered;
                    
        final IActivityManager mgr = ActivityManagerNative.getDefault();
        final Intent intent = mCurIntent;
    
        mCurIntent = null;
        mDispatched = true;
        try {
        ClassLoader cl =  mReceiver.getClass().getClassLoader();
        intent.setExtrasClassLoader(cl);
        intent.prepareToEnterProcess();
        setExtrasClassLoader(cl);
        receiver.setPendingResult(this);
        receiver.onReceive(mContext, intent);
        } catch (Exception e) {
        }
    }
    

    可以看到args对象的run方法最终调用了receiver.onReceive

    所以onReceive方法在哪个线程执行?

    在主线程。因为广播接收最后会通过mH回调到主线程去执行onReceive方法,所以onReceive是执行在主线程的。

    Step2 发送广播给静态广播接收器

    把广播发送到静态广播接收器的逻辑与发送到动态广播接收器的逻辑大同小异,都需要调用到processNextBroadcast中。需要注意区分两种情况,一种是进程已经启动,一种是进程尚未启动。

    // Is this receiver's application already running?
    if (app != null && app.thread != null) {
        //...
        processCurBroadcastLocked(r, app);
    }
    
    Situation1 进程已经启动

    如果进程已经启动,就会调用processCurBroadcastLocked发送广播。其中又会调用processCurBroadcastLocked。其中又会调用app.thread.scheduleReceiver发送广播。app.thread是ActivityThread。

      app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
                        mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
                        r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
                        app.repProcState);
    

    ActivityThread.class

    public final void scheduleReceiver(Intent intent, ActivityInfo info,
                    CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
                    boolean sync, int sendingUser, int processState) {
                updateProcessState(processState, false);
                ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
                        sync, false, mAppThread.asBinder(), sendingUser);
                r.info = info;
                r.compatInfo = compatInfo;
                sendMessage(H.RECEIVER, r);
            }
    

    这个方法很简单,就是发了一个RECEIVER消息。所以静态广播的onReceive方法也会运行在主线程mH接收到这个消息会调用到handleReceiver,通过反射构建receiver实例。

    Situation2 进程未启动

    如果进程没有启动呢?接着看回processNextBroadcast,在该方法的最后面有这样一段逻辑,它主要的作用就是拉起app进程。如果应用已经启动,是不会执行到这段逻辑。startProcessLocked这个方法需要特别留意,这个方法就是用来拉起进程的。

    if ((r.curApp=mService.startProcessLocked(targetProcess,        info.activityInfo.applicationInfo, true,
    r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
    "broadcast", r.curComponent,
    (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
     == null) {
        logBroadcastReceiverDiscardLocked(r);
        finishReceiverLocked(r, r.resultCode, r.resultData,
        r.resultExtras, r.resultAbort, false);
        scheduleBroadcastsLocked();
        r.state = BroadcastRecord.IDLE;
        return;
    }
    

    所以进程没启动的时候接收静态广播会启动进程,并且会调用Application的onCreate方法


    总结

    整个广播发送的流程大致如下
    1.查询和Intent匹配的静态广播接收器
    2.查询和intent匹配的动态广播接收器
    3.发送广播给动态广播接收器
    4.发送广播给静态广播接收器

    最后通过开篇的三个问题总结一下这篇的内容

    • onReceive执行在哪个线程
      • 主线程
    • 广播是如何发送的
      • 利用Binder机制通过AMS发送,最终都通过mH回调到主线程
    • 进程没启动的时候接收静态广播是否会启动进程
      • 会启动进程

    相关文章

      网友评论

          本文标题:从源码看广播之广播发送流程

          本文链接:https://www.haomeiwen.com/subject/tnryeftx.html