AMS分析:BroadcastReceiver管理

Android系統(tǒng)的廣播機制是一種基于消息發(fā)布和訂閱的事件驅(qū)動模型,即廣播發(fā)送者負責(zé)發(fā)布消息奄抽,而接收者需要先訂閱消息,才能接收到消息,Android系統(tǒng)中的廣播機制是BroadcastReceiver組件糯累。

廣播接收者需要首先將自己注冊,最終他們是將自己注冊到了AMS服務(wù)中册踩,當(dāng)廣播發(fā)送者發(fā)送一個廣播的時候泳姐,首先發(fā)送到AMS服務(wù)中,然后由AMS服務(wù)發(fā)送給對應(yīng)的接收者棍好。

BroadCastReceiver注冊分析

首先我們來分析廣播接收者的注冊過程仗岸。我們知道,在Activity或者Service中注冊一個廣播接收者調(diào)用registerReceiver方法來接收借笙,該方法最終調(diào)用ContextImpl方法來實現(xiàn)扒怖。

1. ContextImpl.registerReceiver

public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
            String broadcastPermission, Handler scheduler) {
        return registerReceiverInternal(receiver, getUserId(),
                filter, broadcastPermission, scheduler, getOuterContext());
    }
    
 private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
            IntentFilter filter, String broadcastPermission,
            Handler scheduler, Context context) {
        IIntentReceiver rd = null;
        if (receiver != null) {
            if (mPackageInfo != null && context != null) {
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                rd = mPackageInfo.getReceiverDispatcher(
                    receiver, context, scheduler,
                    mMainThread.getInstrumentation(), true);
            } else {
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                rd = new LoadedApk.ReceiverDispatcher(
                        receiver, context, scheduler, null, true).getIIntentReceiver();
            }
        }
        try {
            return ActivityManagerNative.getDefault().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName,
                    rd, filter, broadcastPermission, userId);
        }
    }

注冊的時候registerReceiver方法直接調(diào)用了registerReceiverInternal方法來實現(xiàn)具體的邏輯,這個方法比較簡單业稼,mPackageInfo是一個LoadedApk類型的對象盗痒,如果mPackageInfo不為空,則根據(jù)receiver到LoadedApk中查找這個Receiver關(guān)聯(lián)的Binder對象低散,如果mPackageInfo為空著直接創(chuàng)建一個Binder對象和receiver關(guān)聯(lián)起來俯邓。最后通過進程間請求,調(diào)用AMS服務(wù)的registerReceiver方法來注冊該廣播

我們知道LoadedApk這個類代表了一個加載到內(nèi)存中的應(yīng)用程序熔号,它里邊保存了一個應(yīng)用程序的基本資源信息稽鞭,包括ActivityThread,ApplicationInfo,包名引镊,資源路徑朦蕴,Lib路徑等信息篮条,它同時還保存了所有注冊的廣播的信息。

private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
        = new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();

這個mReceivers中以Context為鍵值保存了一個Map類型的信息ArrayMap<BroadcastReceiver, ReceiverDispatcher>吩抓,一個Context代表了一個Activity或者一個Service涉茧,因為Activity和Service最終都實現(xiàn)了Context。以Context為鍵值查找的時候可以直接找到某個Activity或者Service中注冊的所有廣播接收者疹娶。

接著看ArrayMap<BroadcastReceiver, ReceiverDispatcher>伴栓,這個是以BroadCastReceiver為鍵值保存了一個ReceiverDispatcher的值,主要是為了將BroadcastReceiver和一個ReceiverDispatcher關(guān)聯(lián)起來雨饺。

首先我們來看下LoadedApk.ReceiverDispatcher這個類具體是什么作用钳垮。

static final class ReceiverDispatcher {
 
        final static class InnerReceiver extends IIntentReceiver.Stub {
            final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;
            final LoadedApk.ReceiverDispatcher mStrongRef;
 
            InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {
                mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
                mStrongRef = strong ? rd : null;
            }
            public void performReceive(Intent intent, int resultCode, String data,
                    Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
                LoadedApk.ReceiverDispatcher rd = mDispatcher.get();
                ……
            }
        }
        //一個Binder類型的對象,實現(xiàn)了Binder的服務(wù)端
        final IIntentReceiver.Stub mIIntentReceiver;
        //指向一個BoradCastReceiver
        final BroadcastReceiver mReceiver;
        //指向發(fā)送者的Context上下文信息
        final Context mContext;
        //指向發(fā)送者進程主線程的Hander對象沛膳,通過它可以向主線程發(fā)送消息
        final Handler mActivityThread;
        final Instrumentation mInstrumentation;
        
        ……

Receiver這個類中保存了一些變量扔枫,分別指向了發(fā)送者的一些信息,其中InnerReceiver類型的變量最重要锹安,它實現(xiàn)了IIntentReceiver.Stub短荐,是一個Binder類型的本地對象,屬于Binder服務(wù)端叹哭。

這個類的主要作用就是忍宋,把Binder對象通過進程間傳遞出去,其他進程要和這邊的Receiver通信的時候风罩,直接調(diào)用該Binder的代理端糠排,這邊的InnerReceiver就可以接收到消息,然后在通過主線程的Handler對象發(fā)送個主線程來處理這個消息超升。

接著看LoadedAPk.getReceiverDispatcher方法的實現(xiàn)入宦。

public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
            Context context, Handler handler,
            Instrumentation instrumentation, boolean registered) {
        synchronized (mReceivers) {
            LoadedApk.ReceiverDispatcher rd = null;
            ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
            if (registered) {
                //根據(jù)當(dāng)前的Context,查找關(guān)于這個Activity或者Service注冊的所有廣播接收者信息
                map = mReceivers.get(context);
                if (map != null) {
                    //找到這個廣播接收者關(guān)聯(lián)的一個Binder服務(wù)端對象
                    rd = map.get(r);
                }
            }
            if (rd == null) {
                //如果LoadedApk中沒有保存的話室琢,就先創(chuàng)建一個乾闰,然后保存的LoadedAPk的mReceivers集合中
                rd = new ReceiverDispatcher(r, context, handler,
                        instrumentation, registered);
                if (registered) {
                    if (map == null) {
                        map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
                        mReceivers.put(context, map);
                    }
                    map.put(r, rd);
                }
            } else {
                rd.validate(context, handler);
            }
            rd.mForgotten = false;
            return rd.getIIntentReceiver();
        }
    }

