深刻Android系統(十一)AMS-4-廣播管理

BroadcastReceiver 管理

Broadcast實質上是提供了一種更靈活的使用Intent的方式。java

不管是啓動Activity,仍是Service,其實都是經過Intent的發送和過濾來調用應用中的功能,可是ActivityServiceContentProvider所完成的功能是固定的android

而對於BroadcastReceiver能夠作任意的事情,同時廣播的發送和接收方式也更加豐富靈活shell

理解BroadcastReceiver

BroadcastReceiver是一種很簡單的組件,甚至在ActivityThread中都沒有管理它的數據結構。數組

BroadcastReceiver的本質就是經過Intent來執行應用中的一個方法,在應用中並不須要一個長期存在的對象安全

雖然應用中沒有專門的數據結構來管理BroadcastReceiver,可是在AMS中仍是須要的,由於BroadcastReceiver能夠在運行時動態向AMS註冊,AMS中須要有數據結構來管理動態接收者markdown

BroadcastReceiver能夠經過兩種方式接收廣播數據結構

  • 經過AndroidManifest中的<receiver/>標籤註冊
  • 經過AMSregisterReceiver()接口註冊

關於廣播使用上的知識你們能夠在官網學習:廣播基礎知識app

咱們看下BroadcastReceiver類相關的重要定義:async

public abstract class BroadcastReceiver {
    private PendingResult mPendingResult;
    public abstract void onReceive(Context context, Intent intent);
    public final PendingResult goAsync() {
        PendingResult res = mPendingResult;
        mPendingResult = null;
        return res;
    }
}
複製代碼

實現一個廣播接收器只須要繼承BroadcastReceiver,而後實現它的抽象方法onReceive()就能夠了。這也是最簡單的廣播的用法。ide

不過從BroadcastReceiver的類定義中還有一個mPendingResult變量和goAsync()方法,它們有什麼做用呢?
它們主要是針對當接收到廣播後須要作一些耗時操做,並且還有可能須要返回處理結果的狀況

  • 咱們知道BroadcastReceiver對象的onReceive()若是長時間不返回,會引起ANR
  • 所以若是要執行耗時的操做,必須在其餘線程中完成

mPendingResult變量的存在就是爲了上述需求。而goAsync()方法提供了獲取mPendingResult變量的接口。

goAsync()方法的實現能夠看到

  • 方法在將mPendingResult對象的指針返回後。同時也會將mPendingResult的值置爲null

按照官方說法:

一旦執行了goAsync()方法,在onReceive()結束前,須要將mPendingResult對象的指針傳遞到新線程中去,在新線程處理完成後必須調用mPendingResult對象的finish()方法來通知AMS

咱們看下PendingResultfinish()方法:

public final void finish() {
        if (mType == TYPE_COMPONENT) {
            final IActivityManager mgr = ActivityManager.getService();
            if (QueuedWork.hasPendingWork()) {
                QueuedWork.queue(new Runnable() {
                    @Override public void run() {
                        if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                                "Finishing broadcast after work to component " + mToken);
                        sendFinished(mgr);
                    }
                }, false);
            } else {
                if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                        "Finishing broadcast to component " + mToken);
                sendFinished(mgr);
            }
        } else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
            if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                    "Finishing broadcast to " + mToken);
            final IActivityManager mgr = ActivityManager.getService();
            sendFinished(mgr);
        }
    }
    public void sendFinished(IActivityManager am) {
        synchronized (this) {
            ...
            try {
                ...
                if (mOrderedHint) {
                    am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,
                            mAbortBroadcast, mFlags);
                } else {
                    am.finishReceiver(mToken, 0, null, null, false, mFlags);
                }
            } catch (RemoteException ex) {
            }
        }
    }
複製代碼

核心操做就是調用AMSfinishReceiver()方法,這纔是當前廣播接收器的結束的標誌。

正常狀況下(沒有執行過goAsync())當onReceive()調用完成後,AMS檢測到mPendingResult不爲空,便會自動執行finish()方法,而對於執行過goAsync()的廣播接收器,AMS便不會主動執行finish()

這個finish()方法很重要,本章最後也有它的身影

廣播的分類

