查看原文
其他

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

新小梦 鸿洋
2024-08-24

本文作者


作者:新小梦

链接:

https://juejin.cn/post/7322156751818522661

本文由作者授权发布。


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

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

1广播的注册


广播的注册分动态注册和静态注册两种,静态注册是指将BroadcastReceiver和IntentFilter写在配置清单里,Android系统在解析包信息的时候,会将其添加到PackageManagerServicemComponentResolver中,后续处理广播会从中查找匹配的接收者BroadcastReceiver。而动态注册,指的是在程序运行后,通过代码进行注册,下面分析的是动态注册的内容。
我们常在Activity或Service、甚至在Application中调用registerReceiver函数来动态注册广播,该函数其实来自他们的父类ContextWrapper中。ContextWrapper是Context的子类,我们会在介绍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, filternullnull);
}

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对象mPackageInfo的getReceiverDispatcher函数,根据context从mReceivers容器中获ArrayMap对象map,再以receiver为Key在map查询对应的ReceiverDispatcher对象。也就是说,一个Context对象,可能是Activity、Application、Service,会注册一个或多个广播接收者BroadcastReceiver,而一个广播接收者对应一个ReceiverDispatcher。后续广播的处理会通过ReceiverDispatcher找到对应的BroadcastReceiver,将Intent传递给它处理。
如果查询不到ReceiverDispatcher对象,说明之前没有添加过,则创建ReceiverDispatcher对象(内部会创建InnerReceiver对象),按照前面获取的逻辑,进行逆操作,最终将receiver添加到mReceivers映射中。如果已经存在,则更新Context和Handler对象。
有了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, nulltrue).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)。AMS的registerReceiverWithFeature函数的主要功能是处理这层关系和存储它们。我们将涉及的几个重要成员用下图的关系表达出来。
  • mRegisteredReceivers:AMS的成员变量,类型为HashMap<IBinder, ReceiverList>,Hash Key是IIntentReceiver的IBinder对象,也就是客户端BroadcastReceiver在客户端的代表。而Value就是ReceiverList对象。
  • ReceiverList:继承自ArrayList<BroadcastFilter>,用于存储BroadcastFilter对象,表示一个BroadReceiver所对应的多个IntenerFilter的关系。
  • BroadcastFilter: 内部封装了IntentFilter,可以表示为IntentFilter在系统层的代表。
  • mReceiverResolver:存储BroadcastFilter,表示当前所有的IntentFilter对象。后续发送广播时,如果广播没有component,需要通过它区解析匹配的接收者。
registerReceiverWithFeature函数首先根据receiver.asBinder()mRegisteredReceivers映射中查找是否有历史的ReceiverList对象。如果没有历史记录,则创建ReceiverList对象,并添加到mRegisteredReceivers中和receiver所在进程的ProcessRecord的mReceivers中。一个应用程序允许创建最多的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处理。
到这里,广播的注册就完成了。

2广播的解注册


有了对广播注册流程的了解,那么对广播的解注册流程理解,会容易很多。也就是在注册过程中,在LoadedApk对象加入到mReceiver中,现在就要将其移除,并重置相关属性。在AMS中,注册时是加入mRegisteredReceivers和ReceiverList,以及mReceiverResolver,那么解注册就是从它们移除。
回到ContextWrapper的unregisterReceiver函数。
//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");
    }
}
LoadedApk类forgetReceiverDispatcher函数的处理逻辑是和注册时getReceiverDispatcher函数反着来。根据context对象在mReceivers映射中获取当前context的所有BroadcastReceiver对应的ReceiverDispatcher对象。这是得到的是一个ArrayMap对象map,再在该map中根据BroadcastReceiver对象获取对应的ReceiverDispatcher。假如存在的话,依次从map、mReceiver(没有其他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
3广播的处理


ContextWrapper的sendBroadcast函数,调用了ContextImpl类sendBroadcast函数,进而调用了AMS的broadcastIntentWithFeature函数。
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, nullnullnull, AppOpsManager.OP_NONE, nullfalse,false, getUserId());
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

1、AMS对广播的处理

调用AMS的broadcastIntentWithFeature函数。
verifyBroadcastLocked函数,对Intent的合法性进行检查,下面三种情况都会抛出异常。
  • 携带文件描述符;
  • 系统未启动完毕,当前Intent发送给所有的接收者。
  • 开机升级广播。
如果Intent携带Intent.FLAG_RECEIVER_FROM_SHELL的Flags,即该广播时从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_CHANGED、ACTION_TIMEZONE_CHANGED;应用相关的变化ACTION_PACKAGE_ADDED、ACTION_PACKAGE_DATA_CLEARED、ACTION_PACKAGE_CHANGED等等很多。
以上的逻辑基本是针对系统的一些机制进行特殊逻辑处理,而接下来就是普通广播进行处理,而广播又三种类型,分为黏性、无序、有序广播。
  • 黏性广播
    • 需要检测发送者是否声明BROADCAST_STICKY权限,且该广播不能携带其他权限检查。
    • 不能指定具体的目标组件。即Intent不能设置setComponent函数。
    • 非全局的黏性广播不能与已存在全局的黏性广播相同(冲突)。
    • 黏性广播的处理:
      mStickyBroadcasts获取当前用户所有的黏性广播stickies,我们知道,广播注册时会从mStickyBroadcasts中取出黏性广播发给广播队列进行处理。根据当前黏性广播的action在stickies映射中查询,查看是否有相同action的黏性广播(list集合),如果有且IntentFilter相同,则替代旧的,不相同则添加到list中。也就是说黏性广播发送只会添加到黏性广播容器中,在广播接收者注册时候才被处理。
