Android 静态广播和动态广播接收顺序

Posted mingfeng002

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 静态广播和动态广播接收顺序相关的知识,希望对你有一定的参考价值。

android广播有两个很重要的要素:

   1 广播 - 用于发送广播

         有序广播  -  被广播接收器接收后,可被终止,无法往下继续传达。         典型代表:短信广播

         普通广播  -  发送至每一个已经注册(订阅)的广播接收器,无法被终止。 典型代表:开机启动广播

   2 广播接收器 - 用于订阅广播后接收广播

         静态注册广播 - 在AndroidManifest.xml中设置,程序不用启动亦可接收。 典型代表:很多开机启动的APP,都是接收开机启动广播带起服务的。

         动态注册广播 - 代码中注册广播,程序未启动时,无法接收广播。             典型代表:Go短信,将Go短信强行停止,Go短信无法接收短信。

 

广播注册过程和接收广播顺序过程

技术图片

    静态广播接收器 由PackageManagerService负责,当手机启动时(或者新安装了应用),PackageManagerService负责扫描手机中所有已安装的APP应用(题外话,确定不再使用的APP需要卸载了),将AndroidManifest.xml中 有关注册广播的信息 解析出来,存储至一个全局静态变量当中mReceivers。

  需要注意的是:

 1 PackageManagerService扫描目录的顺序如下:

  system/framework

  system/app

  vendor/app

  data/app

  drm/app-private

  2 当处于同一目录下时:按照file.list()的返回顺序。(题外话:因为在data/app下的应用都是用户安装的,并且都是以com.xxx.xxx-1.apk 的形式出现,所以如果打算做手机管家之类的应用,需要好好的研究下包名,争取在file.list()的独木桥下抢的头筹---优先接收开机启动完成的广播。)

   3 在此处并未对 接收顺序做完整的排序。(注意修饰词完整的,毕竟先扫描的当然会有一定优先级)

 

    动态广播接收器 由ActivityManagerService负责,当APP的服务或者进程起来之后,执行了注册广播接收的代码逻辑,即进行加载,最后会存储在一个全局静态变量

mReceiverResolver中。

    需要注意的是:

    1 这个并非是一成不变的,当程序被杀死之后,  已注册的动态广播接收器也会被移出mReceiverResolver,直到下次程序启动,再进行动态广播的注册,当然这里面的顺序也已经变更了一次。

    2  这里也并没完整的进行广播的排序,只记录的注册的先后顺序,并未有结合优先级的处理。

 

当有广播发出时,接收顺序如下:

 技术图片

   在ActivityManagerService处理广播,当广播为有序广播时,将动态广播接收器和动态广播接收器合并起来,形成最终的有序广播接收顺序。
   上述的规则1排序为:
                                1 优先级高的先接收
                                2 同优先级的动静态广播接收器,动态优先于静态
                                3 同优先级的动态广播接收器  或者同优先级的静态广播接收器,按照图1 的流程注册顺序。
                                   即静态:先扫描的大于后扫描的,动态:先注册的大于后注册的。
  
   当广播为普通广播时,规则2排序为:
                                1 无视优先级,动态广播接收器优先于静态广播接收器
                                2 同规则1排序的第3点

 

1.为什么普通的动态广播一定在静态广播之前接收到?

广播注册registerReceiverContextImpl.java会调用到AMS.registerReceiver