從廣播的發送方式來劃分,有4類廣播。

  • 普通廣播:經過Context中的方法sendBroadcast()sendBroadcastAsUser()發送的廣播屬於普通廣播。
    • 普通廣播的特色是發送給系統當前的全部註冊的接收者
    • 接收者接收到廣播的順序也是不肯定的
  • 有序廣播:經過Context中的方法sendOrderedBroadcast()sendOrderedBroadcastAsUser()發送的廣播屬於有序廣播。
    • 有序廣播的發送順序是按照接收者的優先級來決定的,系統默認範圍是-1000-1000,但實際上沒有明確的數值限定,能夠到int最大值
    • 接收器的優先級經過匹配的intent-filterandroid:priority屬性來控制
    • 具備相同優先級的接收器將按隨機順序運行(這部分書中和官網的描述並不一致,看完源碼再說啦)
    • 當接收器逐個順序執行時,接收器能夠向下傳遞結果,也能夠徹底停止廣播,使其再也不傳遞給其餘接收器
  • 粘性廣播:經過Context中的方法sendStickyBroadcast()sendOrderedBroadcastAsUser()發送的廣播屬於粘性廣播。
    • 和前兩個廣播不一樣,粘性廣播能將廣播發送給系統中之後註冊的接收者,甚至是新安裝應用中的接收者
    • 官網解釋出於安全因素,在Android 9上,這兩個接口被標記爲了Deprecated
    • 不過這並不影響它在Framework中的使用,咱們看個官網的例子:
    IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
        // 對於一個粘性廣播來講,註冊監聽時即可以獲得當前電池狀態的 Intent
        Intent batteryStatus = context.registerReceiver(null, ifilter);
    複製代碼
  • 本地廣播:經過LocalBroadcastManager.sendBroadcast發送的廣播屬於本地廣播
    • 本地廣播會將廣播發送給與發送者位於同一應用中的接收者
    • 當不須要跨應用發送廣播時,建議使用本地廣播
    • 這種實現方法的效率更高(無需進行進程間通訊)
    • 並且無需擔憂其餘應用在收發廣播時帶來的任何安全問題

廣播的數據結構

AMS中,全部註冊的廣播接收者都放在成員變量mRegisteredReceivers中,定義以下:

final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();
複製代碼

mRegisteredReceivers是一個HashMap,其中

  • key爲接收者的IBinder對象

  • value爲接收者的對象ReceiverList

    • 由於一個接收者中可能包含多個IntentFilter,因此ReceiverList是一個數組
    final class ReceiverList extends ArrayList<BroadcastFilter> 複製代碼
    • BroadcastFilter則是繼承了IntentFilter,定義以下:
    final class BroadcastFilter extends IntentFilter {
        final ReceiverList receiverList; // 所屬 receiver 的引用
        final String packageName;        // 所在應用的包名
        final String requiredPermission; // 發送廣播時須要聲明的權限字符串
        final int owningUid;             // 所在應用的 uid
        final int owningUserId;          // 所在應用的 userid
        ...
    }
    複製代碼

ReceiverList的關鍵定義以下:

final class ReceiverList extends ArrayList<BroadcastFilter> implements IBinder.DeathRecipient {
    final ActivityManagerService owner;
    public final IIntentReceiver receiver; // 用戶進程中定義的 IntentReceiver
    public final ProcessRecord app;        // 所屬用戶進程的 ProcessRecord 對象
    public final int pid;                  // 所屬用戶進程的pid
    public final int uid;                  // 所屬用戶進程的uid
    public final int userId;               // 用戶id
    BroadcastRecord curBroadcast = null;   //
    boolean linkedToDeath = false;         // 是否註冊了 binder 死亡監聽
}
複製代碼

發送廣播時,AMS中收到的廣播消息首先會保存在mBroadcastQueues對象中,而後再發送給用戶進程中的接收者。mBroadcastQueues是一個只有兩個元素的數組,定義以下:

final BroadcastQueue[] mBroadcastQueues = new BroadcastQueue[2];
複製代碼

此外,AMS中還定義了兩個變量:

BroadcastQueue mFgBroadcastQueue;
    BroadcastQueue mBgBroadcastQueue;
複製代碼

AMS在構造方法中將這兩個變量和mBroadcastQueues集合進行了關聯:

public ActivityManagerService(Context systemContext) {
        ...
        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;
        ...
    }
複製代碼
  • mFgBroadcastQueue用來保存帶有FLAG_RECEIVER_FOREGROUND標誌的廣播,它要求接收者進程以foreground的優先級運行,這樣執行的更快。
  • 若是不特別指定,通常的廣播都會保存在mBgBroadcastQueue

