值得一看的Android广播分析好文

本文基于Android 12进行广播流程的分析,主要从四个方面:广播的注册、解注册、处理、结束四方面进行分析,会比较全面、按个人理解对广播进行解析。但个人能力有限,可能存在部分理解错误,但绝对是一篇理解Android 广播流程的好文。和自己前年写的广播分析,简直打脸自己。原来,想要进步,就是推翻自己,重新再来。

在AMS中持有集合用于存储所有的广播,应用程序可以从向其注册和解注册广播。当应用发送广播时,AMS检查相关权限和特殊的Intent。然后再根据对应IntentFilter匹配到一个或多个Receiver,在应用进程回调其onReceive函数。

广播的注册

广播的注册分动态注册和静态注册两种,静态注册是指将BroadcastReceiverIntentFilter写在配置清单里,Android系统在解析包信息的时候,会将其添加到PackageManagerServicemComponentResolver中,后续处理广播会从中查找匹配的接收者BroadcastReceiver。而动态注册,指的是在程序运行后,通过代码进行注册,下面分析的是动态注册的内容。

我们常在ActivityService、甚至在Application中调用registerReceiver函数来动态注册广播,该函数其实来自他们的父类ContextWrapper中。ContextWrapperContext的子类,我们会在介绍Context的文章介绍它们的关系。

public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
    return mBase.registerReceiver(receiver, filter);
}

这里Context类型的mBase,在Activity的创建过程会被赋值为ContextImpl实例。


public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
    return registerReceiver(receiver, filter, null, null);
}
    
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
    String broadcastPermission, Handler scheduler) {
	return registerReceiverInternal(receiver, getUserId(),
        	filter, broadcastPermission, scheduler, getOuterContext(), 0);
}

经过registerReceiver重载函数,接着调用registerReceiverInternal函数。

ContextImpl.registerReceiverInternal

registerReceiverInternal函数,执行到注释1处,说明当前进程还存活着的,通过LoadedApk对象mPackageInfogetReceiverDispatcher函数,根据contextmReceivers容器中获ArrayMap对象map,再以receiverKeymap查询对应的ReceiverDispatcher对象。也就是说,一个Context对象,可能是ActivityApplicationService,会注册一个或多个广播接收者BroadcastReceiver,而一个广播接收者对应一个ReceiverDispatcher。后续广播的处理会通过ReceiverDispatcher找到对应的BroadcastReceiver,将Intent传递给它处理。

如果查询不到ReceiverDispatcher对象,说明之前没有添加过,则创建ReceiverDispatcher对象(内部会创建InnerReceiver对象),按照前面获取的逻辑,进行逆操作,最终将receiver添加到mReceivers映射中。如果已经存在,则更新ContextHandler对象。

image-20240105214017012

有了ReceiverDispatcher对象后,通过其getIIntentReceiver函数获得InnerReceiver对象。InnerReceiver继承自IIntentReceiver.Stub,说明InnerReceiver会在进程之间传递。

注释2,相对于注释1,少传递了Instrumentation对象,我们知道Instrumentation使用来监视系统与应用程序之间的交互的,注释2处,由于应用程序未启动完毕,所以不需要。

注释3调用了AMS的registerReceiverWithFeature函数。这时候离开了应用所在的进程。总结的说,这里缓存我们注册的BroadcastReceiver对象,将相关信息封装成IIntentReceiver对象,传递给了AMS