根據(jù)前面方法的分析看出,這個方法的作用是用來在LoadedApk中查找這個廣播接收者關(guān)聯(lián)的Binder對象的盈滴。如果有則直接返回涯肩,如果找不到則new一個ReceiverDispatcher對象,保存到mReceivers集合中巢钓,然后再返回Receiver關(guān)聯(lián)的Binder對象病苗。

2. AMS.registerReceiver

public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
        ArrayList<Intent> stickyIntents = null;
        ProcessRecord callerApp = null;
        int callingUid;
        int callingPid;
        synchronized(this) {
            if (caller != null) {
                 //獲取調(diào)用注冊程序的進程信息和Uid,Pid
                callerApp = getRecordForAppLocked(caller);
               ……
                callingUid = callerApp.info.uid;
                callingPid = callerApp.pid;
            } 
 
            userId = handleIncomingUser(callingPid, callingUid, userId,
                    true, ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
 
            Iterator<String> actions = filter.actionsIterator();
            ……
            int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
            //查詢所有的Action相關(guān)的粘性廣播症汹,并保存到stickyIntents中
            while (actions.hasNext()) {
                String action = actions.next();
                for (int id : userIds) {
                    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>();
                            }
                            stickyIntents.addAll(intents);
                        }
                    }
                }
            }
        }
……
        synchronized (this) {
            //根據(jù)注冊進程傳入的binder對象從mRegisteredReceivers查找對應(yīng)的ReceiverList
            //mRegisteredReceivers列表硫朦,以注冊進程傳入的receiver.asBinder為key,保存了ReceiverList
            ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
            //如果第一次調(diào)用則new一個ReceiverList,并添加到mRegisteredReceivers列表中
            if (rl == null) {
                rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                        userId, receiver);
                if (rl.app != null) {
                    rl.app.receivers.add(rl);
                } else {
                    try {
                        receiver.asBinder().linkToDeath(rl, 0);
                    } catch (RemoteException e) {
                        return sticky;
                    }
                    rl.linkedToDeath = true;
                }
                mRegisteredReceivers.put(receiver.asBinder(), rl);
            }
            ……
            //創(chuàng)建一個BroadCastReceiver對象并保存到ReceiverList中,BroadcastFilter表示廣播的過濾條件背镇,一個廣播接收者可以有多個過濾條件
            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                    permission, callingUid, userId);
            rl.add(bf);
            //同時也保存到mReceiverResolve列表中阵幸,這個列表保存了系統(tǒng)中所有動態(tài)注冊的廣播接收者所對應(yīng)的BroadCastFilter
            mReceiverResolver.addFilter(bf);
 
            // Enqueue broadcasts for all existing stickies that match
            // this filter.
            if (allSticky != null) {
               ……
 
                final int stickyCount = allSticky.size();
                for (int i = 0; i < stickyCount; i++) {
                    //遍歷所有符合條件的stick廣播Intent花履,然后把它封裝成一個BroadcastRecord并添加到BroadcastQueue的ParallelBroadcastLocked列表
                    Intent intent = allSticky.get(i);
                    BroadcastQueue queue = broadcastQueueForIntent(intent);
                    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);
                    queue.scheduleBroadcastsLocked();
                }
            }
 
            return sticky;
        }
    }

該方法的主要工作:

  1. 獲取調(diào)用程序的進程信息ProcessRecord及PID和Uid
  2. 從AMS的mStickyBroadcasts列表中查詢所有和action匹配的sticky廣播芽世,并保存到allSticky列表中挚赊,該Action就是即將要注冊的廣播的Action。
  3. 我們知道參數(shù)receiver是一個IIntentReceiver類型的變量济瓢,是binder代理端荠割,指向了應(yīng)用進程要注冊的廣播Receiver,registerdReceivers是一個map類型的變量旺矾,它以receiver.asBInder為key保存了ReceiverList蔑鹦。這樣應(yīng)用進程的一個Receiver就和AMS服務(wù)中的一個ReceiverList關(guān)聯(lián)起來了。

ReceiverList又是什么呢箕宙? 它繼承自ArrayList<BroadCastFilter>嚎朽,同時它內(nèi)部有一個IntentReceiver類型的變量指向一個Receiver,這樣一個Receiver就和多個BroadcastFilter關(guān)聯(lián)起來了柬帕。因為一個廣播可以有多個過濾條件哟忍。

第三步的主要工作就是從mRegisterReceivers中查找receiver對應(yīng)的ReceiverList信息,如果為空陷寝,就表示該廣播沒有注冊锅很,需要新創(chuàng)建一個ReceiverList并添加到mRegisterReceivers列表中。

  1. 創(chuàng)建一個BroadcastFilter凤跑,添加到上面的ReceiverList中爆安,同時也添加到mReceiverResolver集合中,mReceiverResolver集合存儲了所有動態(tài)注冊的廣播的過濾條件BroadcastFilter仔引,便于廣播發(fā)送的時候查找Receiver扔仓。
  2. 將第二步找到的和此接收者Action相關(guān)的sticky廣播遍歷,封裝成一個BroadcastRecord咖耘,加入隊列準(zhǔn)備發(fā)送翘簇。

3. BroadCastQueue. scheduleBroadcastsLocked

