Android源码解析四大组件系列(六)---广播的处理过程

这篇文章紧接着上篇分析广播的发送过程,发送广播都是调用ContextImpl的接口去实现的,总共有二十多个,最终都是调用到AMS的broadcastIntent。主要分成下面九小节来说明。
1、设置Flag
2、检查BroadcastOptions
3、当前是否有权力发出广播
4、处理系统相关广播
5、处理粘性广播
6、registeredReceivers和receivers查询
7、处理并行广播
8、整理两个receiver列表
9、处理串行广播

1、上层调用sendBroadcast发送广播


   @Override
   public void sendBroadcast(Intent intent) {
       warnIfCallingFromSystemProcess();
       String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
       try {
           // 准备离开应用程序进程,进入SysfangfatemServer进程  
           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();
       }
   }

省去跨进程的过程,进入AMS的broadcastIntent方法


 public final int broadcastIntent(IApplicationThread caller,
           Intent intent, String resolvedType, IIntentReceiver resultTo,
           int resultCode, String resultData, Bundle resultExtras,
           String[] requiredPermissions, int appOp, Bundle bOptions,
           boolean serialized, boolean sticky, int userId) {
       enforceNotIsolatedCaller("broadcastIntent");
       synchronized(this) {
           intent = verifyBroadcastLocked(intent);
           //从mLruProcesses列表中查询到进程
           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, resultExtras,
                   requiredPermissions, appOp, bOptions, serialized, sticky,
                   callingPid, callingUid, userId);
           Binder.restoreCallingIdentity(origId);
           return res;
       }
   }

2、broadcastIntentLocked中处理广播