public Intent registerReceiver(IApplicationThread caller, String callerPackage,
        IIntentReceiver receiver, IntentFilter filter, String permission, int userId) 
    ArrayList<Intent> stickyIntents = null;
    ProcessRecord callerApp = null;
    ...
    synchronized(this) 
        if (caller != null) 
            //从mLruProcesses查询调用者的进程信息
            callerApp = getRecordForAppLocked(caller);
            ...
            callingUid = callerApp.info.uid;
            callingPid = callerApp.pid;
         else 
            callerPackage = null;
            callingUid = Binder.getCallingUid();
            callingPid = Binder.getCallingPid();
        

        userId = handleIncomingUser(callingPid, callingUid, userId,
                true, ALLOW_FULL_ONLY, "registerReceiver", callerPackage);

        //获取IntentFilter中的actions. 这就是平时所加需要监听的广播action
        Iterator<String> actions = filter.actionsIterator();
        if (actions == null) 
            ArrayList<String> noAction = new ArrayList<String>(1);
            noAction.add(null);
            actions = noAction.iterator();
        

        int[] userIds =  UserHandle.USER_ALL, UserHandle.getUserId(callingUid) ;
        while (actions.hasNext()) 
            String action = actions.next();
            for (int id : userIds) 
                //从mStickyBroadcasts中查看用户的sticky Intent
                ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);
                if (stickies != null) 
                    ArrayList<Intent> intents = stickies.get(action);
                    if (intents != null) 
                        if (stickyIntents == null) 
                            stickyIntents = new ArrayList<Intent>();
                        
                        //将sticky Intent加入到队列
                        stickyIntents.addAll(intents);
                    
                
            
        
    

    ArrayList<Intent> allSticky = null;
    if (stickyIntents != null) 
        final ContentResolver resolver = mContext.getContentResolver();
        for (int i = 0, N = stickyIntents.size(); i < N; i++) 
            Intent intent = stickyIntents.get(i);
            //查询匹配的sticky广播 【见2.5.2】
            if (filter.match(resolver, intent, true, TAG) >= 0) 
                if (allSticky == null) 
                    allSticky = new ArrayList<Intent>();
                
                //匹配成功,则将给intent添加到allSticky队列
                allSticky.add(intent);
            
        
    

    //当IIntentReceiver为空,则直接返回第一个sticky Intent,
    Intent sticky = allSticky != null ? allSticky.get(0) : null;
    if (receiver == null) 
        return sticky;
    

    synchronized (this) 
        if (callerApp != null && (callerApp.thread == null
                || callerApp.thread.asBinder() != caller.asBinder())) 
            return null; //调用者已经死亡
        
        ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
        if (rl == null) 
            //对于没有注册的广播,则创建接收者队列
            rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                    userId, receiver);
            if (rl.app != null) 
                rl.app.receivers.add(rl);
             else 
                receiver.asBinder().linkToDeath(rl, 0); //注册死亡通知
                ...
                rl.linkedToDeath = true;
            
            //新创建的接收者队列,添加到已注册广播队列。
            mRegisteredReceivers.put(receiver.asBinder(), rl);
        
        ...
        //创建BroadcastFilter对象,并添加到接收者队列
        BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                permission, callingUid, userId);
        rl.add(bf);
        //新创建的广播过滤者,添加到ReceiverResolver队列
        mReceiverResolver.addFilter(bf);

        //所有匹配该filter的sticky广播执行入队操作
        //如果没有使用sendStickyBroadcast,则allSticky=null。
        if (allSticky != null) 
            ArrayList receivers = new ArrayList();
            receivers.add(bf);

            final int stickyCount = allSticky.size();
            for (int i = 0; i < stickyCount; i++) 
                Intent intent = allSticky.get(i);
                //根据intent返回前台或后台广播队列
                BroadcastQueue queue = broadcastQueueForIntent(intent);
                //创建BroadcastRecord
                BroadcastRecord r = new BroadcastRecord(queue, intent, null,
                        null, -1, -1, null, null, AppOpsManager.OP_NONE, null, receivers,
                        null, 0, null, null, false, true, true, -1);
                //该广播加入到并行广播队列
                queue.enqueueParallelBroadcastLocked(r);
                //调度广播,发送BROADCAST_INTENT_MSG消息,触发处理下一个广播。
                queue.scheduleBroadcastsLocked();
            
        
        return sticky;
    

其中mRegisteredReceivers记录着所有已注册的广播,以receiver IBinder为key, ReceiverList为value为HashMap。

动态广播添通过BroadcastQueue调用enqueueParallelBroadcastLocked添加到mParallelBroadcasts队列里面

BroadcastQueue中有两个广播队列mParallelBroadcasts,mOrderedBroadcasts,数据类型都为ArrayList:

  • mParallelBroadcasts:并行广播队列,可以立刻执行,而无需等待另一个广播运行完成,该队列只允许动态已注册的广播,从而避免发生同时拉起大量进程来执行广播,前台的和后台的广播分别位于独立的队列。
  • mOrderedBroadcasts:有序广播队列,同一时间只允许执行一个广播,该队列顶部的广播便是活动广播,其他广播必须等待该广播结束才能运行,也是独立区别前台的和后台的广播。

http://androidxref.com/6.0.1_r10/xref/frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java