咱們看下BroadcastQueue類的主要結構:

public final class BroadcastQueue {
    final ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<>();
    final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<>();
}
複製代碼
  • mParallelBroadcasts用來保存全部的普通廣播
  • mOrderedBroadcasts用來保存全部的有序廣播

粘性廣播在哪裏呢?
系統中全部的粘性廣播都保存在AMS的成員變量mStickyBroadcasts中,定義以下:

final SparseArray<ArrayMap<String, ArrayList<Intent>>> mStickyBroadcasts =
            new SparseArray<ArrayMap<String, ArrayList<Intent>>>();
複製代碼
  • mStickyBroadcasts是一個SparseArray類型的數組,使用用戶Id做爲索引,保存的是ArrayMap對象
  • ArrayMap對象儲存的是某個用戶發送的全部的粘性廣播,每條記錄以Intentaction字符串做爲索引,保存的內容是一個ArrayList對象
  • ArrayList對象其中儲存了包含該actionIntent

廣播的註冊過程

動態註冊是經過接口registerReceiver()完成的。應用註冊receiver時,並非直接調用AMS的接口來完成的,而是經過Context類中的方法,由於AMS的接口須要提供應用的ApplicationThread類的IBinder對象來做爲參數,應用中獲得這個對象比較麻煩。

Context中的registerReceiver()最終調用的是ContextImplregisterReceiverInternal()方法,代碼以下:

class ContextImpl{
    private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId, IntentFilter filter, String broadcastPermission, Handler scheduler, Context context, int flags) {
        ...
        try {
            final Intent intent = ActivityManager.getService().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
                    broadcastPermission, userId, flags);
            if (intent != null) {
                intent.setExtrasClassLoader(getClassLoader());
                intent.prepareToEnterProcess();
            }
            return intent;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
}
複製代碼

registerReceiverInternal()方法最終調用的是AMSregisterReceiver()方法,代碼以下:

public Intent registerReceiver(IApplicationThread caller, String callerPackage, IIntentReceiver receiver, IntentFilter filter, String permission, int userId, int flags) {
        // 確保不是 isolated 進程
        enforceNotIsolatedCaller("registerReceiver");
        ArrayList<Intent> stickyIntents = null;
        ProcessRecord callerApp = null;
        final boolean visibleToInstantApps
                = (flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0;
        int callingUid;
        int callingPid;
        boolean instantApp;
        synchronized(this) {
            ...
            // 從註冊信息中獲取 action 信息
            Iterator<String> actions = filter.actionsIterator();
            if (actions == null) {
                ArrayList<String> noAction = new ArrayList<String>(1);
                noAction.add(null);
                actions = noAction.iterator();
            }
            // 獲取 user ID
            int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
            // 循環遍歷 action
            while (actions.hasNext()) {
                String action = actions.next();
                // 遍歷每一個userID下的粘性廣播Map
                for (int id : userIds) {
                    // 獲取特定userID下的粘性廣播Map
                    ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);
                    if (stickies != null) {
                        // 從Map中查找集合中符合當前action信息的Intent集合
                        ArrayList<Intent> intents = stickies.get(action);
                        if (intents != null) {
                            if (stickyIntents == null) {
                                stickyIntents = new ArrayList<Intent>();
                            }
                            // 添加到 stickyIntents 集合中
                            stickyIntents.addAll(intents);
                        }
                    }
                }
            }
        }
        ArrayList<Intent> allSticky = null;
        if (stickyIntents != null) {
            // 粘性廣播不爲空
            final ContentResolver resolver = mContext.getContentResolver();
            // 查找全部符合符合當前請求的Intent
            for (int i = 0, N = stickyIntents.size(); i < N; i++) {
                Intent intent = stickyIntents.get(i);
                ...
                if (filter.match(resolver, intent, true, TAG) >= 0) {
                    if (allSticky == null) {
                        allSticky = new ArrayList<Intent>();
                    }
                    allSticky.add(intent);
                }
            }
        }
        // The first sticky in the list is returned directly back to the client.
        Intent sticky = allSticky != null ? allSticky.get(0) : null;
        // 若是註冊時傳入的 receiver 爲空
        // 此處直接返回 sticky 對象
        if (receiver == null) {
            return sticky;
        }
        synchronized (this) {
            if (callerApp != null && (callerApp.thread == null
                    || callerApp.thread.asBinder() != caller.asBinder())) {
                // Original caller already died
                return null;
            }
            // 檢查註冊的 receiver 是否已經註冊過了
            ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
            if (rl == null) {
                // rl爲空說明沒有註冊過
                // 新建對象
                rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                        userId, receiver);
                if (rl.app != null) {
                    // 若是指定了進程對象,則把receiver保存在進程對象中
                    // 方便進程銷燬時能及時釋放該對象
                    final int totalReceiversForApp = rl.app.receivers.size();
                    if (totalReceiversForApp >= MAX_RECEIVERS_ALLOWED_PER_APP) {
                        throw new IllegalStateException(...);
                    }
                    rl.app.receivers.add(rl);
                } else {
                    // 若是沒有指定進程對象,則註冊receiver的死亡通知
                    try {
                        receiver.asBinder().linkToDeath(rl, 0);
                    } catch (RemoteException e) {
                        return sticky;
                    }
                    rl.linkedToDeath = true;
                }
                mRegisteredReceivers.put(receiver.asBinder(), rl);// 保存新建的 rl
            }
            ...
            // 下面主要是針對 receiver 重複註冊的狀況
            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                    permission, callingUid, userId, instantApp, visibleToInstantApps);
            if (rl.containsFilter(filter)) {
                // 已經包含了本次提交filter,簡單打印,不作其餘處理
                Slog.w(TAG, "Receiver with filter " + filter
                        + " already registered for pid " + rl.pid
                        + ", callerPackage is " + callerPackage);
            } else {
                // 未包含本次提交的filter,添加到集合中
                rl.add(bf);
                if (!bf.debugCheck()) {
                    Slog.w(TAG, "==> For Dynamic broadcast");
                }
                mReceiverResolver.addFilter(bf);
            }
            // 繼續處理粘性廣播部分
            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 中的標誌獲取發送隊列(bg仍是fg)
                    BroadcastQueue queue = broadcastQueueForIntent(intent);
                    // 建立新的廣播對象
                    BroadcastRecord r = new BroadcastRecord(queue, intent, null,
                            null, -1, -1, false, null, null, OP_NONE, null, receivers,
                            null, 0, null, null, false, true, true, -1);
                    // 將廣播加入隊列
                    queue.enqueueParallelBroadcastLocked(r);
                    // 發送 Intent 到指定進程
                    queue.scheduleBroadcastsLocked();
                }
            }
            return sticky;
        }
    }
複製代碼

registerReceiver()方法看上去比較複雜,其實不少都是針對粘性(sticky)廣播的:

  • 若是沒有處理粘性(sticky)廣播,只須要檢查receiver是否已經註冊

    • 沒有註冊過,就能夠建立用來保存receiver的對象ReceiverList,並把它添加到mRegisteredReceivers集合中
    • 註冊過的receiver能夠重複註冊,只要pid、``uiduserid相同就能夠
      • 重複註冊用於向某個receiver增長新的IntentFilter
  • 而對於粘性(sticky)廣播

    • 若是調用registerReceiver()時參數receivernull,則馬上返回找到的第一個粘性廣播的Intent
    • 不然,將查找系統中全部和註冊時傳入的Intent匹配的粘性廣播,並經過BroadcastQueue來發送

咱們再來看下廣播的發送過程

廣播的發送過程

應用發送廣播調用的是Context類中的方法,發送廣播的方法雖然不少,最後都是調用AMSbroadcastIntent()方法。

broadcastIntent()方法簡單檢查了調用者的權限後,轉調內部方法broadcastIntentLocked()來完成廣播發送。

broadcastIntentLocked()方法會

  • 先檢查要廣播的Intent

    • 若是是一些系統Intent,則調用相應的方法處理
    • 若是是粘性廣播,則把廣播的Intent加入AMS的粘性廣播列表中
  • 最後查找全部的接收者,逐個調用它們

廣播發送的時序圖以下:
image

AMS.broadcastIntentLocked

咱們看下具體實現:

final int broadcastIntentLocked(...) {
        intent = new Intent(intent);
        ...
        userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
                ALLOW_NON_FULL, "broadcast", callerPackage);
        // 檢查發送廣播的用戶是否正在運行
        if (userId != UserHandle.USER_ALL && !mUserController.isUserOrItsParentRunning(userId)) {
            if ((callingUid != SYSTEM_UID
                    || (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0)
                    && !Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {
                return ActivityManager.BROADCAST_FAILED_USER_STOPPED;
            }
        }
        final String action = intent.getAction();
        ...
        // 檢查廣播是否爲 protected
        final boolean isProtectedBroadcast;
        try {
            isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);
        } catch (RemoteException e) {
            Slog.w(TAG, "Remote exception", e);
            return ActivityManager.BROADCAST_SUCCESS;
        }
        // 確認發送廣播的進程是否爲 system
        final boolean isCallerSystem;
        switch (UserHandle.getAppId(callingUid)) {
            case ROOT_UID:
            case SYSTEM_UID:
            case PHONE_UID:
            case BLUETOOTH_UID:
            case NFC_UID:
            case SE_UID:
                isCallerSystem = true;
                break;
            default:
                isCallerSystem = (callerApp != null) && callerApp.persistent;
                break;
        }
        if (!isCallerSystem) {
            if (isProtectedBroadcast) {
                // 不是system進程,可是發送的廣播倒是 protected 的狀況
                // 拋出安全異常
                ...
                throw new SecurityException(msg);
            } else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
                    || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
                // 不是 system 進程,也不是 protected 廣播
                // 可是,有一些特殊的廣播仍是不能發送,這裏作一些限制
                if (callerPackage == null) {
                    ...
                    throw new SecurityException(msg);
                } else if (intent.getComponent() != null) {
                    if (!intent.getComponent().getPackageName().equals(
                            callerPackage)) {
                        String msg = "Permission Denial: not allowed to send broadcast "
                                + action + " to "
                                + intent.getComponent().getPackageName() + " from "
                                + callerPackage;
                        ...
                        throw new SecurityException(msg);
                    }
                } else {
                    // Limit broadcast to their own package.
                    intent.setPackage(callerPackage);
                }
            }
        }
        if (action != null) {
            // 從 SystemConfig 中查詢當前 action 是否在 allow-implicit-broadcast 標籤中
            // 就是查查當前 action 是否支持隱式廣播
            if (getBackgroundLaunchBroadcasts().contains(action)) {
                ...
                // 若是是,增長一個Flag標記
                // 這個標記就代表這個廣播能夠做爲隱式廣播發送
                intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
            }
            switch (action) {
                // 檢查處理一些特殊的 action
                // 這部分主要是檢查和 PMS 有關的一些操做
                case Intent.ACTION_UID_REMOVED:
                case Intent.ACTION_PACKAGE_REMOVED:
                case Intent.ACTION_PACKAGE_CHANGED:
                ...
                    // 若是這個廣播時從 PMS 來的,並且是關於應用刪除或應用變化
                    // 須要從 task 中移除全部和該應用管理的 Activity 
                    break;
                ...
                case "com.android.launcher.action.INSTALL_SHORTCUT":
                    // As of O, we no longer support this broadcasts, even for pre-O apps.
                    // Apps should now be using ShortcutManager.pinRequestShortcut().
                    Log.w(TAG, "Broadcast " + action
                            + " no longer supported. It will not be delivered.");
                    return ActivityManager.BROADCAST_SUCCESS;
            }
            ...
        }
        // 對於粘性廣播
        if (sticky) {
            // 檢查廣播發送方是否聲明瞭 android.permission.BROADCAST_STICKY 權限
            if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,
                    callingPid, callingUid)
                    != PackageManager.PERMISSION_GRANTED) {
                ...
                throw new SecurityException(msg);
            }
            if (requiredPermissions != null && requiredPermissions.length > 0) {
                // 發送粘性廣播不能指定 permission
                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) {
                // 若是廣播不是發給全部用戶,先檢查是否存在一個發給全部用戶的相同的廣播
                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");
                            }
                        }
                    }
                }
            }
            // 將廣播保存到 mStickyBroadcasts 中
            ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
            if (stickies == null) {
                stickies = new ArrayMap<>();
                mStickyBroadcasts.put(userId, stickies);
            }
            // 添加 Intent 信息列表
            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 已經存在,覆蓋
                    list.set(i, new Intent(intent));
                    break;
                }
            }
            if (i >= stickiesCount) {
                list.add(new Intent(intent));
            }
        }
        int[] users;
        if (userId == UserHandle.USER_ALL) {
            // 廣播給全部用戶的狀況,獲取全部的用戶id
            users = mUserController.getStartedUserArray();
        } else {
            // 廣播給特定用戶
            users = new int[] {userId};
        }
        // 計算全部接收該 Intent 的 receiver
        List receivers = null;
        List<BroadcastFilter> registeredReceivers = null;
        if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
            // 若是 Intent 中沒有指定 FLAG_RECEIVER_REGISTERED_ONLY 
            // 收集靜態接收者
            receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
        }
        if (intent.getComponent() == null) {
            // 若是沒有指定 Component,則查找匹配該 Intent 的全部 receiver
            if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
                // 針對發送給全部用戶的廣播,而且發送廣播的 user 是 shell
                for (int i = 0; i < users.length; i++) {
                    // 遍歷 userID 
                    ...
                    // 從 mReceiverResolver 中查找,動態註冊的廣播接收者都在這裏
                    List<BroadcastFilter> registeredReceiversForUser = mReceiverResolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, users[i]);
                    if (registeredReceivers == null) {
                        registeredReceivers = registeredReceiversForUser;
                    } else if (registeredReceiversForUser != null) {
                        registeredReceivers.addAll(registeredReceiversForUser);
                    }
                }
            } else {
                registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId);
            }
        }
        final boolean replacePending =
                (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
        int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
        if (!ordered && NR > 0) {
            // 若是不是有序廣播,也就是普通廣播的狀況
            if (isCallerSystem) {
                checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
                        isProtectedBroadcast, registeredReceivers);
            }
            // 獲取發送隊列
            final BroadcastQueue queue = broadcastQueueForIntent(intent);
            // 建立 BroadcastRecord 對象
            BroadcastRecord r = new BroadcastRecord(...);
            ...
            if (!replaced) {
                queue.enqueueParallelBroadcastLocked(r); // 加入到並行隊列中
                queue.scheduleBroadcastsLocked(); // 發送廣播
            }
            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) {
                    // 優先把動態廣播接收者插入到靜態廣播接收者
                    // 同優先級下,動態接受者會被插入到靜態接收者後面
                    receivers.add(it, curr);
                    ir++;
                    curr = null;
                    it++;
                    NT++;
                } else {
                    // Skip to the next ResolveInfo in the final list.
                    it++;
                    curt = null;
                }
            }
        }
        // 在隊列中加入剩下的動態接受者
        while (ir < NR) {
            if (receivers == null) {
                receivers = new ArrayList();
            }
            receivers.add(registeredReceivers.get(ir));
            ir++;
        }
        ...
        if ((receivers != null && receivers.size() > 0) || resultTo != null) {
            // 獲取廣播發送隊列
            BroadcastQueue queue = broadcastQueueForIntent(intent);
            // 建立廣播對象
            BroadcastRecord r = new BroadcastRecord(...);
            ...
            final BroadcastRecord oldRecord =
                    replacePending ? queue.replaceOrderedBroadcastLocked(r) : null;
            if (oldRecord != null) {
                // 若是存在舊的廣播,調用 performReceiveLocked 執行廣播
                if (oldRecord.resultTo != null) {
                    final BroadcastQueue oldQueue = broadcastQueueForIntent(oldRecord.intent);
                    try {
                        // 這個方法就會調用到 onReceived()
                        oldQueue.performReceiveLocked(...);
                    } catch (RemoteException e) {
                        ...
                    }
                }
            } else {
                queue.enqueueOrderedBroadcastLocked(r); // 將廣播加入到order隊列
                queue.scheduleBroadcastsLocked();       // 發送廣播
            }
        } 
        ...
        return ActivityManager.BROADCAST_SUCCESS;
    }