broadcastIntentLocked的方法接近600行代码,需要分段阅读,才比较清楚。

   final int broadcastIntentLocked(ProcessRecord callerApp,
           String callerPackage, Intent intent, String resolvedType,
           IIntentReceiver resultTo, int resultCode, String resultData,
           Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
           boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {

       //设置Flag
       //检查BroadcastOptions
       //当前是否有权力发出广播
       //处理系统相关广播
       //处理粘性广播
       //registeredReceivers和receivers查询
       // 处理并行广播
       //整理两个receiver列表
       // 处理串行广播

}
2.1、设置Flag
        intent = new Intent(intent);

       // 设置这个flag后,广播不会发送给已经停止的应用,看来系统默认是不让我们的广播发送给已经停止的应用的
       intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);

       //如果AMS还没有启动好,不允许启动一个新的进程
       if (!mProcessesReady && (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {
           //这个flag表示只有动态注册的广播接收者能收到广播,如果你错误的设置了这个标记,广播又是静态注册的,那么就收不到广播
           intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
       }

     // 当不是USER_ALL广播且当前用户不是运行状态,除非是系统升级广播或者关机广播,否则直接返回
   if (userId != UserHandle.USER_ALL && !mUserController.isUserRunningLocked(userId, 0)) {
           if ((callingUid != Process.SYSTEM_UID
                   || (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0)
                   && !Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {
               Slog.w(TAG, "Skipping broadcast of " + intent
                       + ": user " + userId + " is stopped");
               return ActivityManager.BROADCAST_FAILED_USER_STOPPED;
           }
       }

Intent中有两个FLAG,FLAG_INCLUDE_STOPPED_PACKAGES和FLAG_EXCLUDE_STOPPED_PACKAGES,表示intent是否要激活“处于停止状态的”应用,如果确定要激活“处于停止状态的”应用,那么Intent add FLAG_INCLUDE_STOPPED_PACKAGES就行了。

/**
   * If set, this intent will not match any components in packages that
   * are currently stopped.  If this is not set, then the default behavior
   * is to include such applications in the result.
   */
  public static final int FLAG_EXCLUDE_STOPPED_PACKAGES = 0x00000010;
  /**
   * If set, this intent will always match any components in packages that
   * are currently stopped.  This is the default behavior when
   * {@link #FLAG_EXCLUDE_STOPPED_PACKAGES} is not set.  If both of these
   * flags are set, this one wins (it allows overriding of exclude for
   * places where the framework may automatically set the exclude flag).
   */
  public static final int FLAG_INCLUDE_STOPPED_PACKAGES = 0x00000020;

2.2、检查BroadcastOptions
BroadcastOptions brOptions = null;
       if (bOptions != null) {
           brOptions = new BroadcastOptions(bOptions);
           if (brOptions.getTemporaryAppWhitelistDuration() > 0) {
               // See if the caller is allowed to do this.  Note we are checking against
               // the actual real caller (not whoever provided the operation as say a
               // PendingIntent), because that who is actually supplied the arguments.
               if (checkComponentPermission(
                       android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
                       Binder.getCallingPid(), Binder.getCallingUid(), -1, true)
                       != PackageManager.PERMISSION_GRANTED) {
                   String msg = "Permission Denial: " + intent.getAction()
                           + " broadcast from " + callerPackage + " (pid=" + callingPid
                           + ", uid=" + callingUid + ")"
                           + " requires "
                           + android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST;
                   Slog.w(TAG, msg);
                   throw new SecurityException(msg);
               }
           }
       }
2.3、当前是否有权力发出广播
      final String action = intent.getAction();
       final boolean isProtectedBroadcast;
       try {
         //isProtectedBroadcast为true则代表该广播在Framework/base/core/res/AndroidManifest.xml中有声明为保护广播,这样的广播只能由系统发出
           isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);
       } catch (RemoteException e) {
           Slog.w(TAG, "Remote exception", e);
           return ActivityManager.BROADCAST_SUCCESS;
       }
       
       final boolean isCallerSystem;
       switch (UserHandle.getAppId(callingUid)) {
           case Process.ROOT_UID:
           case Process.SYSTEM_UID:
           case Process.PHONE_UID:
           case Process.BLUETOOTH_UID:
           case Process.NFC_UID:
                //以上进程都有权限发送广播
               isCallerSystem = true;
               break;
           default:
               isCallerSystem = (callerApp != null) && callerApp.persistent;
               break;
       }

       // First line security check before anything else: stop non-system apps from
       // sending protected broadcasts.
       if (!isCallerSystem) {
           if (isProtectedBroadcast) {
               String msg = "Permission Denial: not allowed to send broadcast "
                       + action + " from pid="
                       + callingPid + ", uid=" + callingUid;
               Slog.w(TAG, msg);
               throw new SecurityException(msg);

           } else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
                   || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
               // Special case for compatibility: we don't want apps to send this,
               // but historically it has not been protected and apps may be using it
               // to poke their own app widget.  So, instead of making it protected,
               // just limit it to the caller.
               if (callerPackage == null) {
                   String msg = "Permission Denial: not allowed to send broadcast "
                           + action + " from unknown caller.";
                   Slog.w(TAG, msg);
                   throw new SecurityException(msg);
               } else if (intent.getComponent() != null) {
                   // They are good enough to send to an explicit component...  verify
                   // it is being sent to the calling app.
                   if (!intent.getComponent().getPackageName().equals(
                           callerPackage)) {
                       String msg = "Permission Denial: not allowed to send broadcast "
                               + action + " to "
                               + intent.getComponent().getPackageName() + " from "
                               + callerPackage;
                       Slog.w(TAG, msg);
                       throw new SecurityException(msg);
                   }
               } else {
                   // Limit broadcast to their own package.
                   intent.setPackage(callerPackage);
               }
           }
       }

保护性广播是什么呢,frameworks/base/core/res/AndroidManifest.xml文件中可以看到定义,这些定义的广播都是保护广播,只能由系统发送,如果有不具有系统权限的应用试图发送系统中的“保护性广播”,那么到AMS的broadcastIntentLocked()处就会被拦住,AMS会抛出异常,提示"Permission Denial: not allowed to send broadcast"。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="android" coreApp="true" android:sharedUserId="android.uid.system"
   android:sharedUserLabel="@string/android_system_label">
   <!-- ================================================ -->
   <!-- Special broadcasts that only the system can send -->
   <!-- ================================================ -->
   <eat-comment />

   <protected-broadcast android:name="android.net.tether.CONNECTEDSTA_CHANGE" />
   <protected-broadcast android:name="android.intent.action.SCREEN_OFF" />
   <protected-broadcast android:name="android.intent.action.SCREEN_ON" />
   ...
   <protected-broadcast android:name="android.intent.action.PRE_BOOT_COMPLETED" />

   ...
</manifest>
2.4、处理系统相关广播
   if (action != null) {
           switch (action) {
               case Intent.ACTION_UID_REMOVED://uid移除
               case Intent.ACTION_PACKAGE_REMOVED://package移除
               case Intent.ACTION_PACKAGE_CHANGED://package改变
               case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:  app正在移动到SD卡中,发出的广播
               case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE: app完成移动到SD的操作,发出的广播  
               
       case Intent.ACTION_PACKAGES_SUSPENDED:
               
       case Intent.ACTION_PACKAGES_UNSUSPENDED:
                  
               case Intent.ACTION_PACKAGE_REPLACED://替换一个现有的安装包时发出的广播(不管现在安装的APP比之前的新还是旧,都会发出此广播)
               
               case Intent.ACTION_PACKAGE_ADDED: //增加package
               
               case Intent.ACTION_PACKAGE_DATA_CLEARED:
             
               case Intent.ACTION_TIMEZONE_CHANGED://时区改变
               
               case Intent.ACTION_TIME_CHANGED://时间改变
                
               case Intent.ACTION_CLEAR_DNS_CACHE://DNS缓存清空
               
               case Proxy.PROXY_CHANGE_ACTION://网络代理改变
               
               case android.hardware.Camera.ACTION_NEW_PICTURE:

               case android.hardware.Camera.ACTION_NEW_VIDEO:
                 
           }
       }

还有更多系统广播

2.5、处理粘性广播
if (sticky) {
          //需要android.Manifest.permission.BROADCAST_STICKY才能发送粘性广播
           if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,
                   callingPid, callingUid)
                   != PackageManager.PERMISSION_GRANTED) {
               String msg = "Permission Denial: broadcastIntent() requesting a sticky broadcast from pid="
                       + callingPid + ", uid=" + callingUid
                       + " requires " + android.Manifest.permission.BROADCAST_STICKY;
               Slog.w(TAG, msg);
               throw new SecurityException(msg);
           }
           //发送粘性广播不能强制添加别的权限
           if (requiredPermissions != null && requiredPermissions.length > 0) {
               Slog.w(TAG, "Can't broadcast sticky intent " + intent
                       + " and enforce permissions " + Arrays.toString(requiredPermissions));
               return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION;
           }
           //粘性广播也不能指定特定的组件名称
           if (intent.getComponent() != null) {
               throw new SecurityException(
                       "Sticky broadcasts can't target a specific component");
           }
        
           if (userId != UserHandle.USER_ALL) {
            //根据广播类型,取出stickies
               ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(
                       UserHandle.USER_ALL);
               if (stickies != null) {
                 //广播是使用Intent描述的,用action取出广播列表
                   ArrayList<Intent> list = stickies.get(intent.getAction());
                   if (list != null) {
                       int N = list.size();
                       int i;
                       for (i=0; i<N; i++) {
                         //粘性广播发送后是会保存下来的,故如果已经存在则不需要重新发送  ,
                         // filterEquals函数会比较两个intent的action、data、type、class以及categories等信息,
                           if (intent.filterEquals(list.get(i))) {
                               throw new IllegalArgumentException(
                                       "Sticky broadcast " + intent + " for user "
                                       + userId + " conflicts with existing global broadcast");
                           }
                       }
                   }
               }
           }
           ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
           if (stickies == null) {
               stickies = new ArrayMap<>();
               mStickyBroadcasts.put(userId, stickies);
           }
           ArrayList<Intent> list = stickies.get(intent.getAction());
           if (list == null) {
               list = new ArrayList<>();
               stickies.put(intent.getAction(), list);
           }
           final int stickiesCount = list.size();
           int i;
           for (i = 0; i < stickiesCount; i++) {
               if (intent.filterEquals(list.get(i))) {
                   // 新发送的intent在ArrayList中已经有个“相等的”旧intent时,则会用新的替掉旧的
                   list.set(i, new Intent(intent));
                   break;
               }
           }
           if (i >= stickiesCount) {
               list.add(new Intent(intent));
           }
       }