public void scheduleBroadcastsLocked() {
        //發(fā)送Handler消息個AMS服務(wù),通知AMS服務(wù)發(fā)送廣播
        mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
        mBroadcastsScheduled = true;
    }

最終調(diào)用scheduleBroadcastsLocked方法鲤看,發(fā)送一個Hander消息給AMS服務(wù)缘揪,由AMS服務(wù)來發(fā)送廣播。

同時也了解了sticky廣播的實現(xiàn)原理义桂,發(fā)送完的sticky廣播會保存到mStickyBraodcasts集合中找筝,在注冊的時候根據(jù)action查找對應(yīng)的sticky廣播,如有有相關(guān)的sticky廣播的話慷吊,注冊完成后袖裕,立刻發(fā)送出去,這樣剛注冊的Receiver就可以立刻受到sticky的廣播了

這樣廣播的動態(tài)注冊就完成了溉瓶。經(jīng)過分析可以看出急鳄,AMS服務(wù)是廣播的注冊中心谤民,廣播接收者要想接收廣播消息需要將自己注冊到AMS服務(wù)中去,指定要接收的廣播類型疾宏,發(fā)送廣播也是首先發(fā)送給AMS服務(wù)张足,由AMS服務(wù)來找到對應(yīng)的廣播接收者,然后在調(diào)用對應(yīng)的廣播接收者來處理坎藐。

Broadcast的發(fā)送過程分析

廣播的發(fā)送方式分為有序廣播和無序廣播为牍,在注冊廣播的時候可以指定廣播的優(yōu)先級,這樣廣播就會優(yōu)先發(fā)送給優(yōu)先級比較高的廣播岩馍,然后才會發(fā)送給優(yōu)先級比較低的廣播碉咆。而無序廣播則不管廣播接收者的優(yōu)先級。

同樣要分析廣播的發(fā)送過程需要從ContextImpl類開始分析蛀恩,因為它最終實現(xiàn)了sendBroadcast的接口和方法疫铜,service和activity發(fā)送廣播的過程都是調(diào)用ContextImpl的方法來實現(xiàn)的。

1. ContextImpl.sendBroadcast

public void sendBroadcast(Intent intent) {
        ……
        try {
            intent.prepareToLeaveProcess();
            ActivityManagerNative.getDefault().broadcastIntent(
                    mMainThread.getApplicationThread(), intent, resolvedType, null,
                    Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
                    getUserId());
        } 
    }

ContextImpl的發(fā)送廣播的方法最終通過進程間通信請求双谆,調(diào)用AMS服務(wù)的broadcastIntent方法壳咕。同時在參數(shù)中將發(fā)送者的ApplicationThread和廣播的intent傳遞給了AMS服務(wù)。

這個方法比較簡單佃乘,進程間通信方式不再解釋囱井,我們直接看AMS服務(wù)的broadcastIntent方法

2. AMS.broascastIntent

 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) {
        ……
        synchronized(this) {
            //驗證Intent信息
            intent = verifyBroadcastLocked(intent);
            //獲取調(diào)用進程的信息ProcessRecord
            final ProcessRecord callerApp = getRecordForAppLocked(caller);
            //獲取調(diào)用應(yīng)用的uid和pid
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            //通過broadcastIntentLocked方法來繼續(xù)處理發(fā)送廣播的邏輯
            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;
        }
    }

這個方式實現(xiàn)也是比較簡單,獲取調(diào)用進程的ProcessRecord信息和調(diào)用程序的UID和PID趣避,然后直接調(diào)用broadcastIntentLocked方法來繼續(xù)處理發(fā)送廣播的邏輯

3. AMS.broadcastIntentLocked

這個方法比較長庞呕,我們一部分一部分的來看

intent = new Intent(intent);
 
        //確保要接收的廣播的用戶仍然存在,否則的話直接返回錯誤信息
        if (userId != UserHandle.USER_ALL && !isUserRunningLocked(userId, false)) {
            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;
            }
        }
        
        //如果當(dāng)前的廣播類型是sticky廣播程帕,則把廣播添加到List列表中去
        if (sticky) {
            //首先檢查相關(guān)的權(quán)限信息
            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;
            ……
            
            if (userId != UserHandle.USER_ALL) {
                //如果這個廣播不是發(fā)送給所有用戶的住练,則檢查發(fā)送給所有用戶的廣播中是否包含這個廣播,如果包含則和發(fā)送給所有用戶的廣播有沖突愁拭,拋出異常信息
                ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(
                        UserHandle.USER_ALL);
                if (stickies != null) {
                    ArrayList<Intent> list = stickies.get(intent.getAction());
                    if (list != null) {
                        int N = list.size();
                        int i;
                        for (i=0; i<N; i++) {
                            if (intent.filterEquals(list.get(i))) {
                                throw new IllegalArgumentException(
                                        "Sticky broadcast " + intent + " for user "
                                        + userId + " conflicts with existing global broadcast");
                            }
                        }
                    }
                }
            }
            //找到和這個用戶相關(guān)的所有的sticky廣播的信息
            ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
            if (stickies == null) {
                stickies = new ArrayMap<>();
                mStickyBroadcasts.put(userId, stickies);
            }
            //從得到的廣播信息中讲逛,找到和當(dāng)前要發(fā)送廣播action相同的sticky廣播
            ArrayList<Intent> list = stickies.get(intent.getAction());
            if (list == null) {
                list = new ArrayList<>();
                stickies.put(intent.getAction(), list);
            }
            //遍歷拿到的stick廣播信息,如果已經(jīng)存在岭埠,則更新替換掉他
            //如果不存在盏混,則添加進去
            final int stickiesCount = list.size();
            int i;
            for (i = 0; i < stickiesCount; i++) {
                if (intent.filterEquals(list.get(i))) {
                    // This sticky already exists, replace it.
                    list.set(i, new Intent(intent));
                    break;
                }
            }
            if (i >= stickiesCount) {
                list.add(new Intent(intent));
            }
        }