複製代碼

broadcastIntentLocked()方法比較長,咱們再簡單梳理下:

  • 首先是檢查發送的廣播是否爲一些特殊的廣播
    • 尤爲是從PMS中發出的有關應用安裝變化相關的廣播
  • 接下來是判斷是否爲粘性廣播,若是是
    • 先是檢查一些相關的權限,好比粘性廣播不能帶有permission,並且Intent必須指定Component
    • 而後建立對應的數據結構,並添加到mStickyBroadcasts集合中
  • 而後查找該廣播對應的廣播接收者
    • 若是是普通廣播,會優先發給動態接收者
    • 若是是有序廣播,會將動態接收者和靜態接收者一塊兒根據優先級排序,而後再建立BroadcastRecord對象並添加到有序廣播隊列中
  • 最後,經過scheduleBroadcastsLocked()發送廣播

從上面的代碼實現上能夠看到,不管哪一種廣播,靜態接收者之間必定會排序,並且相同優先級下,靜態接收者會排在動態接收者以前。

爲何靜態接收者必定要放到有序隊列呢?
對於靜態接收者來講,它所屬的進程可能已經在運行,也可能沒有。若是進程沒有運行,就須要先啓動它。首先進程的啓動是一個耗時過程,並且啓動有可能失敗,這個過程只能逐一處理,不能簡單的羣發消息。