sticky为true,表示是粘性广播,发送粘性广播,一定要有android.Manifest.permission.BROADCAST_STICKY权限,没有的话就抛出SecurityException,Permission Denial: broadcastIntent() requesting a sticky broadcast...。在AMS中,所有相同的粘性广播都被保存在一个List中,这些List最终被保存在AMS成员变量mStickyBroadcasts中, mStickyBroadcasts的定义是这样的: final HashMap<String, ArrayList<Intent>> mStickyBroadcasts = new HashMap<String, ArrayList<Intent>>();注意粘性广播是在注册的时候加入到广播队列并且处理的,请移步Android源码解析---广播的注册过程

2.6、registeredReceivers和receivers查询

为了合理处理“串行广播”和“并行广播”,broadcastIntentLocked()方法中搞出了两个list,一个是receivers,另一个是registeredReceivers,registeredReceivers是动态广播接收器列表 ,receivers是静态广播接收器列表 。

       List receivers = null;
       List<BroadcastFilter> registeredReceivers = null;
       // Need to resolve the intent to interested receivers...
       if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
                == 0) {
       //collectReceiverComponents内部调用包管理器的queryIntentReceivers()接口,查询出和intent匹配的所有静态receivers,此时所返回的查询结果本身已经排好序了,因此,该返回值被直接赋值给了receivers变量
           receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
       }
       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;
                   }
                 //此时返回的registeredReceivers中的子项是没有经过排序的,在后面queue.scheduleBroadcastsLocked()会被处理掉
                   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);
           }
       }