參數(shù)sticky用來描述要發(fā)送的廣播是否是一個sticky類型的廣播,如果是惜论,就需要對sticky廣播進行一些處理许赃,在前面我們分析過,在注冊廣播的時候馆类,如果AMS中保存了對應(yīng)action類型的sticky廣播混聊,則直接將保存的sticky廣播發(fā)送出去。那些sticky廣播是如何保存到AMS服務(wù)中去的呢乾巧?

代碼邏輯就是在這邊處理的句喜,在注冊一個廣播的時候预愤,如果它是sticky類型的廣播,就直接將它保存在AMS服務(wù)的mStickyBroadcasts集合中咳胃。

首先從AMS系統(tǒng)服務(wù)中查詢Action相關(guān)的sticky廣播植康,然后遍歷,如果該sticky廣播已經(jīng)在AMS中注冊保存了拙绊,則用新的Sticky廣播替換掉它向图,否則直接添加到列表中去。

  List receivers = null;
        List<BroadcastFilter> registeredReceivers = null;
        //如果沒有FLAG_RECEIVER_REGISTERED_ONLY標(biāo)簽标沪,說明不是發(fā)送給動態(tài)注冊的廣播的,那么調(diào)用collectReceiverComponents嗜傅,查找所有和該Intent相關(guān)的廣播接收者
        //保存到receivers列表中
        if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
                 == 0) {
            receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
        }
        //如果沒有指定特定的廣播接收者金句,則查找所用的動態(tài)注冊的廣播接收者的信息
        if (intent.getComponent() == null) {
            ……
                registeredReceivers = mReceiverResolver.queryIntent(intent,
                        resolvedType, false, userId);
            }
        }

第二部分的主要處理邏輯就是查找和intent相關(guān)的廣播接收者。我們知道廣播注冊的時候有兩種注冊方法吕嘀,一種是靜態(tài)注冊方法违寞,在Manifest文件中注冊,另一種是動態(tài)注冊方法偶房,在代碼中調(diào)用registerBroadcast方法來注冊廣播趁曼。

這個地方進行了兩次查找,判斷發(fā)送廣播的intent的標(biāo)簽棕洋,如果不是僅僅發(fā)送給動態(tài)注冊的廣播的時候挡闰,就調(diào)用collectReceiverComponents方法查詢所有Intent感興趣的廣播接收者,這里僅僅去PMS服務(wù)中查詢了感興趣的靜態(tài)注冊的廣播接收者掰盘,保存在receivers列表中摄悯。

然后判斷這個廣播是否指定了值發(fā)送給特定的廣播接收者,如果沒有指定的話愧捕,默認發(fā)送給所有感興趣的廣播接收者奢驯,這時候再去查詢一次,這次查詢的是所有對這個Intent感興趣的動態(tài)注冊的廣播接收者次绘,保存在registeredReceivers列表中瘪阁。

  int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
        if (!ordered && NR > 0) {
            //如果當(dāng)前不是有序廣播,且動態(tài)注冊的感興趣的廣播不為0邮偎,將廣播intent封裝為一個BroadcastRecord
            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);
           ……
            //發(fā)送該廣播給動態(tài)注冊的廣播接收者
                queue.enqueueParallelBroadcastLocked(r);
                queue.scheduleBroadcastsLocked();
            }
            //發(fā)送完成后管跺,將registeredReceivers 置為 null
            registeredReceivers = null;
            NR = 0;
        }

第三部分的處理工作:

如果當(dāng)前發(fā)送的廣播不是有序廣播,且動態(tài)注冊的廣播接收者>0钢猛,此時則需要把這個廣播優(yōu)先發(fā)送給動態(tài)注冊的廣播接收者伙菜,為什么不和靜態(tài)注冊的廣播一起發(fā)送,卻要分開發(fā)送呢命迈?這樣做是因為贩绕,動態(tài)注冊的廣播接收者一般情況下都已經(jīng)運行起來了火的,而靜態(tài)注冊的廣播接收者只是在Manifest文件中注冊,要想接收廣播必須要等待要接收的組件啟動起來才可淑倾,所以這邊處理選擇分開發(fā)送馏鹤,優(yōu)先發(fā)送動態(tài)注冊的廣播接收者。

發(fā)送的廣播的時候娇哆,無序廣播都添加到了mParallelBroadcats隊列中湃累,具體發(fā)送的處理邏輯后邊在詳細分析。

下一步處理發(fā)送給靜態(tài)注冊的廣播接收者的邏輯碍讨。

  int ir = 0;
        //感興趣的靜態(tài)注冊廣播接收者不為空
        if (receivers != null) {
            //首先對于特殊的ACTION_PACKAGE_ADDED的廣播需要特殊處理
            //這樣是為了防止一些應(yīng)用則安裝完成之后監(jiān)聽到package_add的廣播就立刻運行
            //所以要把它已標(biāo)記起來治力,以后從廣播接收者信息中移除
            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 };
                    }
                }
            //同樣,如果Intenty有ACTION_EXTERNAL_APPLICATIONS_AVAILABLE標(biāo)簽勃黍,則同樣移除
            } 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 curt = (ResolveInfo)receivers.get(it);
                            if (curt.activityInfo.packageName.equals(skipPackage)) {
                                receivers.remove(it);
                                it--;
                                NT--;
                            }
                        }
                    }
                }