到這裏,廣播接收者和廣播內容都已經檢查準備好了,咱們繼續學習scheduleBroadcastsLocked()廣播發送的過程

BroadcastQueue.scheduleBroadcastsLocked

scheduleBroadcastsLocked()方法只是發送了一個BROADCAST_INTENT_MSG消息,代碼以下:

public void scheduleBroadcastsLocked() {
        ...
        mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
        mBroadcastsScheduled = true;
    }
複製代碼

BROADCAST_INTENT_MSG消息的處理是調用processNextBroadcast()方法

case BROADCAST_INTENT_MSG: {
        ...
        processNextBroadcast(true);
    } break;
    
    final void processNextBroadcast(boolean fromMsg) {
        synchronized (mService) {
            processNextBroadcastLocked(fromMsg, false);
        }
    }
複製代碼

調用到了processNextBroadcastLocked()方法。這個方法比較長,咱們簡要分析:

final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
    BroadcastRecord r;
    ...
    // 循環處理 mParallelBroadcasts 集合中的消息
    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);
            ...
            // 調用通知 receiver
            deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
        }
        addBroadcastToHistoryLocked(r);
    }
    // 接下來處理 mPendingBroadcast 集合
    // 這個集合存放的是等待進程啓動的廣播
    if (mPendingBroadcast != null) {
        ...
        boolean isDead;
        if (mPendingBroadcast.curApp.pid > 0) {
            synchronized (mService.mPidsSelfLocked) {
                ProcessRecord proc = mService.mPidsSelfLocked.get(
                        mPendingBroadcast.curApp.pid);
                isDead = proc == null || proc.crashing;
            }
        } else {
            final ProcessRecord proc = mService.mProcessNames.get(
                    mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);
            isDead = proc == null || !proc.pendingStart;
        }
        if (!isDead) {
            // It's still alive, so keep waiting
            return;
        } else {
            Slog.w(TAG, "pending app ["
                    + mQueueName + "]" + mPendingBroadcast.curApp
                    + " died before responding to broadcast");
            mPendingBroadcast.state = BroadcastRecord.IDLE;
            mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
            mPendingBroadcast = null;
        }
    }
    // 處理 mOrderedBroadcasts 中的廣播
    boolean looped = false;
    do {
        ...
        r = mOrderedBroadcasts.get(0);
        boolean forceReceive = false;
        ...
        int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
        if (mService.mProcessesReady && r.dispatchTime > 0) {
            long now = SystemClock.uptimeMillis();
            // forground 的 mTimeoutPeriod 超時是 10*1000
            // bgground 的 mTimeoutPeriod 超時是 60*1000
            if ((numReceivers > 0) && (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
                // 若是超時則終止廣播
                ...
                broadcastTimeoutLocked(false); // forcibly finish this broadcast
                forceReceive = true;
                r.state = BroadcastRecord.IDLE;
            }
        }
        ...
        if (r.receivers == null || r.nextReceiver >= numReceivers
                || r.resultAbort || forceReceive) {
            // 沒有多餘的 receiver 了
            if (r.resultTo != null) {
                try {
                    ...
                    // 把廣播結果傳遞給發送廣播的進程
                    performReceiveLocked(r.callerApp, r.resultTo,
                        new Intent(r.intent), r.resultCode,
                        r.resultData, r.resultExtras, false, false, r.userId);
                    r.resultTo = null;
                } catch (RemoteException e) {
                    r.resultTo = null;
                    ...

                }
            }
            ...
        }
    } while (r == null);
    ...
    if (nextReceiver instanceof BroadcastFilter) {
        BroadcastFilter filter = (BroadcastFilter)nextReceiver;
        ...
        // 發送給接收者進程
        deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
        if (r.receiver == null || !r.ordered) {
            ...
            // 已經處理完成的狀況
            r.state = BroadcastRecord.IDLE;
            // 處理下一條廣播
            scheduleBroadcastsLocked();
        } else {
            if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
                scheduleTempWhitelistLocked(filter.owningUid,
                        brOptions.getTemporaryAppWhitelistDuration(), r);
            }
        }
        return;
    }
    ...
    ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
            info.activityInfo.applicationInfo.uid, false);
    ...
    if (app != null && app.thread != null && !app.killed) {
        // 若是是靜態接收者,而且接收者對應的進程已經啓動的狀況
        try {
            app.addPackage(info.activityInfo.packageName,
                    info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
            // 調用 processCurBroadcastLocked 通知 receiver
            processCurBroadcastLocked(r, app, skipOomAdj);
            // 直接返回
            return;
        } 
        ...
    }
    ...
    // 走到這裏說明進程沒有啓動,先調用 startProcessLocked 啓動進程
    if ((r.curApp=mService.startProcessLocked(...)) == null) {
        ....
        // 若是進程啓動失敗,處理下一條廣播
        scheduleBroadcastsLocked();
        r.state = BroadcastRecord.IDLE;
        return;
    }
    mPendingBroadcast = r;
    mPendingBroadcastRecvIndex = recIdx;
}
複製代碼