2.7 处理并行广播

上面已经获取了并行广播和串行广播,现在现将并行广播给处理掉

       final boolean replacePending =
               (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;

       if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueing broadcast: " + intent.getAction()
               + " replacePending=" + replacePending);
       //并行广播列表大小
       int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
       //参数ordered标记当前发送的广播是否是有序广播,可以看到如果发送的是无序广播,进入的是并行广播队列
       if (!ordered && NR > 0) {
           // 如果不是有序广播, 不用等待目标组件是否启动,就可以发送
           if (isCallerSystem) {
               checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
                       isProtectedBroadcast, registeredReceivers);
           }
           //构建广播队列
           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);
           if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
           final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
           if (!replaced) {
               //加入到并行广播队列
               queue.enqueueParallelBroadcastLocked(r);
              //处理上面加入并行广播消息队列里面的广播
               queue.scheduleBroadcastsLocked();
           }
           //处理完之后,registeredReceivers要赋值为null
           registeredReceivers = null;
           NR = 0;
       }

关于上面的广播队列BroadcastQueue,AMS内部维持了后台广播队列和前台广播队列

BroadcastQueue mFgBroadcastQueue;//前台广播队列
BroadcastQueue mBgBroadcastQueue;//后台广播队列
final BroadcastQueue[] mBroadcastQueues = new BroadcastQueue[2];

并且在AMS的构造函数中进行初始化

       mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
               "foreground", BROADCAST_FG_TIMEOUT, false);
       mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
               "background", BROADCAST_BG_TIMEOUT, true);
       mBroadcastQueues[0] = mFgBroadcastQueue;
       mBroadcastQueues[1] = mBgBroadcastQueue;

初始化之后,就可以像上面那样(通过broadcastQueueForIntent方法)获取相应的广播队列了,主要就是根据intent中有没有FLAG_RECEIVER_FOREGROUND标记。

 BroadcastQueue broadcastQueueForIntent(Intent intent) {
       final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
       if (DEBUG_BROADCAST_BACKGROUND) Slog.i(TAG_BROADCAST,
               "Broadcast intent " + intent + " on "
               + (isFg ? "foreground" : "background") + " queue");
       return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
   }