處理靜態(tài)注冊的廣播接收者邏輯的時候宵统,首先判斷該廣播事件是否是Package_add的廣播事件,如果是的話覆获,需要從廣播接收者列表中移除马澈,這樣為了防止一些毒瘤應(yīng)用在安裝完成后接收到package_add廣播就立刻運行。

    int NT = receivers != null ? receivers.size() : 0;
            int it = 0;
            ResolveInfo curt = null;
            BroadcastFilter curr = null;
            //上面的無序廣播處理后弄息,把mRegisterReceivers列表置為null痊班,并且把NR設(shè)置成了0
            //而此時要想滿足此處條件,NR不為0摹量,說明此處是有序廣播的處理邏輯
            //遍歷動態(tài)注冊的廣播和靜態(tài)注冊的廣播涤伐,安priority值排序合并放到receivers列表中
            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;
                }
            }
        ……
        //有序廣播接收者不為空,發(fā)送有序廣播
        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();
            }
        }

此處處理邏輯主要為有序廣播的處理邏輯荆永。

在前面的分析中知道废亭,如果是無序廣播的時候,動態(tài)注冊的廣播接收者就直接處理了具钥,然后把mRegisteredReceivers列表置為了null豆村,所以在有序廣播的時候就不會有動態(tài)注冊的廣播接收者合并進來。此時就會把靜態(tài)廣播放到有序廣播類別mOrderedBroadcasts隊列中發(fā)送骂删。為什么會把靜態(tài)注冊的廣播接收和按照有廣播的邏輯來發(fā)送呢掌动?有序廣播發(fā)送的處理邏輯是發(fā)送完成一個在發(fā)送另一個,而無序廣播是直接循環(huán)發(fā)送出去宁玫,靜態(tài)注冊的廣播接收者的進程不一定已經(jīng)運行起來了粗恢,在發(fā)送的過程中可能需要先啟動進程,這樣很消耗資源欧瘪,所以一個一個發(fā)送會好一點眷射。

如果發(fā)送的是有序廣播,動態(tài)注冊的廣播在前面邏輯中則不會處理,此處就會把動態(tài)注冊的廣播和靜態(tài)注冊的廣播按照priority值排序妖碉,然后合并到receivers列表中涌庭。

最終把receiver列表中的廣播接收者封裝成一個BroadcastRecord對象放到mOrderedBroadcasts隊列中進行發(fā)送。

此處邏輯可以總結(jié)出來欧宜,如果是無序廣播的話坐榆。動態(tài)注冊的廣播接收者會比靜態(tài)注冊的廣播者優(yōu)先收到廣播
下面我們來分析具體的廣播發(fā)送處理邏輯:

  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);
 
            queue.enqueueOrderedBroadcastLocked(r);
            queue.scheduleBroadcastsLocked();

對于BroadCast的發(fā)送的處理邏輯大體上分為以上幾個步驟

  1. 根據(jù)廣播的Intent找到對應(yīng)的廣播隊列
  2. 將Intent和廣播接收者封裝成一個BroadcastRecord對象
  3. 將BroadcastRecord對象添加拿到queue隊列中
  4. 調(diào)用queue的scheduleBroadcastsLocked方法發(fā)送廣播

4. AMS. broadcastQueueForIntent

  BroadcastQueue broadcastQueueForIntent(Intent intent) {
        final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
        return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
    }

這個方法很簡單,就是根據(jù)Intent中是否有Flag_Receiver_foreground標(biāo)簽冗茸,來決定添加到那個隊列中去席镀。

這個標(biāo)簽是什么作用呢?看下SDK中的描述:

FLAG_RECEIVER_FOREGROUND 接受器以前臺優(yōu)先級運行

If set, when sending a broadcast the recipient is allowed to run at foreground priority, with a shorter timeout interval. During normal broadcasts the receivers are not automatically hoisted out of the background priority class.

廣播默認都是后臺優(yōu)先級夏漱,如果廣播中添加了這個標(biāo)簽就會以前臺優(yōu)先級運行豪诲,發(fā)送到接收到廣播的時間間隔會更短。

也就是說麻蹋,默認廣播都是天劍到mBgBroadcastQueue隊列中去的跛溉,只有廣播添加了這個標(biāo)簽后,才會被添加到mFgBroadcastQueue隊列中扮授。這兩個隊列中處理邏輯的區(qū)別我們后面再詳細分析。

第二步专肪,將Intent和廣播接收者隊列封裝成了一個BroadcastRecord對象刹勃,也就是說每一個廣播在AMS都是對應(yīng)一個BroadcastRecord對象的。

5. BroadcastQueue. Enqueue……BroadcastLocked

BroadcastQueue中有兩個添加隊列的方法:

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

該消息發(fā)送了了一個Handler消息嚎尤,有Hander的handleMessage來處理荔仁,最終調(diào)用了processNextBroadcast方法來處理。

7. BroadcastQueue. processNextBroadcast

動態(tài)注冊廣播接收者的處理邏輯

這個方法有點復(fù)雜芽死,主要是兩種廣播的處理邏輯不一樣乏梁,那我們就先分析動態(tài)注冊的廣播接收者處理邏輯,然后在分析靜態(tài)注冊或者有序廣播的處理邏輯关贵。