public final class BroadcastQueue 
    //...

    /**
     * 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<>();

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

 

发送广播的时候在ActivityManagerService的broadcastIntent

public final int broadcastIntent(IApplicationThread caller, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle options, boolean serialized, boolean sticky, int userId) 
    enforceNotIsolatedCaller("broadcastIntent");
    synchronized(this) 
        //验证广播intent是否有效
        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, resultExtras,
                requiredPermissions, appOp, null, serialized, sticky,
                callingPid, callingUid, userId);
        Binder.restoreCallingIdentity(origId);
        return res;
    

AMS.broadcastIntentLocked

step1: 设置flag
step2: 广播权限验证
step3: 处理系统相关广播
step4: 增加sticky广播
step5: 查询receivers和registeredReceivers
step6: 处理并行广播
step7: 合并registeredReceivers到receivers
step8: 处理串行广播

private final int broadcastIntentLocked(ProcessRecord callerApp,     
        String callerPackage, Intent intent, String resolvedType,     
        IIntentReceiver resultTo, int resultCode, String resultData,     
        Bundle map, String requiredPermission,     
        boolean ordered, boolean sticky, int callingPid, int callingUid)      
  
    …………  
    …………  
      
    // 静态广播接收器list  
    List receivers = null;   
      
    // 动态广播接收器List  
    List<BroadcastFilter> registeredReceivers = null;     
  
    // 获取静态广播接收器mReceivers  
    try      
        if (intent.getComponent() != null)      
            // Broadcast is going to one specific receiver class...     
            ActivityInfo ai = AppGlobals.getPackageManager().     
                getReceiverInfo(intent.getComponent(), STOCK_PM_FLAGS);     
            if (ai != null)      
                receivers = new ArrayList();     
                ResolveInfo ri = new ResolveInfo();     
                ri.activityInfo = ai;  
                receivers.add(ri);     
                 
         else      
            // Need to resolve the intent to interested receivers...     
            if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)     
                     == 0)      
                receivers =     
                    AppGlobals.getPackageManager().queryIntentReceivers(     
                            intent, resolvedType, STOCK_PM_FLAGS);     
               
            // 获取动态广播接收器mReceiverResolver  
            registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false);     
             
     catch (RemoteException ex)      
        // pm is in same process, this will never happen.     
         
    
    final boolean replacePending =     
            (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;     
        
    int NR = registeredReceivers != null ? registeredReceivers.size() : 0;    
    ……  
    // 如果接收到的广播 是普通广播。  
    if (!ordered && NR > 0)      
        // If we are not serializing this broadcast, then send the     
        // registered receivers separately so they don‘t wait for the     
        // components to be launched.     
        BroadcastRecord r = new BroadcastRecord(intent, callerApp,     
                callerPackage, callingPid, callingUid, requiredPermission,     
                registeredReceivers, resultTo, resultCode, resultData, map,     
                ordered, sticky, false);     
  
    // 很明显接收到普通广播之后,在这只处理了动态广播 registeredReceivers,对于普通广播而言,动态广播接收器要优先于静态广播接收器 无关设置的优先级  
        boolean replaced = false;     
        if (replacePending)      
            for (int i=mParallelBroadcasts.size()-1; i>=0; i--)      
                if (intent.filterEquals(mParallelBroadcasts.get(i).intent))      
                    if (DEBUG_BROADCAST) Slog.v(TAG,     
                            "***** DROPPING PARALLEL: " + intent);     
                    mParallelBroadcasts.set(i, r);     
                    replaced = true;     
                    break;     
                     
                 
             
        if (!replaced)      
            mParallelBroadcasts.add(r);     
            scheduleBroadcastsLocked();     
             
        //将registeredReceivers置为null,后面只处理静态广播接收器,所以不会有冲突。  
        registeredReceivers = null;     
        NR = 0;     
         
      
    //如果是有序广播,将静态广播接收器和动态广播接收器组合成一个最终的顺序  
    int ir = 0;     
    if (receivers != null)      
        ...     
        //合并的过程,注意顺序     
        int NT = receivers != null ? receivers.size() : 0;     
        int it = 0;     
        ResolveInfo curt = null;     
        BroadcastFilter curr = null;     
        while (it < NT && ir < NR)      
            if (curt == null)      
                curt = (ResolveInfo)receivers.get(it);     
                 
            if (curr == null)      
                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;     
                 
             
      

2.为什么为什么有序广播和普通广播顺序不一样

有序广播,将静态广播接收器和动态广播接收器组合成一个最终的按照优先级顺序  

 //如果是有序广播,将静态广播接收器和动态广播接收器组合成一个最终的顺序  
    int ir = 0;     
    if (receivers != null)      
        ...     
        //合并的过程,注意顺序     
        int NT = receivers != null ? receivers.size() : 0;     
        int it = 0;     
        ResolveInfo curt = null;     
        BroadcastFilter curr = null;     
        while (it < NT && ir < NR)      
            if (curt == null)      
                curt = (ResolveInfo)receivers.get(it);     
                 
            if (curr == null)      
                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;     
                 
             

最后举个例子:

(以下的静A 表示静态广播接收器,同理动B。)

1 静A (优先级1)

2 动B(优先级1)

3 静C (优先级2,后扫描)

4 静D (优先级2,先扫描)

5 动E   (优先级2,先注册)

6 动F  (优先级2,后注册)

当来了一个 有序广播,接收顺序如下:动E >  动F  > 静D > 静C > 动B > 静A

当来了一个 普通广播,接收顺序如下:动E >  动F  > 动B > 静D > 静C > 静A

 

以上是关于Android 静态广播和动态广播接收顺序的主要内容,如果未能解决你的问题,请参考以下文章

Android 第六讲 广播接收器和服务

android有序广播和无序广播的区别

Android 静态注册广播接收者和动态注册广播接收者(Android8.0之前和之后)

android(eclipse)广播机制知识梳理

Android 静态注册广播接收者和动态注册广播接收者

android8-android10静态广播收到到