相应的,将广播加入到队列也很esay,调用 queue.enqueueParallelBroadcastLocked(r)

public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
       mParallelBroadcasts.add(r);
       r.enqueueClockTime = System.currentTimeMillis();
   }

这里又要说明一下mParallelBroadcasts了,BroadcastQueue名字叫做队列,但并不是任何集合的子类,自身不带有存储数据的功能,所以它的内部维护了两个ArrayList。mParallelBroadcasts存放并行广播(无序广播),mOrderedBroadcasts存放串行广播(有序广播)

/**
    * Lists of all active broadcasts that are to be executed immediately
    * (without waiting for another broadcast to finish).  Currently this only
    * contains broadcasts to registered receivers, to avoid spinning up
    * a bunch of processes to execute IntentReceiver components.  Background-
    * and foreground-priority broadcasts are queued separately.
    */
   final ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<>();

   /**
    * List of all active broadcasts that are to be executed one at a time.
    * The object at the top of the list is the currently activity broadcasts;
    * those after it are waiting for the top to finish.  As with parallel
    * broadcasts, separate background- and foreground-priority queues are
    * maintained.
    */
   final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<>();
   

关于最后调用scheduleBroadcastsLocked进行广播处理的,本文不分析,下篇文章分析。

2.8 整理两个receiver列表

上面判断了ordered,如果ordered==false,也就是发送的是无序广播,那么就进入并行广播队列直接处理掉,如果ordered==true,也就是发送的是有序广播,需要整合将registeredReceivers里面的合并到receivers中。

 // Merge into one list.
       int ir = 0;
       if (receivers != null) {
           // A special case for PACKAGE_ADDED: do not allow the package
           // being added to see this broadcast.  This prevents them from
           // using this as a back door to get run as soon as they are
           // installed.  Maybe in the future we want to have a special install
           // broadcast or such for apps, but we'd like to deliberately make
           // this decision.
           String skipPackages[] = null;
           if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())
                   || Intent.ACTION_PACKAGE_RESTARTED.equals(intent.getAction())
                   || Intent.ACTION_PACKAGE_DATA_CLEARED.equals(intent.getAction())) {
               Uri data = intent.getData();
               if (data != null) {
                   String pkgName = data.getSchemeSpecificPart();
                   if (pkgName != null) {
                       skipPackages = new String[] { pkgName };
                   }
               }
           } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(intent.getAction())) {
               skipPackages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
           }
           if (skipPackages != null && (skipPackages.length > 0)) {
               for (String skipPackage : skipPackages) {
                   if (skipPackage != null) {
                       int NT = receivers.size();
                       for (int it=0; it<NT; it++) {
                           // 静态注册的广播是ResolveInfo类型  ,动态的是BrocastFilter
                           ResolveInfo curt = (ResolveInfo)receivers.get(it);
                           if (curt.activityInfo.packageName.equals(skipPackage)) {
                               receivers.remove(it);
                               it--;
                               NT--;
                           }
                       }
                   }
               }
           }

           int NT = receivers != null ? receivers.size() : 0;
           int it = 0;
           ResolveInfo curt = null;
           BroadcastFilter curr = null;
           while (it < NT && ir < NR) {
               if (curt == null) {
                  //  静态注册的广播是ResolveInfo类型 
                   curt = (ResolveInfo)receivers.get(it);
               }
               if (curr == null) {
                    // 动态注册的广播是BroadcastFilter类型
                   curr = registeredReceivers.get(ir);
               }
               // 如果动态注册广播接收者优先级高于等于静态广播接收者,则把动态注册的广播接收者插入到当前位置,  
           // 静态注册的广播接收者后移,所以同优先级动态注册的先于静态注册的接收到广播  
               if (curr.getPriority() >= curt.priority) {
                   // Insert this broadcast record into the final list.
                   receivers.add(it, curr);
                   ir++;
                   curr = null;
                   it++;
                   NT++;
               } else {
                   // Skip to the next ResolveInfo in the final list.
                   it++;
                   curt = null;
               }
           }
       }
        /// 把优先级低于所有静态注册广播接收者的动态广播接收者都追加到receivers列表中的末尾  
       while (ir < NR) {
           if (receivers == null) {
               receivers = new ArrayList();
           }
           receivers.add(registeredReceivers.get(ir));
           ir++;
       }