我們在前面講過遇骑,動態(tài)注冊的廣播接收者處理的時候是一次性的循環(huán)發(fā)送完國有廣播,而靜態(tài)注冊或者有序廣播則是一個發(fā)送完成在發(fā)送另一個揖曾。

 final void processNextBroadcast(boolean fromMsg) {
        synchronized(mService) {
            BroadcastRecord r;
 
           ……
            while (mParallelBroadcasts.size() > 0) {
                r = mParallelBroadcasts.remove(0);
                r.dispatchTime = SystemClock.uptimeMillis();
                r.dispatchClockTime = System.currentTimeMillis();
                final int N = r.receivers.size();
                
                for (int i=0; i<N; i++) {
                    Object target = r.receivers.get(i);
                    deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
                }
                addBroadcastToHistoryLocked(r);
            }

動態(tài)廣播的處理邏輯是在processNextBroadcast方法的開始處理的落萎,變量mParallelBroadcasts列表,從列表開始位置一次拿到保存的BroadcastRecord對象炭剪,每個BroadcastRecord中可能對應(yīng)多個廣播接收者练链,依次遍歷廣播接收者,然后調(diào)用deliverToRegisteredReceiverLocked方法發(fā)送廣播

 private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
            BroadcastFilter filter, boolean ordered) {
        boolean skip = false;
        //檢查是否具有相關(guān)權(quán)限传泊,沒有權(quán)限則skip = true 表示跳過這個廣播接收者
        ……
        //檢查接收廣播的進程是否存在逼庞,如果接收廣播的進程不存在則跳過這個廣播接收者霸妹,不向它發(fā)送該廣播
        if (filter.receiverList.app == null || filter.receiverList.app.crashing) {
            Slog.w(TAG, "Skipping deliver [" + mQueueName + "] " + r
                    + " to " + filter.receiverList + ": process crashing");
            skip = true;
        }
 
        if (!skip) {
            
            if (ordered) {
                //有序廣播處理邏輯
            }
            try {
               
                performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
                        new Intent(r.intent), r.resultCode, r.resultData,
                        r.resultExtras, r.ordered, r.initialSticky, r.userId);
                ……
            } 
        }
    }

我們只關(guān)心和我們處理邏輯相關(guān)的關(guān)鍵代碼规辱。

檢查相關(guān)的權(quán)限绿鸣,如果沒有權(quán)限則skip設(shè)置為true,
檢查廣播接收者對應(yīng)的進程是否存在疚沐,如果不存在skip設(shè)置為true
我們目前是無序廣播,所以ordered為false,最終調(diào)用performReceiveLocked方法來處理枚驻。

我么回憶下ReceiverList是什么濒旦,ReceiverList是我們在注冊廣播接收者的時候,一個廣播接收者的代表信息再登,它繼承者ArrayList<BroadcastFilter>尔邓,這樣可以實現(xiàn)一對多的數(shù)據(jù)結(jié)構(gòu),因為一個廣播接收者可能對應(yīng)多個BroadcastFilter锉矢。所以receiverList.app代表了廣播接收者所在的進程梯嗽,receiverList.receiver則是一個Binder對象,指向了App進程中的一個InnerReceiver的對象沽损。

繼續(xù)看performReceiveLocked方法如何處理灯节。

private static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
            Intent intent, int resultCode, String data, Bundle extras,
            boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
        if (app != null) {
            if (app.thread != null) {
                app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                        data, extras, ordered, sticky, sendingUser, app.repProcState);
            }
        } else {
            receiver.performReceive(intent, resultCode, data, extras, ordered,
                    sticky, sendingUser);
        }
    }

參數(shù)receiver是一個IIntentReceiver接口的Binder代理對象,它的服務(wù)端是應(yīng)用進程的InnerReceiver绵估,參數(shù)app則是接收廣播的應(yīng)用進程的進程對象ProcessRecord炎疆,在注冊廣播接收者的時候,我們知道在創(chuàng)建廣播接收者對應(yīng)的ReceiverList對象的時候會初始化app的值国裳。

ReceiverList rl = new ReceiverList(this, callerApp, callingPid, callingUid, userId, receiver);

一般情況下receiver的變量app都是不為null的形入,所以大部分情況下廣播都是通過應(yīng)用進程的ApplicationThread對象的Binder代理對象,通過進程間調(diào)用缝左,將這個廣播發(fā)送到應(yīng)用進程主線程來處理亿遂,應(yīng)用進程的主線程再調(diào)用對應(yīng)的receiver來處理。而不是直接通過Receiver的Binder代理端來進行進程間的調(diào)用渺杉。

接著看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);
        }

此時直接調(diào)用receiver的performReceive方法,receiver是一個binder類型的代理對象是越,這個方法調(diào)用最終調(diào)用到InnerReceiver的performReceiver方法中耳舅。

public void performReceive(Intent intent, int resultCode, String data,
                    Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
                //得到當(dāng)前的dispatcher
                LoadedApk.ReceiverDispatcher rd = mDispatcher.get();
                if (rd != null) {
                    rd.performReceive(intent, resultCode, data, extras,
                            ordered, sticky, sendingUser);
                }
                ……
            }

這個方法很簡單,首先得到對應(yīng)的dispatcher英妓,然后調(diào)用dispatcher的方法來處理挽放。

public void performReceive(Intent intent, int resultCode, String data,
                Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
            Args args = new Args(intent, resultCode, data, extras, ordered,
                    sticky, sendingUser);
            if (!mActivityThread.post(args)) {
                if (mRegistered && ordered) {
                    IActivityManager mgr = ActivityManagerNative.getDefault();
                    args.sendFinished(mgr);
                }
            }
        }

mActivityThread是一個Handler對象,該對象指向ActivityThread的Hander對象蔓纠,Handler的post最終調(diào)用了Args的run方法辑畦。

final class Args extends BroadcastReceiver.PendingResult implements Runnable {
           ……
 
            public void run() {
                final BroadcastReceiver receiver = mReceiver;
               ……
                try {
                    ClassLoader cl =  mReceiver.getClass().getClassLoader();
                    intent.setExtrasClassLoader(cl);
                    setExtrasClassLoader(cl);
                    receiver.setPendingResult(this);
                    receiver.onReceive(mContext, intent);
                } 
                ……
            }
        }

變量receiver指向了一個廣播接收者BroadcastReceiver,然后調(diào)用receiver.onReceive()方法腿倚。到此動態(tài)注冊的廣播接收者接收廣播的邏輯就處理完成了纯出。

下一步看靜態(tài)注冊或者有序廣播的發(fā)送邏輯。

靜態(tài)注冊或者有序廣播接收者的處理邏輯