接下来,与当前广播Intent匹配的广播接收者,静态注册会被收集到receivers集合中(FLAG_RECEIVER_REGISTERED_ONLY没有设置),动态注册的会被收集到registerReceiver容器。
  • 无序广播
    如果当前广播属于普通,无序的,且registerReceiver集合有收集到匹配的接收者。
  1. 如果广播发送者是系统应用,需要调用checkBroadcastFromSystem函数检测一下广播的action,做一些警告。
  2. 根据Intent的flags获取当前广播属于前台广播队列或后台广播队列。
  3. 广播相关信息封装成BroadcastRecord对象,添加到广播队列的并行集合中mParallelBroadcasts,表示接收者之间可以同时处理该广播。
  4. 调用广播队列BroadcastQueue的`scheduleBroadcastsLocked`函数。
那么接下来,还有静态注册的广播接收者(假如有的话)和有序广播没有处理。在处理完无序广播之后,registerReceiver会被清空。根据所有接收者(receivers和registerReceiver)的优先权合并到同一个同一个队列中,优先权高的放在前面。这里面有几种情况:
  • 如果广播是无序广播,那么不会有合并到同一个队列的动作,因为有判断条件。这时候,接下来处理的是无序广播被静态注册接收者处理的部分。
  • 如果广播是有序广播,那么不会执行无序广播的处理逻辑,receivers和registerReceiver被合并在一起。然后就是下面的有序广播处理逻辑。
  • 有序广播
  1. 如果广播发送者是系统应用,需要调用checkBroadcastFromSystem函数检测一下广播的action,做一些警告。
  2. 根据Intent的flags获取当前广播属于前台广播队列或后台广播队列。
  3. 广播相关信息封装成BroadcastRecord对象,添加到广播队列的顺序集合中mOrderedBroadcasts
  4. 调用广播队列BroadcastQueue的`scheduleBroadcastsLocked`函数。
也就是说AMS的broadcastIntentLocked函数主要处理一些特殊的Action。然后根据广播类型:黏性、无序、有序,添加到队列的不同集合中,然后调用广播队列BroadcastQueue的scheduleBroadcastsLocked函数对它们进一步处理。
而广播队列分三种类型:前台(优先级最高)、后台(普通优先级)、长广播(耗时很长,例如开机广播)。
特殊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;
}
BroadcastHandler的handleMessage函数对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, falsefalse, 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。广播的receiverTime和dispatchTime在此被记录,会影响广播处理时间的计算,可能会导致receiver的进程加入延期队列,被特殊对待。
• 是BroadcastFilter对象,说明是动态注册的接收者,直接调用deliverToRegisteredReceiverLocked,与无序广播处理逻辑一致。
•  是ResolveInfo对象。说明是静态注册的,创建目标组件对象ComponentName。
7. 在第6步情况下,receiver是ResolveInfo对象,需要进行毕竟繁琐复杂的逻辑处理。
•  进行权限检查,这部分内容很多,感兴趣的朋友可以自己翻阅看看,主要是对系统的一些机制,广播发送者、接收者的合法权限检查。
• 进程已启动,执行processCurBroadcastLocked函数。该函数处理调到后面第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);
    }
}
异步方式,我们定位到ApplicationThread的scheduleRegisteredReceiver函数。
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();
        }
    }
}
ReceiverDispatcher的performReceive函数
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语句,Args的getRunnable函数返回了Runnable对象,也就说这里执行了Runnable对象的run函数。这时切换到应用进程的主线程。
Args.getRunnable函数调用了BroadcastReceiver的onReceive函数,即我们的业务逻辑。接着就是调用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函数。该函数进又调用了ApplicationThread的scheduleReceiver函数。
  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);
        }
    }
}
ApplicationThread的scheduleReceiver函数将相关信息封装成了ReceiverData类型的对象。ReceiverData继承自PendingResult,也代表了广播在当前BroadcastReceiver的处理结果,广播结束会调用它的finish函数。后面再看看finish函数做了什么。接着给ActivityThread的H对象发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.handleMessage的RECEIVER分支调用了ActivityThread的handleReceiver函数,也就说,现在切回到应用的主线程。函数中通过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();
}

4广播的结束


按照前面的分析,广播Intent传递给接收者的onReceive函数之后,都会调用PendingRsult的finish函数来结束当前广播。
如果程序当前工作队列还有要执行的工作,则添加一个执行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,再次执行就会抛出异常。接着根据广播的类型是有序还是无序,传递不同参数给ActivityManagerService的finishReceiver函数,通知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, 0nullnullfalse, mFlags);
            }
        } catch (RemoteException ex) {
        }
    }
}
在广播的解注册,我们分析过AMS的unregisterReceiver函数,与这里的finishReceiver函数类似。根据广播Intent的flags查询广播所在队列,然后再广播队列中查出当前广播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函数主要是对Broadcast和BroadcastReceiver做一些清场工作,同时根据本次广播处理时间在核心系统应用做一些优化策略。同时也有可能需要等待下一个程序的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;
}

5总结


广播会发送到广播队列中不同集合。其中广播队列有三种类型,分别对应优先级从高到底:前台、后台、长广播队列类型。而广播又分三种:黏性广播、无序广播、有序广播。无序广播和有序广播的处理主要要发送广播的时候,而黏性广播则在广播接收者注册时候被处理。
一个应用程序允许注册最大的广播接收者是1000个。广播之间的传递也要经历各种权限检查,所以广播不适合在应用间用于频繁的交互。


最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!


推荐阅读

Android hide api反射方案合集
OpenHarmony源码系列:如何触发UI刷新?
2024年移动端技术探索


扫一扫 关注我的公众号

如果你想要跟大家分享你的文章,欢迎投稿~


┏(^0^)┛明天见!

继续滑动看下一个
鸿洋
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存