processNextBroadcastLocked()方法也只是對要發送的廣播接收者的集合進行的遍歷發送處理,真正通知到應用的部分是在deliverToRegisteredReceiverLocked()方法中,咱們具體看下。

BroadcastQueue.deliverToRegisteredReceiverLocked

方法以下:

private void deliverToRegisteredReceiverLocked(BroadcastRecord r, BroadcastFilter filter, boolean ordered, int index) {
        ...
        performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
                        new Intent(r.intent), r.resultCode, r.resultData,
                        r.resultExtras, r.ordered, r.initialSticky, r.userId);
        ...
    }
    void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver, Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
        // Send the intent to the receiver asynchronously using one-way binder calls.
        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);
        }
    }
複製代碼

咱們已經在performReceiveLocked方法中明顯看到app.thread字樣了,這也就意味着開始去調用應用進程中的接口了,也就是ActivityThread中的scheduleRegisteredReceiver()接口,以下:

public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent, int resultCode, String dataStr, Bundle extras, boolean ordered, boolean sticky, int sendingUser, int processState) throws RemoteException {
        ...
        receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
                sticky, sendingUser);
    }
複製代碼

ActivityThread調用的是IIntentReceiverperformReceive()方法

IIntentReceiver是一個AIDL,跟蹤實現是在LoadedApk中,具體的裝配過程是在ContextImpl初始化時完成的

咱們看下在LoadedApk中的關鍵定義

LoadedApk.performReceive

代碼以下:

class LoadedApk{
    static final class ReceiverDispatcher {
        final static class InnerReceiver extends IIntentReceiver.Stub {
            ...
            @Override
            public void performReceive(...) {
                final LoadedApk.ReceiverDispatcher rd;
                ...
                // 調用內部的 performReceive() 方法
                rd.performReceive(...);
                ...
            }
        }
        ...
        public void performReceive(...) {
            final Args args = new Args(...);
            ...
            // 此處重點是執行經過 Args.getRunnable() 獲取的 Runnable 對象
            // Runnable 對象中執行了 onReceive() 方法回調
            if (intent == null || !mActivityThread.post(args.getRunnable())) {
                if (mRegistered && ordered) {
                    IActivityManager mgr = ActivityManager.getService();
                    ...
                    args.sendFinished(mgr);
                }
            }
        }
        final class Args extends BroadcastReceiver.PendingResult {
            ...
            public final Runnable getRunnable() {
                return () -> {
                    final BroadcastReceiver receiver = mReceiver;
                    ...
                    try {
                        ...
                        // onReceive 回調
                        receiver.onReceive(mContext, intent);
                    }
                    ...
                    if (receiver.getPendingResult() != null) {
                        // mPendingResult 不爲空,自動執行 finish()
                        finish();
                    }
                };
            }
        }
    }
}
複製代碼

這樣一個廣播的發送就結束了。。。

AMS的章節終因而磕磕絆絆的學完了,不少細節沒有掌握,好在總體流程上暫時還留有印象。留此筆記方便複習

下一篇學習Android的圖形顯示系統,加油💪

相關文章
相關標籤/搜索