我們再回到processNextBroadcast方法中,接著往下看

            //判斷當(dāng)前是否正在等待靜態(tài)注冊的廣播接收者控件啟動并接收任務(wù)
            if (mPendingBroadcast != null) {
 
                boolean isDead;
                synchronized (mService.mPidsSelfLocked) {
                    ProcessRecord proc = mService.mPidsSelfLocked.get(mPendingBroadcast.curApp.pid);
                    isDead = proc == null || proc.crashing;
                }
                if (!isDead) {
                    //如果有個任務(wù)正在啟動則直接返回繼續(xù)等待
                    return;
                }
            }
 
            boolean looped = false;
 
            do {
                //從mOrderedBroadcasts列表中獲取下一個要發(fā)送的廣播
                r = mOrderedBroadcasts.get(0);
                boolean forceReceive = false;
 
                //判斷有序廣播是否發(fā)送超時暂筝,如果發(fā)送超時則直接強制停止發(fā)送這個廣播
                int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
                if (mService.mProcessesReady && r.dispatchTime > 0) {
                    long now = SystemClock.uptimeMillis();
                    if ((numReceivers > 0) &&
                            (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
                        broadcastTimeoutLocked(false); //強制停止這個廣播
                        forceReceive = true;
                        r.state = BroadcastRecord.IDLE;
                    }
                }
 
                ……
 
                if (r.receivers == null || r.nextReceiver >= numReceivers
                        || r.resultAbort || forceReceive) {
                    //如果這個廣播接收者receivers已經(jīng)處理完了箩言,或者這個廣播已經(jīng)強制停止了
                    if (r.resultTo != null) {
                        ……
                    }
 
                    //從Handler延時消息中移除timeout的消息
                    cancelBroadcastTimeoutLocked();
 
                    //添加到history中
                    addBroadcastToHistoryLocked(r);
                    //從mOrderedBroadcasts列表中移除
                    mOrderedBroadcasts.remove(0);
                    //置為null
                    r = null;
                    looped = true; 
                    continue;
                }
            } while (r == null);

首先判斷mPendingBroadcast是不是為空,這個mPendingBroadcast的作用是什么呢焕襟?

我們知道陨收,mOrderedBroadcast中保存的廣播接收者都是需要一個發(fā)送完成另一個才可以發(fā)送的。其中靜態(tài)注冊的廣播接收者有可能對應(yīng)的進程還未啟動鸵赖,者就需要首先來啟動該進程务漩。如果是這種情況的話mPendingBroadcast就是描述了一個正在等待靜態(tài)注冊的對應(yīng)進程啟動的廣播。

如果他不為空它褪,說明當(dāng)前有任務(wù)正在處理饵骨,在對這個正在啟動的進程檢查之后就直接退出了。

如果mPendingBroadcast不為空茫打,AMS就會繼續(xù)等待居触,否則的話就會準(zhǔn)備處理mOrderedBroadcast的下一個廣播。

接下來是一個while循化老赤,主要工作是從mOrderedBroadcasts列表中找到下一個要處理的廣播轮洋,退出條件是 r != null,即找到下一個合適的廣播。

1.首先從列表的開始拿出第一個BroadcastRecord抬旺,第一個有可能是正在處理的廣播砖瞧,判斷他是否已經(jīng)開始分發(fā)廣播,如果開始但是已經(jīng)超時嚷狞,則直接強制停止這個廣播。
2.然后荣堰,判斷這個廣播是否已經(jīng)處理完成床未,或者是否強制停止,然后直接停止該廣播振坚。
3.將發(fā)送給AMS的關(guān)于超時的延時消息移除
4.將第一個廣播移除掉薇搁,并把r設(shè)置為null,表示沒有找到合適的廣播渡八,然后繼續(xù)循環(huán)查找啃洋,知道找到下一個合適的廣播,退出循環(huán)屎鳍。

   //獲取下一個要執(zhí)行的Receiver信息
            int recIdx = r.nextReceiver++;
 
            r.receiverTime = SystemClock.uptimeMillis();
            //如果recIdx = 0宏娄,說明這個廣播的才剛開始發(fā)送廣播任務(wù),則記錄下當(dāng)前的時間為分發(fā)廣播的時間
            if (recIdx == 0) {
                r.dispatchTime = r.receiverTime;
                r.dispatchClockTime = System.currentTimeMillis();
            }
            if (! mPendingBroadcastTimeoutMessage) {
                //向AMS的handler發(fā)送一個延時的超時消息逮壁,到超時的時間后自動發(fā)送
                long timeoutTime = r.receiverTime + mTimeoutPeriod;
                setBroadcastTimeoutLocked(timeoutTime);
            }
……
            final Object nextReceiver = r.receivers.get(recIdx);
 
            if (nextReceiver instanceof BroadcastFilter) {
                BroadcastFilter filter = (BroadcastFilter)nextReceiver;
                deliverToRegisteredReceiverLocked(r, filter, r.ordered);
                return;
            }

我們知道一個廣播可能對應(yīng)多個廣播接收者孵坚,在mOrderedBroadcasts列表處理的時候是需要向一個Receiver發(fā)送成功后,在向另一個發(fā)送。BroadcastRecord的變量nextReceiver表示下一個廣播接收者的序號卖宠。

如果為0巍杈,說明這個廣播的廣播接收者一個都沒有發(fā)送,也就是說這個廣播才剛開始發(fā)送扛伍,此時就要記錄下當(dāng)前廣播分發(fā)的時間筷畦,然后計算下超時時間然后向AMS服務(wù)發(fā)送一個延時消息。接著刺洒,獲取到下一個Receiver鳖宾,判斷它是否是BroadcastFilter類型,如果是作媚,然后調(diào)用deliverToRegisteredReceiverLocked方法來處理攘滩,我們在動態(tài)注冊的廣播處理的時候已經(jīng)分析過這個方法了,這邊不再討論纸泡。

 ResolveInfo info =
                (ResolveInfo)nextReceiver;
            ComponentName component = new ComponentName(
                    info.activityInfo.applicationInfo.packageName,
                    info.activityInfo.name);
            //判斷權(quán)限相關(guān)及其他來覺是否跳過這個廣播接收者
            boolean skip = false;
            int perm = mService.checkComponentPermission(info.activityInfo.permission,
            ……
 
            //判斷這個廣播接收者對應(yīng)的進程是否已經(jīng)啟動
            ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
                    info.activityInfo.applicationInfo.uid, false);
            if (app != null && app.thread != null) {
                //如果已經(jīng)啟動則發(fā)送廣播給當(dāng)前的廣播接收者
                try {
                    app.addPackage(info.activityInfo.packageName,
                            info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
                    processCurBroadcastLocked(r, app);
                    return;
                } 
                ……
            }
 
            //否則的話則新建一個進程漂问,并把mPendingBroadcast設(shè)置為當(dāng)前的廣播接收者,等待新進程啟動完成
            if ((r.curApp=mService.startProcessLocked(targetProcess,
                    info.activityInfo.applicationInfo, true,
                    r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
                    "broadcast", r.curComponent,
                    (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
                            == null) {
                ……
            }
            
            mPendingBroadcast = r;
            mPendingBroadcastRecvIndex = recIdx;

廣播接收者分為兩種類型女揭,動態(tài)注冊和靜態(tài)注冊蚤假,靜態(tài)注冊的廣播是ResolveInfo類型,所以此處主要來處理靜態(tài)注冊的廣播接收者發(fā)送廣播的邏輯吧兔。
1.首先磷仰,判斷相關(guān)的權(quán)限及其他信息來決定是否可以發(fā)送廣播給這個廣播接收者
2.如果可以,查詢廣播接收者對應(yīng)的進程是否存在境蔼,如果已經(jīng)存在灶平,直接 調(diào)用processCurBroadcastLocked方法來處理當(dāng)前廣播

如果不存在,則啟動一個新的進程箍土,將mPendingBroadcast的值設(shè)置為當(dāng)前的BroadcastRecord的值逢享,然后等待新的進程啟動完成。

后邊發(fā)送的過程和動態(tài)注冊的廣播mParallelBroadcasts列表處理邏輯一致吴藻,不再詳細介紹瞒爬。由于mOrderedBroadcasts列表中的廣播是一個處理完成在處理另一個,那我們看下發(fā)送廣播給receiver后沟堡,如何處理的侧但。

在前面分析過,Args的run方法中最終調(diào)用了BroadcastReceiver的onReceive方法航罗,在調(diào)用完receiver方法后會調(diào)用AMS的finishReceiver方法禀横。其實不僅僅是這個地方調(diào)用了finishReceiver方法,在發(fā)送失敗等情況下也會調(diào)用伤哺。

IActivityManager mgr = ActivityManagerNative.getDefault();
mgr.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());

AMS的finishReceiver方法中則會繼續(xù)處理后續(xù)的廣播接收者的發(fā)送或者調(diào)用processNextBroadcast方法燕侠,繼續(xù)執(zhí)行下一個廣播的發(fā)送者祖。

到此為止廣播的發(fā)送過程也就簡單的分析完了。從分析過程中可以看出AMS服務(wù)具體是如何管理Broadcast廣播绢彤。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末七问,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子茫舶,更是在濱河造成了極大的恐慌械巡,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件饶氏,死亡現(xiàn)場離奇詭異讥耗,居然都是意外死亡,警方通過查閱死者的電腦和手機疹启,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門古程,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人喊崖,你說我怎么就攤上這事挣磨。” “怎么了荤懂?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵茁裙,是天一觀的道長。 經(jīng)常有香客問我节仿,道長晤锥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任廊宪,我火速辦了婚禮矾瘾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘箭启。我一直安慰自己霜威,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布册烈。 她就那樣靜靜地躺著,像睡著了一般婿禽。 火紅的嫁衣襯著肌膚如雪赏僧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天扭倾,我揣著相機與錄音淀零,去河邊找鬼。 笑死膛壹,一個胖子當(dāng)著我的面吹牛驾中,可吹牛的內(nèi)容都是我干的唉堪。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼肩民,長吁一口氣:“原來是場噩夢啊……” “哼唠亚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起持痰,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤灶搜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后工窍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體割卖,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年患雏,在試婚紗的時候發(fā)現(xiàn)自己被綠了鹏溯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡丙挽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出攻人,到底是詐尸還是另有隱情取试,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布怀吻,位于F島的核電站瞬浓,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蓬坡。R本人自食惡果不足惜猿棉,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望屑咳。 院中可真熱鬧萨赁,春花似錦、人聲如沸兆龙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽紫皇。三九已至慰安,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間聪铺,已是汗流浹背化焕。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留铃剔,地道東北人撒桨。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓查刻,卻偏偏與公主長得像,于是被迫代替她去往敵國和親凤类。 傳聞我的和親對象是個殘疾皇子穗泵,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 1、梅縣碧桂園趕工期踱蠢,兩個工程公司在做火欧,4天一層,二期十多棟茎截,主體今年6月份完工苇侵,估計年底交不了房,還要做環(huán)境企锌、裝修
    fa4a50c8ddcd閱讀 192評論 0 0
  • 失落 【原創(chuàng)詩第11首】 文\戀風(fēng) 失落 莫名的一波波 如潮水 漫過心的沙灘 留下 滿灘回憶的斑斕貝殼 那個 曾經(jīng)...
    戀風(fēng)2016閱讀 244評論 0 1
  • 雪天里……… 又在下雪 它姍姍降臨 染白一切 停駐在窗外 遍布每一個角落 我看雪 每個午后 按時來敲門的 總是寂寞...
    樹與樹的對望閱讀 366評論 5 9