2.9 处理串行广播
  if ((receivers != null && receivers.size() > 0)
               || resultTo != null) {
           //获取广播队列
           BroadcastQueue queue = broadcastQueueForIntent(intent);
           //广播实体类
           BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                   callerPackage, callingPid, callingUid, resolvedType,
                   requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
                   resultData, resultExtras, ordered, sticky, false, userId);

           if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r
                   + ": prev had " + queue.mOrderedBroadcasts.size());
           if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST,
                   "Enqueueing broadcast " + r.intent.getAction());

           boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
           if (!replaced) {
               //加入串行广播队列
               queue.enqueueOrderedBroadcastLocked(r);
               //处理串行广播
               queue.scheduleBroadcastsLocked();
           }
       } else {
           // There was nobody interested in the broadcast, but we still want to record
           // that it happened.
           if (intent.getComponent() == null && intent.getPackage() == null
                   && (intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
               // This was an implicit broadcast... let's record it for posterity.
               addBroadcastStatLocked(intent.getAction(), callerPackage, 0, 0, 0);
           }
       }

在2.6小节, 有行代码是 final boolean replacePending = (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;这是为了查看intent的flag有没有设置FLAG_RECEIVER_REPLACE_PENDING,如果设置的话, AMS就会在当前的系统中查看有没有相同的intent还未处理,如果有的话,就用当前这个新的intent 来替换旧的intent。所以当replacePending==true的时候,执行queue.replaceParallelBroadcastLocked(r)进行替换,并且返回true.


   public final boolean replaceParallelBroadcastLocked(BroadcastRecord r) {
       for (int i = mParallelBroadcasts.size() - 1; i >= 0; i--) {
           final Intent curIntent = mParallelBroadcasts.get(i).intent;
           if (r.intent.filterEquals(curIntent)) {
               if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
                       "***** DROPPING PARALLEL ["
               + mQueueName + "]: " + r.intent);
               mParallelBroadcasts.set(i, r);
               return true;
           }
       }
       return false;
   }

好了,文章比较长,在总结一下广播的发送过程,分为九个部分。
2.1、设置Flag
2.2、检查BroadcastOptions
2.3、当前是否有权力发出广播
2.4、处理系统相关广播
2.5、处理粘性广播
2.6、registeredReceivers和receivers查询
2.7 处理并行广播
2.8 整理两个receiver列表
2.9 处理串行广播

  • 对于粘性广播是在注册过程处理的,创建BroadcastRecord对象;并添加到mParallelBroadcasts队列;
    然后执行queue.scheduleBroadcastsLocked进行处理

  • 对于并行广播: 动态注册的广播会进入mRegisteredReceivers表,会创建BroadcastRecord对象,并添加到mParallelBroadcasts队列;然后执行queue.scheduleBroadcastsLocked;

  • 对于所有静态注册的广播和动态注册的有序广播会进入receivers表中(串行),会创建BroadcastRecord对象,并添加到mOrderedBroadcasts队列;然后执行queue.scheduleBroadcastsLocked;

下篇文章分析广播的处理过程,即scheduleBroadcastsLocked方法到底做了什么?暂时over!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,780评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,424评论 1 305
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,397评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,576评论 0 218
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,997评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,945评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,107评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,850评论 0 208
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,625评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,804评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,285评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,613评论 3 261
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,291评论 3 242
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,164评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,963评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,096评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,886评论 2 278

推荐阅读更多精彩内容