private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
        IntentFilter filter, String broadcastPermission,
        Handler scheduler, Context context, int flags) {
    IIntentReceiver rd = null;
    if (receiver != null) {
        //应用已启动
        if (mPackageInfo != null && context != null) {
            if (scheduler == null) {
                //主线程的H对象,用于接收广播用于保持receivcer广播顺序到达问题
                scheduler = mMainThread.getHandler();
            }
            //注释1:已注册,则获取旧的对象,否则新创建
            rd = mPackageInfo.getReceiverDispatcher(
                receiver, context, scheduler,
                mMainThread.getInstrumentation(), true);
        } else {
            if (scheduler == null) {
                scheduler = mMainThread.getHandler();
            }
            //注释2
            rd = new LoadedApk.ReceiverDispatcher(
                    receiver, context, scheduler, null, true).getIIntentReceiver();
        }
    }
    try {
        //注释3,返回值是第一个匹配IntentFilter的黏性广播的Intent或者null
        final Intent intent = ActivityManager.getService().registerReceiverWithFeature(
                mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(), rd,
                filter, broadcastPermission, userId, flags);
        if (intent != null) {
            intent.setExtrasClassLoader(getClassLoader());
            intent.prepareToEnterProcess();
        }
        return intent;
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

AMS.registerReceiverWithFeature

我们知道,一个广播接收者(BroadcastReceiver)可能会多个匹配条件(IntentFilter)。AMSregisterReceiverWithFeature函数的主要功能是处理这层关系和存储它们。我们将涉及的几个重要成员用下图的关系表达出来。

image-20240105201502308

  • mRegisteredReceivers:AMS的成员变量,类型为HashMap<IBinder, ReceiverList>,Hash KeyIIntentReceiverIBinder对象,也就是客户端BroadcastReceiver在客户端的代表。而Value就是ReceiverList对象。

  • ReceiverList:继承自ArrayList<BroadcastFilter>,用于存储BroadcastFilter对象,表示一个BroadReceiver所对应的多个IntenerFilter的关系。

  • BroadcastFilter: 内部封装了IntentFilter,可以表示为IntentFilter在系统层的代表。

  • mReceiverResolver:存储BroadcastFilter,表示当前所有的IntentFilter对象。后续发送广播时,如果广播没有component,需要通过它区解析匹配的接收者。

registerReceiverWithFeature函数首先根据receiver.asBinder()mRegisteredReceivers映射中查找是否有历史的ReceiverList对象。如果没有历史记录,则创建ReceiverList对象,并添加到mRegisteredReceivers中和receiver所在进程的ProcessRecordmReceivers中。一个应用程序允许创建最多的ReceiverList对象数量是1000个

接着会将相关信息,主要是IntentFilter,封装成BroadcastFilter对象。第一次会将该BroadcastFilter对象添加到对应的ReceiverList对象。也会添加到mReceiverResolver中。

registerReceiverWithFeature函数的另一个功能就是对黏性广播的处理。我们知道广播分三种类型:无序广播、有序广播、黏性广播。黏性广播的处理时机在此处,其他两个类型的处理可以看后文广播的发送小节。

先从AMS的mStickyBroadcasts数组中找出发给当前用户的Intent数组stickyIntents,包括发给当前用户和所有用户的黏性Intent。然后再在stickyIntents数组中找出与IntentFilter匹配的所有Intent,存储到allSticky中。然后根据allSticky数组,创建出每个Intent对应的BroadcastRecord(一个BroadcastRecord代表着一个广播),并将它们的sticky属性赋值为true,表示是黏性广播。然后加入到并行广播队列中,调用队列的 scheduleBroadcastsLocked的函数执行广播的处理流程。总结的说,就是找出与当前IntentFilter匹配的黏性广播,交给其receiver处理。

到这里,广播的注册就完成了。

image-20240105225704698

广播的解注册

有了对广播注册流程的了解,那么对广播的解注册流程理解,会容易很多。也就是在注册过程中,在LoadedApk对象加入到mReceiver中,现在就要将其移除,并重置相关属性。在AMS中,注册时是加入mRegisteredReceiversReceiverList,以及mReceiverResolver,那么解注册就是从它们移除。

回到ContextWrapperunregisterReceiver函数。

//ContextWrapper
public void unregisterReceiver(BroadcastReceiver receiver) {
    mBase.unregisterReceiver(receiver);
}

//ContextImpl
public void unregisterReceiver(BroadcastReceiver receiver) {
    if (mPackageInfo != null) {
        //LoadedApk
        IIntentReceiver rd = mPackageInfo.forgetReceiverDispatcher(
                getOuterContext(), receiver);
        try {
            //AMS
            ActivityManager.getService().unregisterReceiver(rd);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    } else {
        throw new RuntimeException("Not supported in system context");
    }
}

LoadedApkforgetReceiverDispatcher函数的处理逻辑是和注册时getReceiverDispatcher函数反着来。根据context对象在mReceivers映射中获取当前context的所有BroadcastReceiver对应的ReceiverDispatcher对象。这是得到的是一个ArrayMap对象map,再在该map中根据BroadcastReceiver对象获取对应的ReceiverDispatcher。假如存在的话,依次从mapmReceiver(没有其他receiver情况下)中移除。如果不存在map对象中,还要从已解注册列表mUnregisteredReceivers中查看是否存在,如果存在,报下异常。

接着调用AMS的unregisterReceiver函数。AMS的解注册稍微复杂一点。如果此时正有有序广播在处理,那么要调用广播队列的finishReceiverLocked函数,如果返回结果为true,则调用队列的processNextBroadcastLocked将广播传递给下一个receiver。然后将ReceiverList对象从mRegisteredReceivers中移除,BroadcastFilter对象从mReceiverResolver中移除。

public void unregisterReceiver(IIntentReceiver receiver) {
    final long origId = Binder.clearCallingIdentity();
    try {
        boolean doTrim = false;

        synchronized(this) {
            ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
            if (rl != null) {
                //当前正在处理的有序广播,可以参考=》广播的结束小节
                final BroadcastRecord r = rl.curBroadcast;
                if (r != null && r == r.queue.getMatchingOrderedReceiver(r)) {
                    final boolean doNext = r.queue.finishReceiverLocked(
                            r, r.resultCode, r.resultData, r.resultExtras,
                            r.resultAbort, false);
                    if (doNext) {
                        doTrim = true;
                        //参考=》广播队列对广播的处理小节
                        r.queue.processNextBroadcastLocked(/* frommsg */ false,
                                /* skipOomAdj */ true);
                    }
                }
				//ProcessRecord移除
                if (rl.app != null) {
                    rl.app.mReceivers.removeReceiver(rl);
                }
                //从mRegisteredReceivers移除
                //从mReceiverResolver移除
                removeReceiverLocked(rl);
                if (rl.linkedToDeath) {
                        rl.linkedToDeath = false;
                        rl.receiver.asBinder().unlinkToDeath(rl, 0);
                    }
                }

                //清理进程
                if (doTrim) {
                    trimApplicationsLocked(false, OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER);
                    return;
                }

    } finally {
        Binder.restoreCallingIden

广播的处理

ContextWrappersendBroadcast函数,调用了ContextImplsendBroadcast函数,进而调用了AMSbroadcastIntentWithFeature函数。

public void sendBroadcast(Intent intent) {
    mBase.sendBroadcast(intent);
}

public void sendBroadcast(Intent intent) {
    warnIfCallingFromSystemProcess();
    String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
    try {
        intent.prepareToLeaveProcess(this);
        //AMS
        ActivityManager.getService().broadcastIntentWithFeature(mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false,false, getUserId());
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

1、AMS对广播的处理

调用AMSbroadcastIntentWithFeature函数。

verifyBroadcastLocked函数,对Intent的合法性进行检查,下面三种情况都会抛出异常。

  • 携带文件描述符;
  • 系统未启动完毕,当前Intent发送给所有的接收者。
  • 开机升级广播。

如果Intent携带Intent.FLAG_RECEIVER_FROM_SHELLFlags,即该广播时从Shell发送出来,且调用者不是系统进程或者Shell进程的UID,则需要将该Flags移除,定义为普通的广播。

public final int broadcastIntentWithFeature(IApplicationThread caller, String callingFeatureId,
        Intent intent, String resolvedType, IIntentReceiver resultTo,
        int resultCode, String resultData, Bundle resultExtras,
        String[] requiredPermissions, String[] excludedPermissions, int appOp, Bundle bOptions,
        boolean serialized, boolean sticky, int userId) {
   
    synchronized(this) {
   	   //对广播Intent合法性检查
       intent = verifyBroadcastLocked(intent);

        final ProcessRecord callerApp = getRecordForAppLOSP(caller);
        final int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();

        final long origId = Binder.clearCallingIdentity();
        try {
            //下一个调用,会在调用重载函数
            return broadcastIntentLocked(callerApp,
                    callerApp != null ? callerApp.info.packageName : null, callingFeatureId,
                    intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
                    requiredPermissions, excludedPermissions, appOp, bOptions, serialized,
                    sticky, callingPid, callingUid, callingUid, callingPid, userId);
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }
}

接着调用了broadcastIntentLocked函数。根据该函数内部处理逻辑顺序,大概出下面主要功能。

  • Instant应用禁止发送广播给Instant应用接收者,即快应用之间禁止通过广播交互。

  • 系统未启动完成(非升级过程),禁止通过广播启动新的进程。

  • 广播发给特定用户,用户或者父用户没有运行,则不发送广播。

  • 广播发送时,设置options数据,需要根据options情况,检查白名单、受限目标、后台启动Activity等权限进行检查。

  • 检查广播Action,判断是否受保护的广播的Action,即声明在framework/base/core/res/AndroidMenifest.xml文件内的protected-broadcast,这些广播只能由系统应用发出,例如息屏android.intent.action.SCREEN_OFF。非系统应用发送这些广播会报异常。同时非系统应用在发送AppWidgetManager.ACTION_APPWIDGET_CONFIGUREAppWidgetManager.ACTION_APPWIDGET_UPDATE广播时,只能发给自己,这些是操作自己的桌面小组件。

  • 如果当前广播Action属于系统的隐式广播一种,例如ACTION_LOCALE_CHANGED,那么添加上Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND标志,表示广播允许被静态注册的接收者收到。

  • 如果广播Action属于系统的一些特殊广播,需要进行一些特别的处理,例如时间变化ACTION_TIME_CHANGEDACTION_TIMEZONE_CHANGED;应用相关的变化ACTION_PACKAGE_ADDEDACTION_PACKAGE_DATA_CLEAREDACTION_PACKAGE_CHANGED等等很多。

以上的逻辑基本是针对系统的一些机制进行特殊逻辑处理,而接下来就是普通广播进行处理,而广播又三种类型,分为黏性、无序、有序广播。

  • 黏性广播
    • 需要检测发送者是否声明BROADCAST_STICKY权限,且该广播不能携带其他权限检查。

    • 不能指定具体的目标组件。即Intent不能设置setComponent函数。

    • 非全局的黏性广播不能与已存在全局的黏性广播相同(冲突)。

    • 黏性广播的处理:

      mStickyBroadcasts获取当前用户所有的黏性广播stickies,我们知道,广播注册时会从mStickyBroadcasts中取出黏性广播发给广播队列进行处理。根据当前黏性广播的actionstickies映射中查询,查看是否有相同action的黏性广播(list集合),如果有且IntentFilter相同,则替代旧的,不相同则添加到list中。也就是说黏性广播发送只会添加到黏性广播容器中,在广播接收者注册时候才被处理。

接下来,与当前广播Intent匹配的广播接收者,静态注册会被收集到receivers集合中(FLAG_RECEIVER_REGISTERED_ONLY没有设置),动态注册的会被收集到registerReceiver容器。

  • 无序广播

    如果当前广播属于普通,无序的,且registerReceiver集合有收集到匹配的接收者。

    1. 如果广播发送者是系统应用,需要调用checkBroadcastFromSystem函数检测一下广播的action,做一些警告。
    2. 根据Intentflags获取当前广播属于前台广播队列或后台广播队列。
    3. 广播相关信息封装成BroadcastRecord对象,添加到广播队列的并行集合中mParallelBroadcasts,表示接收者之间可以同时处理该广播。
    4. 调用广播队列BroadcastQueue的``scheduleBroadcastsLocked`函数。

那么接下来,还有静态注册的广播接收者(假如有的话)和有序广播没有处理。在处理完无序广播之后,registerReceiver会被清空。根据所有接收者(receiversregisterReceiver)的优先权合并到同一个同一个队列中,优先权高的放在前面。这里面有几种情况:

  • 如果广播是无序广播,那么不会有合并到同一个队列的动作,因为有判断条件。这时候,接下来处理的是无序广播被静态注册接收者处理的部分。

  • 如果广播是有序广播,那么不会执行无序广播的处理逻辑,receiversregisterReceiver被合并在一起。然后就是下面的有序广播处理逻辑。

  • 有序广播

    1. 如果广播发送者是系统应用,需要调用checkBroadcastFromSystem函数检测一下广播的action,做一些警告。
    2. 根据Intentflags获取当前广播属于前台广播队列或后台广播队列。
    3. 广播相关信息封装成BroadcastRecord对象,添加到广播队列的顺序集合中mOrderedBroadcasts
    4. 调用广播队列BroadcastQueue的``scheduleBroadcastsLocked`函数。

也就是说AMS的broadcastIntentLocked函数主要处理一些特殊的Action。然后根据广播类型:黏性、无序、有序,添加到队列的不同集合中,然后调用广播队列BroadcastQueuescheduleBroadcastsLocked函数对它们进一步处理。

而广播队列分三种类型:前台(优先级最高)、后台(普通优先级)、长广播(耗时很长,例如开机广播)

特殊Flags处理

  • 默认会给广播Intent对象添加上Intent.FLAG_EXCLUDE_STOPPED_PACKAGES,表示广播不发送给处于停止状态(没有在运行)的BroadcastReceiver。意味着正常情况下,如果我们程序被退出,将无法接收任何广播。
  • FLAG_RECEIVER_REGISTERED_ONLY表示广播只能被动态注册的接收者接收。

2、广播队列对广播的处理

scheduleBroadcastsLocked函数调用mHandler对象发送一个BROADCAST_INTENT_MSG消息。

//BroadcastQueue xxm
public void scheduleBroadcastsLocked() {
    if (mBroadcastsScheduled) {//避免重复执行
        return;
    }
    //BroadcastHandler
    mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
    mBroadcastsScheduled = true;
}

BroadcastHandlerhandleMessage函数对BROADCAST_INTENT_MSG消息的处理,直接调用了processNextBroadcast函数。

#BroadcastHandler xxm
public void handleMessage(Message msg) {
    switch (msg.what) {
        case BROADCAST_INTENT_MSG: {
            processNextBroadcast(true);//执行该函数
        } break;
        case BROADCAST_TIMEOUT_MSG: {
            synchronized (mService) {
                broadcastTimeoutLocked(true);
            }
        } break;
    }
}

private void processNextBroadcast(boolean fromMsg) {
    synchronized (mService) {
        processNextBroadcastLocked(fromMsg, false);
    }
}

processNextBroadcastLocked函数第一步是处理无序广播,即将mParallelBroadcasts列表中的广播发送给它们的接收者进行处理,这里调用了deliverToRegisteredReceiverLocked函数。

//Broadcast对象r来自mParallelBroadcasts集合,N即为receivers长度
for (int i=0; i<N; i++) {
    Object target = r.receivers.get(i);
    deliverToRegisteredReceiverLocked(r,
            (BroadcastFilter) target, false, i);
}

第二步是对有序广播进行处理:

  1. 根据mPendingBroadcast==null来判断当前是否正在等待接收者进程完成启动来处理广播。mPendingBroadcast会在后续进程后被赋值。如果mPendingBroadcast不为null,且进程活着,则继续等待,也就是说,当进程启动完毕之后,处理完广播后,在某个地方会重置mPendingBroadcast,并走到此处。如果进程死亡,则指向下一个接收者,重置mPendingBroadcast

  2. 获取下一个广播,getNextBroadcastLocked函数获取。

    优先闹钟广播,接着到期广播,最后有序广播。如果没有获取到广播,调整odj和回收一些资源,那么执行到此处就结束了。

    final long now = SystemClock.uptimeMillis();
    //优先获取闹钟广播,接着到期广播,最后有序广播
    r = mDispatcher.getNextBroadcastLocked(now);//获取下一个BroadcastRecord
    if (r == null) {//没有广播需要处理
        ...
        return;
    }
    
    
  3. 丢弃早期的超时广播,即当前时间已经超过了广播约定的处理时间2 * mConstants.TIMEOUT * numReceivers,那么就结束强行结束该广播。

    //当前时间超过了广播约定的处理时间,结束该广播
    int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
    if (mService.mProcessesReady && !r.timeoutExempt && r.dispatchTime > 0) {
        if ((numReceivers > 0) &&
                (now > r.dispatchTime + (2 * mConstants.TIMEOUT * numReceivers))) {
            //最终调用finishReceiverLocked,参考广播的结束
            broadcastTimeoutLocked(false); // forcibly finish this broadcast
            forceReceive = true;
            r.state = BroadcastRecord.IDLE;
        }
    }
    
    
  4. 如果广播BraodcastRecord对象没有接收者(receivers ==null),或接收者都已经处理完毕或跳过(nextReceiver >= numReceivers),或者广播被放弃resultAbort、或因为超时被强制丢弃forceReceive,都判断为广播在当前receiver处理结束,调用performReceiveLocked函数。也就意味着一个有序广播处理结束

    //广播的默认状态是IDLE,也就是位对广播处理
    if (r.state != BroadcastRecord.IDLE) {
        return;
    }
    
    //四个条件来判断广播在所有的receiver中处理完毕
    if (r.receivers == null || r.nextReceiver >= numReceivers
            || r.resultAbort || forceReceive) {
        // Send the final result if requested
        if (r.resultTo != null) {
            boolean sendResult = true;
    
            // splitToken是用于处理延期广播的一个特殊计数
            if (r.splitToken != 0) {
                int newCount = mSplitRefcounts.get(r.splitToken) - 1;
                if (newCount == 0) {
                    mSplitRefcounts.delete(r.splitToken);
                } else {
                    sendResult = false;
                    mSplitRefcounts.put(r.splitToken, newCount);
                }
            }
            if (sendResult) {
                if (r.callerApp != null) {
                    mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(
                            r.callerApp);
                }
                try {
                    //广播下个处理的地方
                    performReceiveLocked(r.callerApp, r.resultTo,
                            new Intent(r.intent), r.resultCode,
                            r.resultData, r.resultExtras, false, false, r.userId);
                    r.resultTo = null;
                } catch (RemoteException e) {
                    r.resultTo = null;
                }
            }
        }
    	//将广播添加到历史消息
        cancelBroadcastTimeoutLocked();
        addBroadcastToHistoryLocked(r);
        if (r.intent.getComponent() == null && r.intent.getPackage() == null
                && (r.intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
            mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage,
                    r.manifestCount, r.manifestSkipCount, r.finishTime-r.dispatchTime);
        }
        mDispatcher.retireBroadcastLocked(r);
        r = null;
        looped = true;
        continue;
    }
    
    

    如果当前需要将结果传递给调用者,那么需要调用performReceiveLocked函数。

  5. Broadcast对象deferred属性为true时,说明当前广播是从延期广播数组获取的,不需要再进行延期判断。为false时,判断当前广播后续接收者的进程对广播是否需要延期处理(如果之前处理广播所花费的时间超过约定时间,该进程会被记录)

    //在被加入延期广播的时候为true,默认为false
    if (!r.deferred) {
        //广播下一个接收者的进程uid
        final int receiverUid = r.getReceiverUid(r.receivers.get(r.nextReceiver));
        //根据uid在mDeferredBroadcasts列表获取是否有延期的广播
        if (mDispatcher.isDeferringLocked(receiverUid)) {
            BroadcastRecord defer;
            if (r.nextReceiver + 1 == numReceivers) {//只有一个接收者,不需要设计splitToken
                defer = r;
                mDispatcher.retireBroadcastLocked(r);
            } else {//多个接收者
                //splitRecipientsLocked函数会检查当前广播剩下的receiver,获取于当前receiver相同进程的receiver列表,新建broadcast对象,把receiver列表加到该对象中,也就是这里的defer
                defer = r.splitRecipientsLocked(receiverUid, r.nextReceiver);
                // Track completion refcount as well if relevant
                if (r.resultTo != null) {
                    int token = r.splitToken;
                    if (token == 0) {
                        // 第一次给广播设计splitToken,本质了i++。叠加mSplitRefcounts计数
                        r.splitToken = defer.splitToken = nextSplitTokenLocked();
                        mSplitRefcounts.put(r.splitToken, 2);
                    } else {
                        //后续只需要增加mSplitRefcounts计数
                        final int curCount = mSplitRefcounts.get(token);
                        mSplitRefcounts.put(token, curCount + 1);
                    }
                }
            }
            //将广播添加到延期队列中,这里deferred会被设置为true;
            mDispatcher.addDeferredBroadcast(receiverUid, defer);
            r = null;
            looped = true;
            continue;
        }
    }
    
    

    第2到第4,是在一个do-while循环,退出条件是r!=null,即找到下个要处理广播对象。在第2,是从闹钟广播队列、延期广播队列、顺序广播队列获取即将要执行的广播,如果没有广播,会直接跳出本函数;第4是当前广播处理结束,执行结束流程;第5是广播转入到延期广播队列中。经过前面的步骤,能获取到广播对象,则进入下面第6。

  6. 获取broadcast下个接收者receiver。广播的receiverTimedispatchTime在此被记录,会影响广播处理时间的计算,可能会导致receiver的进程加入延期队列,被特殊对待。

    1. BroadcastFilter对象,说明是动态注册的接收者,直接调用deliverToRegisteredReceiverLocked,与无序广播处理逻辑一致。
    2. ResolveInfo对象。说明是静态注册的,创建目标组件对象ComponentName
  7. 在第6步情况下,receiverResolveInfo对象,需要进行毕竟繁琐复杂的逻辑处理。

    1. 进行权限检查,这部分内容很多,感兴趣的朋友可以自己翻阅看看,主要是对系统的一些机制,广播发送者、接收者的合法权限检查。
    2. 进程已启动,执行processCurBroadcastLocked函数。该函数处理调到后面第3小节。
    3. 进程未启动,执行AMS.startProcessLocked启动进程,如果启动进程失败,需要结束接收者(参考广播的结束),执行下个广播处理(即处理本小结所有内容)。mPendingBroadcast被设置为当前广播对象,于开头等待广播所在进程启动完毕相照应。

回到第一步的无序广播处理中,调用deliverToRegisteredReceiverLocked函数主要是对权限进行处理,与前面的权限检查大致相同,然后调用了performReceiveLocked函数,与有序广播在第4对结束处理逻辑一致。

performReceiveLocked函数中,如果receiver所在的进程已经启动,直接调用 app.thread.scheduleRegisteredReceiver异步方式调用,这样保证广播Intent回到receiver所在进程进行处理。如果app未启动,则以同步方式调用 receiver.performReceive

//BroadcastQueue xxm
void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
        Intent intent, int resultCode, String data, Bundle extras,
        boolean ordered, boolean sticky, int sendingUser)
        throws RemoteException {
    //异步方式调用receiver所在线程scheduleRegisteredReceiver函数
    if (app != null) {
        if (app.thread != null) {
           	...
            app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
            	data, extras, ordered, sticky, sendingUser, app.getReportedProcState());
            ...
    } else {
            //同步方式
        	receiver.performReceive(intent, resultCode, data, extras, ordered,
                sticky, sendingUser);
    }
}

异步方式,我们定位到ApplicationThreadscheduleRegisteredReceiver函数。

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);
}       

所以,无论异步方式还是同步方式调用,最终还是回调LoadedApk.ReceiverDispatcher.InnerReceiver类的performReceive函数。

public void performReceive(Intent intent, int resultCode, String data,
        Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
    final LoadedApk.ReceiverDispatcher rd;
    if (intent == null) {
        rd = null;
    } else {
        rd = mDispatcher.get();
    }
    if (rd != null) {
        //调用performReceive
        rd.performReceive(intent, resultCode, data, extras,
                ordered, sticky, sendingUser);
    } else {//Intent为null,finish掉receiver,逻辑与解注册unregisterReceiver函数相似
        IActivityManager mgr = ActivityManager.getService();
        try {
            if (extras != null) {
                extras.setAllowFds(false);
            }
            mgr.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
}

ReceiverDispatcherperformReceive函数

public void performReceive(Intent intent, int resultCode, String data,
        Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
    final Args args = new Args(intent, resultCode, data, extras, ordered,
            sticky, sendingUser);
    ...
 	//分析2
 	if (intent == null || !mActivityThread.post(args.getRunnable())) {
        if (mRegistered && ordered) {//顺序广播
            IActivityManager mgr = ActivityManager.getService();
            args.sendFinished(mgr);//通知结束
        }
    }
}

ReceiverDispatcher.performReceive函数中将相关数据封装成Args对象,Args继承自PendingResult,代表了广播的处理结果。注意分析1,if语句,ArgsgetRunnable函数返回了Runnable对象,也就说这里执行了Runnable对象的run函数。这时切换到应用进程的主线程。

Args.getRunnable函数调用了BroadcastReceiveronReceive函数,即我们的业务逻辑。接着就是调用finish函数,执行广播的结束操作,这部分可以参考后文广播的结束

public final Runnable getRunnable() {
    return () -> {
        //自定义的BroadcastReceiver对象
        final BroadcastReceiver receiver = mReceiver;
        final boolean ordered = mOrdered;
        final IActivityManager mgr = ActivityManager.getService();
        final Intent intent = mCurIntent;

        mCurIntent = null;
        mDispatched = true;
        mRunCalled = true;
        //广播异常、接收者异常、丢弃
        if (receiver == null || intent == null || mForgotten) {
            if (mRegistered && ordered) {
                sendFinished(mgr);//有序广播的回调
            }
            return;
        }

        try {
            ClassLoader cl = mReceiver.getClass().getClassLoader();
            intent.setExtrasClassLoader(cl);
intent.prepareToEnterProcess(ActivityThread.isProtectedBroadcast(intent,
                    mContext.getAttributionSource());
            setExtrasClassLoader(cl);
            //设置了处理结果
            receiver.setPendingResult(this);
            //回调onReceive函数
            receiver.onReceive(mContext, intent);
        } catch (Exception e) {
            //异常,有序广播通知结束
            if (mRegistered && ordered) {
                sendFinished(mgr);
            }
             ...
        }
        //onReceiver函数前设置了
        if (receiver.getPendingResult() != null) {
            finish();
        }
    };
}

3、processCurBroadcastLocked

在第2步中,有序广播在对Receiver进行处理的时候,如果进程已经启动,会调用processCurBroadcastLocked函数。该函数进又调用了ApplicationThreadscheduleReceiver函数。

  private final void processCurBroadcastLocked(BroadcastRecord r,
            ProcessRecord app) throws RemoteException {

    final IApplicationThread thread = app.getThread();
	...
    r.receiver = thread.asBinder();
    r.curApp = app;
    final ProcessReceiverRecord prr = app.mReceivers;
    prr.addCurReceiver(r);
	...
    // Tell the application to launch this receiver.
    r.intent.setComponent(r.curComponent);

    boolean started = false;
    try {
		...
        thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
                mService.compatibilityInfoForPackage(r.curReceiver.applicationInfo),
                r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
                app.mState.getReportedProcState());
        started = true;
    } finally {
        if (!started) {
            r.receiver = null;
            r.curApp = null;
            prr.removeCurReceiver(r);
        }
    }
}

ApplicationThreadscheduleReceiver函数将相关信息封装成了ReceiverData类型的对象。ReceiverData继承自PendingResult,也代表了广播在当前BroadcastReceiver的处理结果,广播结束会调用它的finish函数。后面再看看finish函数做了什么。接着给ActivityThreadH对象发RECEIVER消息。

//这里的info是通过Resolver解析出来的,存储了BroadcastReceiver信息
//sync顺序与无序==同步与异步
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);
}

H.handleMessageRECEIVER分支调用了ActivityThreadhandleReceiver函数,也就说,现在切回到应用的主线程。函数中通过packageInfo.getAppFactory().instantiateReceiver函数通类加载机制创建了BroadcastReceiver对象,也就是我们自定义的广播接收者,接着回调了onReceive函数,将Intent对象传递给我们,广播处理流程也到此结束了。


 private void handleReceiver(ReceiverData data) {
	...
    String component = data.intent.getComponent().getClassName();

    LoadedApk packageInfo = getPackageInfoNoCheck(
            data.info.applicationInfo, data.compatInfo);

    IActivityManager mgr = ActivityManager.getService();

    Application app;
    BroadcastReceiver receiver;
    ContextImpl context;
    try {
        app = packageInfo.makeApplication(false, mInstrumentation);
        context = (ContextImpl) app.getBaseContext();
        if (data.info.splitName != null) {
            context = (ContextImpl) context.createContextForSplit(data.info.splitName);
        }
        if (data.info.attributionTags != null && data.info.attributionTags.length > 0) {
            final String attributionTag = data.info.attributionTags[0];
            context = (ContextImpl) context.createAttributionContext(attributionTag);
        }
        //类加载机制所必须的信息
        java.lang.ClassLoader cl = context.getClassLoader();
        data.intent.setExtrasClassLoader(cl);
        data.intent.prepareToEnterProcess(
                isProtectedComponent(data.info) || isProtectedBroadcast(data.intent),
                context.getAttributionSource());
        data.setExtrasClassLoader(cl);
        //通过类加载机制创建我们的BroadcastReceiver对象,packageInfo.getAppFactory返回AppComponentFactory对象
        receiver = packageInfo.getAppFactory()
                .instantiateReceiver(cl, data.info.name, data.intent);
    } catch (Exception e) {
        data.sendFinished(mgr);
    }

    try {
        //线程内的全局单例,应该在某个地方会读取判断
        sCurrentBroadcastIntent.set(data.intent);
        //设置处理结果
        receiver.setPendingResult(data);
        //回调我们自定义的onReceive方法。
        receiver.onReceive(context.getReceiverRestrictedContext(),
                data.intent);
    } catch (Exception e) {
       
        data.sendFinished(mgr);
       
    } finally {
        sCurrentBroadcastIntent.set(null);
    }
	//前面默认设置了data,所以默认情况接收处理,如果不想广播结束,再次调用setResult
    if (receiver.getPendingResult() != null) {
        data.finish();
    }
}
//AppComponentFactory
public @NonNull BroadcastReceiver instantiateReceiver(@NonNull ClassLoader cl,
        @NonNull String className, @Nullable Intent intent)
        throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    return (BroadcastReceiver) cl.loadClass(className).newInstance();
}

image-20240108210255101

广播的结束

按照前面的分析,广播Intent传递给接收者的onReceive函数之后,都会调用PendingRsultfinish函数来结束当前广播。

如果程序当前工作队列还有要执行的工作,则添加一个执行sendFinished函数任务到队列,排队等待执行。否则就立即执行sendFinished函数。

//PendingRsult
public final void finish() {
    if (mType == TYPE_COMPONENT) {//ReceiverData创建时,被设置为TYPE_COMPONENT,Args创建时TYPE_REGISTERED
        final IActivityManager mgr = ActivityManager.getService();
        //QueuedWork用于跟踪应用全局的工作是否结束
        if (QueuedWork.hasPendingWork()) {
            QueuedWork.queue(new Runnable() {
                @Override public void run() {
                    sendFinished(mgr);
                }
            }, false);
        } else {
            sendFinished(mgr);
        }
    } else if (mOrderedHint && mType != TYPE_UNREGISTERED) {//mOrderedHint为true表示顺序广播,TYPE_UNREGISTERED表示当前结注册
        final IActivityManager mgr = ActivityManager.getService();
        sendFinished(mgr);
    }
}

如果当前广播已经执行过结束流程,那么mFinished设置为true,再次执行就会抛出异常。接着根据广播的类型是有序还是无序,传递不同参数给ActivityManagerServicefinishReceiver函数,通知AMS结束广播。

public void sendFinished(IActivityManager am) {
    synchronized (this) {
        if (mFinished) {
            throw new IllegalStateException("Broadcast already finished");
        }
        mFinished = true;

        try {
            if (mResultExtras != null) {
                mResultExtras.setAllowFds(false);
            }
            if (mOrderedHint) {//有序
                am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,
                        mAbortBroadcast, mFlags);
            } else {//无序
                am.finishReceiver(mToken, 0, null, null, false, mFlags);
            }
        } catch (RemoteException ex) {
        }
    }
}

在广播的解注册,我们分析过AMS的unregisterReceiver函数,与这里的finishReceiver函数类似。根据广播Intentflags查询广播所在队列,然后再广播队列中查出当前广播BroadcastRecord对象,调用其finishReceiverLocked函数,结束掉当前广播,如果还有其他接收者在等待,调用processNextBroadcastLocked函数,执行下一个广播

public void finishReceiver(IBinder who, int resultCode, String resultData,
        Bundle resultExtras, boolean resultAbort, int flags) {
    
    // Refuse possible leaked file descriptors
    if (resultExtras != null && resultExtras.hasFileDescriptors()) {
        throw new IllegalArgumentException("File descriptors passed in Bundle");
    }

    final long origId = Binder.clearCallingIdentity();
    try {
        boolean doNext = false;
        BroadcastRecord r;
        BroadcastQueue queue;

        synchronized(this) {
            //根据Intent的Flags获取广播队列
            if (isOnOffloadQueue(flags)) {
                queue = mOffloadBroadcastQueue;
            } else {
                queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0
                        ? mFgBroadcastQueue : mBgBroadcastQueue;
            }
			//获取当前正在处理的广播,也就是现在接收者对应的广播
            r = queue.getMatchingOrderedReceiver(who);
            if (r != null) {
                doNext = r.queue.finishReceiverLocked(r, resultCode,
                    resultData, resultExtras, resultAbort, true);
            }
            if (doNext) {//如果有,执行下一个广播。参考=>广播队列对广播的处理小节
                r.queue.processNextBroadcastLocked(/*fromMsg=*/ false, /*skipOomAdj=*/ true);
            }
            // updateOomAdjLocked() will be done here
            trimApplicationsLocked(false, OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER);
        }

    } finally {
        Binder.restoreCallingIdentity(origId);
    }
}

finishReceiverLocked函数主要是对BroadcastBroadcastReceiver做一些清场工作,同时根据本次广播处理时间在核心系统应用做一些优化策略。同时也有可能需要等待下一个程序的Receiver来执行当前广播,也就是说广播只是在当前程序的接收者处理完,可能还有下个程序也要处理。

public boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
        String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) {
    final int state = r.state;
    final ActivityInfo receiver = r.curReceiver;
    final long finishTime = SystemClock.uptimeMillis();
    final long elapsed = finishTime - r.receiverTime;
    r.state = BroadcastRecord.IDLE;
   
    if (r.allowBackgroundActivityStarts && r.curApp != null) {
        if (elapsed > mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT) {
            //执行时间超过允许后台Activity启动时间,则没有启动,移除token即可
            r.curApp.removeAllowBackgroundActivityStartsToken(r);
        } else {
            //赋予更多的时间用来启动后台Activity
            postActivityStartTokenRemoval(r.curApp, r);
        }
    }
    //记录执行时间
    if (r.nextReceiver > 0) {
        r.duration[r.nextReceiver - 1] = elapsed;
    }

    // 广播在当前程序执行缓慢,非核心系统app下次处理广播时启动延迟策略
    //timeoutExempt为true表示不受超时影响,默认为false
    if (!r.timeoutExempt) {
        if (r.curApp != null
                && mConstants.SLOW_TIME > 0 && elapsed > mConstants.SLOW_TIME) {
            if (!UserHandle.isCore(r.curApp.uid)) {
                //将进程添加到缓慢列表中
                mDispatcher.startDeferring(r.curApp.uid);
            }
        }
    } 
	//当前广播的清场工作
    r.receiver = null;
    r.intent.setComponent(null);
    if (r.curApp != null && r.curApp.mReceivers.hasCurReceiver(r)) {
        r.curApp.mReceivers.removeCurReceiver(r);
        mService.enqueueOomAdjTargetLocked(r.curApp);
    }
    if (r.curFilter != null) {
        r.curFilter.receiverList.curBroadcast = null;
    }
    r.curFilter = null;
    r.curReceiver = null;
    r.curApp = null;
    mPendingBroadcast = null;

    r.resultCode = resultCode;
    r.resultData = resultData;
    r.resultExtras = resultExtras;
    if ( && (r.intent.getFlags()&Intent.FLAG_RECEIVER_NO_ABORT) == 0) {
        r.resultAbort = resultAbort;
    } else {
        r.resultAbort = false;
    }

    // 其他接收者正在等待当前接收者处理完
    if (waitForServices && r.curComponent != null && r.queue.mDelayBehindServices
            && r.queue.mDispatcher.getActiveBroadcastLocked() == r) {
        ActivityInfo nextReceiver;
        if (r.nextReceiver < r.receivers.size()) {
            Object obj = r.receivers.get(r.nextReceiver);
            nextReceiver = (obj instanceof ActivityInfo) ? (ActivityInfo)obj : null;
        } else {
            nextReceiver = null;
        }
        //相同进程的receiver不会执行到此处
        if (receiver == null || nextReceiver == null
                || receiver.applicationInfo.uid != nextReceiver.applicationInfo.uid
                || !receiver.processName.equals(nextReceiver.processName)) {
            //有后台服务,表示当前正在切换不同的程序的receiver来处理当前broadcast
            if (mService.mServices.hasBackgroundServicesLocked(r.userId)) {
                r.state = BroadcastRecord.WAITING_SERVICES;
                return false;
            }
        }
    }

    r.curComponent = null;

    // We will process the next receiver right now if this is finishing
    // an app receiver (which is always asynchronous) or after we have
    // come back from calling a receiver.
    return state == BroadcastRecord.APP_RECEIVE
            || state == BroadcastRecord.CALL_DONE_RECEIVE;
}

image-20240109194936281

总结

广播会发送到广播队列中不同集合。其中广播队列有三种类型,分别对应优先级从高到底:前台、后台、长广播队列类型。而广播又分三种:黏性广播、无序广播、有序广播。无序广播和有序广播的处理主要要发送广播的时候,而黏性广播则在广播接收者注册时候被处理。

一个应用程序允许注册最大的广播接收者是1000个。广播之间的传递也要经历各种权限检查,所以广播不适合在应用间用于频繁的交互。

如果你看到了这里,觉得文章写得不错就给个赞呗?
更多Android进阶指南 可以扫码 解锁更多Android进阶资料


在这里插入图片描述
敲代码不易,关注一下吧。ღ( ´・ᴗ・` )

  • 24
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值