💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
#### 9.4.2 广播的发送和接收过程 上面分析了广播的注册过程,可以发现注册过程的逻辑还是比较简单的,下面来分析广播的发送和接收过程。当通过send方法来发送广播时,AMS会查找出匹配的广播接收者并将广播发送给它们处理。广播的发送有几种类型:普通广播、有序广播和粘性广播,有序广播和粘性广播与普通广播相比具有不同的特性,但是它们的发送/接收过程的流程是类似的,因此这里只分析普通广播的实现。 广播的发送和接收,其本质是一个过程的两个阶段。这里从广播的发送可以说起,广播的发送仍然开始于ContextWrapper的sendBroadcast方法,之所以不是Context,那是因为Context的sendBroadcast是一个抽象方法。和广播的注册过程一样,ContextWrapper的sendBroadcast方法仍然什么都不做,只是把事情交给ContextImpl去处理,ContextImpl的sendBroadcast方法的源码如下所示。 public void sendBroadcast(Intent intent) { warnIfCallingFromSystemProcess(); String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, false, getUserId()); } catch (RemoteException e) { } } 从上面的代码来看,ContextImpl也是几乎什么事都没干,它直接向AMS发起了一个异步请求用于发送广播。因此,下面直接看AMS对广播发送过程的处理,AMS的broadcastIntent方法的源码如下所示。 public final int broadcastIntent(IApplicationThread caller, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle map, String requiredPermission, int appOp, boolean serialized, boolean sticky, int userId) { enforceNotIsolatedCaller("broadcastIntent"); synchronized(this) { intent = verifyBroadcastLocked(intent); final ProcessRecord callerApp = getRecordForAppLocked(caller); final int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); int res = broadcastIntentLocked(callerApp, callerApp ! = null ? callerApp.info.packageName : null, intent, resolvedType, resultTo, resultCode, resultData, map, requiredPermission, appOp, serialized, sticky, callingPid, callingUid, userId); Binder.restoreCallingIdentity(origId); return res; } } 从上面代码来看,broadcastIntent调用了broadcastIntentLocked方法,AMS的broadcastIntentLocked方法有436行代码,看起来比较复杂。在代码最开始有如下一行: // By default broadcasts do not go to stopped apps. intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES); 这表示在Android 5.0中,默认情况下广播不会发送给已经停止的应用,其实不仅仅是Android 5.0,从Android 3.1开始广播已经具有这种特性了。这是因为系统在Android 3.1中为Intent添加了两个标记位,分别是FLAG_INCLUDE_STOPPED_PACKAGES和FLAG_EXCLUDE_STOPPED_PACKAGES,用来控制广播是否要对处于停止状态的应用起作用,它们的含义如下所示。 * FLAG_INCLUDE_STOPPED_PACKAGES 表示包含已经停止的应用,这个时候广播会发送给已经停止的应用。 * FLAG_EXCLUDE_STOPPED_PACKAGES 表示不包含已经停止的应用,这个时候广播不会发送给已经停止的应用。 从Android 3.1开始,系统为所有广播默认添加了FLAG_EXCLUDE_STOPPED_PACKAGES标志,这样做是为了防止广播无意间或者在不必要的时候调起已经停止运行的应用。如果的确需要调起未启动的应用,那么只需要为广播的Intent添加FLAG_INCLUDE_STOPPED_PACKAGES标记即可。当FLAG_EXCLUDE_STOPPED_PACKAGES和FLAG_INCLUDE_STOPPED_PACKAGES两种标记位共存时,以FLAG_INCLUDE_STOPPED_PACKAGES为准。这里需要补充一下,一个应用处于停止状态分为两种情形:第一种是应用安装后未运行,第二种是应用被手动或者其他应用强停了。Android 3.1中广播的这个特性同样会影响开机广播,从Android 3.1开始,处于停止状态的应用同样无法接收到开机广播,而在Android 3.1之前,处于停止状态的应用是可以收到开机广播的。 在broadcastIntentLocked的内部,会根据intent-filter查找出匹配的广播接收者并经过一系列的条件过滤,最终会将满足条件的广播接收者添加到BroadcastQueue中,接着BroadcastQueue就会将广播发送给相应的广播接收者,这个过程的源码如下所示。 if ((receivers ! = null && receivers.size() > 0) || resultTo ! = null) { BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callingPid, callingUid, resolvedType, requiredPermission, appOp, receivers, resultTo, resultCode, resultData, map, ordered, sticky, false, userId); if (DEBUG_BROADCAST) Slog.v( TAG, "Enqueueing ordered broadcast " + r + ": prev had " + queue.mOrderedBroadcasts.size()); if (DEBUG_BROADCAST) { int seq = r.intent.getIntExtra("seq", -1); Slog.i(TAG, "Enqueueing broadcast " + r.intent.getAction() + " seq=" + seq); } boolean replaced = replacePending && queue.replaceOrderedBroadcast- Locked(r); if (! replaced) { queue.enqueueOrderedBroadcastLocked(r); queue.scheduleBroadcastsLocked(); } } 下面看一下BroadcastQueue中广播的发送过程的实现,如下所示。 public void scheduleBroadcastsLocked() { if (DEBUG_BROADCAST) Slog.v(TAG, "Schedule broadcasts [" + mQueueName + "]: current=" + mBroadcastsScheduled); if (mBroadcastsScheduled) { return; } mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this)); mBroadcastsScheduled = true; } BroadcastQueue的scheduleBroadcastsLocked方法并没有立即发送广播,而是发送了一个BROADCAST_INTENT_MSG类型的消息,BroadcastQueue收到消息后会调用process-NextBroadcast方法,BroadcastQueue的processNextBroadcast方法对普通广播的处理如下所示。 // 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(); if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing parallel broadcast [" + mQueueName + "] " + r); for (int i=0; i<N; i++) { Object target = r.receivers.get(i); if (DEBUG_BROADCAST) Slog.v(TAG, "Delivering non-ordered on["+mQueueName + "] to registered " + target + ": " + r); deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false); } addBroadcastToHistoryLocked(r); if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Done with parallel broadcast [" + mQueueName + "] " + r); } 可以看到,无序广播存储在mParallelBroadcasts中,系统会遍历mParallelBroadcasts并将其中的广播发送给它们所有的接收者,具体的发送过程是通过deliverToRegistered-ReceiverLocked方法来实现的。deliverToRegisteredReceiverLocked方法负责将一个广播发送给一个特定的接收者,它内部调用了performReceiveLocked方法来完成具体的发送过程: performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver, new Intent(r.intent), r.resultCode, r.resultData, r.resultExtras, r.ordered, r.initialSticky, r.userId); performReceiveLocked方法的实现如下所示。由于接收广播会调起应用程序,因此app.thread不为null,根据前面的分析我们知道这里的app.thread仍然指ApplicationThread。 private static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver, Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) throws Remote- Exception { // Send the intent to the receiver asynchronously using one-way binder calls. if (app ! = null) { if (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, sendingUser, app.repProcState); } else { // Application has died. Receiver doesn't exist. throw new RemoteException("app.thread must not be null"); } } else { receiver.performReceive(intent, resultCode, data, extras, ordered, sticky, sendingUser); } } ApplicationThread的scheduleRegisteredReceiver的实现比较简单,它通过InnerReceiver来实现广播的接收,如下所示。 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); } InnerReceiver的performReceive方法会调用LoadedApk.ReceiverDispatcher的perform-Receive方法,LoadedApk.ReceiverDispatcher的performReceive方法的实现如下所示。 public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) { if (ActivityThread.DEBUG_BROADCAST) { int seq = intent.getIntExtra("seq", -1); Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent. getAction() + " seq=" + seq + " to " + mReceiver); } Args args = new Args(intent, resultCode, data, extras, ordered, sticky, sendingUser); if (! mActivityThread.post(args)) { if (mRegistered && ordered) { IActivityManager mgr = ActivityManagerNative.getDefault(); if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG, "Finishing sync broadcast to " + mReceiver); args.sendFinished(mgr); } } } 在上面的代码中,会创建一个Args对象并通过mActivityThread的post方法来执行Args中的逻辑,而Args实现了Runnable接口。mActivityThread是一个Handler,它其实就是ActivityThread中的mH, mH的类型是ActivityThread的内部类H,关于H这个类前面已经介绍过了,这里就不再多说了。在Args的run方法中有如下几行代码: final BroadcastReceiver receiver = mReceiver; receiver.setPendingResult(this); receiver.onReceive(mContext, intent); 很显然,这个时候BroadcastReceiver的onReceive方法被执行了,也就是说应用已经接收到广播了,同时onReceive方法是在广播接收者的主线程中被调用的。到这里,整个广播的注册、发送和接收过程已经分析完了,读者应该对广播的整个工作过程有了一定的理解。