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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM