中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

ActivityManagerService廣播怎么注冊與發送

發布時間:2023-03-02 11:32:15 來源:億速云 閱讀:128 作者:iii 欄目:開發技術

今天小編給大家分享一下ActivityManagerService廣播怎么注冊與發送的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

注冊廣播接收器

廣播接收器可以分為動態和靜態,靜態廣播接收器就是在 AndroidManifest.xml 中注冊的,而動態的廣播接收器是在代碼中通過 Context#registerReceiver() 注冊的。

靜態廣播接收器,在發送廣播時,服務端會從 PKMS 中收集,而動態的廣播接收器,需要接收方發送給服務端。因此,下面只分析動態廣播接收器的注冊過程

// ContextImpl.java
public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
        IntentFilter filter, String broadcastPermission, Handler scheduler) {
    return registerReceiverInternal(receiver, user.getIdentifier(),
            filter, broadcastPermission, scheduler, getOuterContext(), 0);
}
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
        IntentFilter filter, String broadcastPermission,
        Handler scheduler, Context context, int flags) {
    IIntentReceiver rd = null;
    if (receiver != null) {
        if (mPackageInfo != null && context != null) {
            // 默認主線程 Handler
            if (scheduler == null) {
                scheduler = mMainThread.getHandler();
            }
            // 1. 獲取 IIntentReceiver 對象
            // 其實這里獲取的就是一個 Binder 對象,用于注冊給 AMS,從而接收廣播信息的回調
            rd = mPackageInfo.getReceiverDispatcher(
                receiver, context, scheduler,
                mMainThread.getInstrumentation(), true);
        } else {
            // ...
        }
    }
    try {
        // 2. 向 AMS 注冊 IIntentReceiver
        final Intent intent = ActivityManager.getService().registerReceiverWithFeature(
                mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(),
                AppOpsManager.toReceiverId(receiver), rd, filter, broadcastPermission, userId,
                flags);
        if (intent != null) {
            intent.setExtrasClassLoader(getClassLoader());
            intent.prepareToEnterProcess(ActivityThread.isProtectedBroadcast(intent),
                    getAttributionSource());
        }
        return intent;
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

廣播接收方注冊接收器的過程如下

  • 獲取 IIntentReceiver 對象,它是一個 Binder 對象,其實就是一個 Binder 回調。

  • 向服務端 AMS 注冊 IIntentReceiver 對象,用于接收廣播消息的回調。

當接收方收到來自服務端的廣播消息后,會通過 IIntentReceiver 對象,調用 BroadcastReceiver#onReceive() 來處理廣播。

現在來看下服務端是如何完成廣播接收器的注冊工作的

// AcitityManagerService.java
public Intent registerReceiverWithFeature(IApplicationThread caller, String callerPackage,
        String callerFeatureId, String receiverId, IIntentReceiver receiver,
        IntentFilter filter, String permission, int userId, int flags) {
    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) {
        // 確保接收方進程存在,并該進程的 uid 和 pid
        if (caller != null) {
            callerApp = getRecordForAppLOSP(caller);
            if (callerApp == null) {
                throw new SecurityException(
                        "Unable to find app for caller " + caller
                        + " (pid=" + Binder.getCallingPid()
                        + ") when registering receiver " + receiver);
            }
            if (callerApp.info.uid != SYSTEM_UID
                    && !callerApp.getPkgList().containsKey(callerPackage)
                    && !"android".equals(callerPackage)) {
                throw new SecurityException("Given caller package " + callerPackage
                        + " is not running in process " + callerApp);
            }
            callingUid = callerApp.info.uid;
            callingPid = callerApp.getPid();
        } else {
            callerPackage = null;
            callingUid = Binder.getCallingUid();
            callingPid = Binder.getCallingPid();
        }
        instantApp = isInstantApp(callerApp, callerPackage, callingUid);
        userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
                ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
        Iterator<String> actions = filter.actionsIterator();
        if (actions == null) {
            ArrayList<String> noAction = new ArrayList<String>(1);
            noAction.add(null);
            actions = noAction.iterator();
        }
        // Collect stickies of users
        int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
        // 遍歷 IntentFilter 保存的所有 action,匹配相應的 sticky 廣播,并保存到 stickyIntents
        // 從這里可以看出,可以先發送 sticky 廣播,然后再注冊 sticky 廣播接收器
        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);
                    }
                }
            }
        }
    }
    // 剛才是用 action 匹配 sticky 廣播,現在使用 IntentFilter 再次過濾
    // 過濾后的 sticky 廣播 ,保存到 allSticky
    // 因此 allSticky 保存的才是最終完美匹配到的 sticky 廣播
    ArrayList<Intent> allSticky = null;
    if (stickyIntents != null) {
        final ContentResolver resolver = mContext.getContentResolver();
        // Look for any matching sticky broadcasts...
        for (int i = 0, N = stickyIntents.size(); i < N; i++) {
            Intent intent = stickyIntents.get(i);
            // Don't provided intents that aren't available to instant apps.
            if (instantApp &&
                    (intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) == 0) {
                continue;
            }
            // If intent has scheme "content", it will need to acccess
            // provider that needs to lock mProviderMap in ActivityThread
            // and also it may need to wait application response, so we
            // cannot lock ActivityManagerService here.
            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.
    // 從這里可以看出,如果注冊的廣播接收器為 null,那么表示要獲取最近一次 sticky 廣播的數據
    Intent sticky = allSticky != null ? allSticky.get(0) : null;
    if (receiver == null) {
        return sticky;
    }
    // ...
    synchronized (this) {
        IApplicationThread thread;
        // 注意學會這里的操作,如何判斷原來的進程已經死亡
        if (callerApp != null && ((thread = callerApp.getThread()) == null
                || thread.asBinder() != caller.asBinder())) {
            // Original caller already died
            return null;
        }
        // 1. mRegisteredReceivers 建立客戶端與服務端的廣播接收器的映射
        // 客戶端注冊的廣播接收器是 IIntentReceiver, 而服務端的是 ReceiverList
        ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
        if (rl == null) {
            rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                    userId, receiver);
            if (rl.app != null) {
                final int totalReceiversForApp = rl.app.mReceivers.numberOfReceivers();
                if (totalReceiversForApp >= MAX_RECEIVERS_ALLOWED_PER_APP) {
                    throw new IllegalStateException("Too many receivers, total of "
                            + totalReceiversForApp + ", registered for pid: "
                            + rl.pid + ", callerPackage: " + callerPackage);
                }
                // ProcessRecord#mReceivers 保存 ReceiverList
                rl.app.mReceivers.addReceiver(rl);
            } else {
                // ...
            }
            mRegisteredReceivers.put(receiver.asBinder(), rl);
        } else {
            // ...
        }
        // 2. 創建服務端的廣播過濾器 BroadcastFilter,并保存到 mReceiverResolver
        BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, callerFeatureId,
                receiverId, permission, callingUid, userId, instantApp, visibleToInstantApps);
        if (rl.containsFilter(filter)) {
            Slog.w(TAG, "Receiver with filter " + filter
                    + " already registered for pid " + rl.pid
                    + ", callerPackage is " + callerPackage);
        } else {
            // ReceiverList 是 ArrayList 子類,之所以用一個列表保存 BroadcastFilter
            // 是因為在注冊廣播接收器時,可以為同一個廣播接收器匹配多個過濾器
            rl.add(bf);
            if (!bf.debugCheck()) {
                Slog.w(TAG, "==> For Dynamic broadcast");
            }
            // 解析過濾器的數據,然后用相應的數據結構保存
            mReceiverResolver.addFilter(bf);
        }
        // Enqueue broadcasts for all existing stickies that match
        // this filter.
        // 注意,這里處理的情況是,注冊的 sticky 廣播接收器不為 null
        // 那么把匹配到的 sticky 廣播,發送給這個廣播接收器
        // 是不是非常有意思,注冊 sticky 廣播接收器,就能立即收到廣播,這得益于 sticky 廣播被緩存
        if (allSticky != null) {
            // 很奇怪,BroadcastFilter 怎么是廣播接收器呢?
            ArrayList receivers = new ArrayList();
            receivers.add(bf);
            final int stickyCount = allSticky.size();
            for (int i = 0; i < stickyCount; i++) {
                Intent intent = allSticky.get(i);
                BroadcastQueue queue = broadcastQueueForIntent(intent);
                BroadcastRecord r = new BroadcastRecord(queue, intent, null,
                        null, null, -1, -1, false, null, null, null, OP_NONE, null, receivers,
                        null, 0, null, null, false, true, true, -1, false, null,
                        false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */);
                queue.enqueueParallelBroadcastLocked(r);
                queue.scheduleBroadcastsLocked();
            }
        }
        return sticky;
    }
}

服務端對于動態注冊的廣播接收器的處理過程如下

  • 使用 mRegisteredReceivers 建立客戶端與服務端的廣播接收器的映射。ReceiverList 代表服務端的廣播接收器,IIntentReceiver 代表客戶端的廣播接收器。

  • 使用客戶端的 IntentFilter , 創建服務端的廣播過濾器 BroadcastFilter,并保存到 mReceiverResolver。注意,這一步中,ReceiverList 和 BroadcastFilter 互相保存了引用。

這些數據結構都是相互關聯的,有何種用意呢?當發送方發送廣播到 AMS,AMS 會使用 mReceiverResolver 匹配 BroadcastFilter,BroadcastFilter 找到 ReceiverList,ReceiverList 找到 IIntentReceiver,IIntentReceiver 發送廣播給接收方。

另外,由于 sticky 廣播是會被緩存的,當注冊 sticky 廣播的接收器時,有以下兩種處理方式

  • 如果注冊的廣播接收器為 null,那么會返回最近的一次廣播數據給接收方。

  • 如果注冊的廣播接收器不為null,那么會把匹配到的 sticky 廣播發送給接收方的廣播接收器,也就是會調用 BroadcastReceiver#onReceive()。

發送廣播

發送廣播的方法,有多個重載方法,挑選一個最簡單的來分析,如下

// ContextImpl.java
public void sendBroadcast(Intent intent) {
    warnIfCallingFromSystemProcess();
    String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
    try {
        intent.prepareToLeaveProcess(this);
        ActivityManager.getService().broadcastIntentWithFeature(
                mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
                null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/,
                null, AppOpsManager.OP_NONE, null, false, false, getUserId());
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}
// ActivityManagerService.java
public final int broadcastIntentWithFeature(IApplicationThread caller, String callingFeatureId,
        Intent intent, String resolvedType, IIntentReceiver resultTo,
        int resultCode, String resultData, Bundle resultExtras,
        String[] requiredPermissions, String[] excludedPermissions,
        String[] excludedPackages, int appOp, Bundle bOptions,
        boolean serialized, boolean sticky, int userId) {
    enforceNotIsolatedCaller("broadcastIntent");
    synchronized(this) {
        intent = verifyBroadcastLocked(intent);
        final ProcessRecord callerApp = getRecordForAppLOSP(caller);
        final int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        try {
            return broadcastIntentLocked(callerApp,
                    callerApp != null ? callerApp.info.packageName : null, callingFeatureId,
                    intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
                    requiredPermissions, excludedPermissions, excludedPackages, appOp, bOptions,
                    serialized, sticky, callingPid, callingUid, callingUid, callingPid, userId);
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }
}
final int broadcastIntentLocked(ProcessRecord callerApp,
        String callerPackage, String callerFeatureId, Intent intent, String resolvedType,
        IIntentReceiver resultTo, int resultCode, String resultData,
        Bundle resultExtras, String[] requiredPermissions, String[] excludedPermissions,
        String[] excludedPackages, int appOp, Bundle bOptions, boolean ordered,
        boolean sticky, int callingPid,
        int callingUid, int realCallingUid, int realCallingPid, int userId) {
    return broadcastIntentLocked(callerApp, callerPackage, callerFeatureId, intent,
            resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions,
            excludedPermissions, excludedPackages, appOp, bOptions, ordered, sticky, callingPid,
            callingUid, realCallingUid, realCallingPid, userId,
            false /* allowBackgroundActivityStarts */,
            null /* tokenNeededForBackgroundActivityStarts */, null /* broadcastAllowList */);
}
final int broadcastIntentLocked(ProcessRecord callerApp, String callerPackage,
        @Nullable String callerFeatureId, Intent intent, String resolvedType,
        IIntentReceiver resultTo, int resultCode, String resultData,
        Bundle resultExtras, String[] requiredPermissions,
        String[] excludedPermissions, String[] excludedPackages, int appOp, Bundle bOptions,
        boolean ordered, boolean sticky, int callingPid, int callingUid,
        int realCallingUid, int realCallingPid, int userId,
        boolean allowBackgroundActivityStarts,
        @Nullable IBinder backgroundActivityStartsToken,
        @Nullable int[] broadcastAllowList) {
    // 克隆一個 Intent,防止原始 intent 數據被修改
    intent = new Intent(intent);
    // ...
    // If we have not finished booting, don't allow this to launch new processes.
    // AMS 還沒有啟動完成前,廣播只能發送給動態注冊的廣播接收器,這樣可以防止拉起新的進程
    if (!mProcessesReady && (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {
        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
    }
    // ...
    final String action = intent.getAction();
    BroadcastOptions brOptions = null;
    // bOptions 可以解決發送廣播的一些限制,例如從后臺啟動Activity,只有系統 app 才能用到的 API
    if (bOptions != null) {
        // ...
    }
    // 限制受保護的廣播,只能由系統代碼發送
    final boolean isProtectedBroadcast;
    try {
        isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);
    } catch (RemoteException e) {
        Slog.w(TAG, "Remote exception", e);
        return ActivityManager.BROADCAST_SUCCESS;
    }
    final boolean isCallerSystem;
    switch (UserHandle.getAppId(callingUid)) {
        case ROOT_UID:
        case SYSTEM_UID:
        case PHONE_UID:
        case BLUETOOTH_UID:
        case NFC_UID:
        case SE_UID:
        case NETWORK_STACK_UID:
            isCallerSystem = true;
            break;
        default:
            isCallerSystem = (callerApp != null) && callerApp.isPersistent();
            break;
    }
    if (!isCallerSystem) {
        if (isProtectedBroadcast) {
            String msg = "Permission Denial: not allowed to send broadcast "
                    + action + " from pid="
                    + callingPid + ", uid=" + callingUid;
            Slog.w(TAG, msg);
            throw new SecurityException(msg);
        } else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
                || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
            // ...
        }
    }
    boolean timeoutExempt = false;
    if (action != null) {
        // 如果系統配置文件中允許發送這個后臺廣播,那么添加 Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND 標志位
        // 例如 frameworks/base/data/etc/framework-sysconfig.xml 允許發送如下后臺廣播
        // <allow-implicit-broadcast action="android.intent.action.SIM_STATE_CHANGED" />
        if (getBackgroundLaunchBroadcasts().contains(action)) {
            if (DEBUG_BACKGROUND_CHECK) {
                Slog.i(TAG, "Broadcast action " + action + " forcing include-background");
            }
            intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
        }
        switch (action) {
            // 對一些特殊的廣播進行處理...
        }
    }
    // 1. 緩存 sticky 廣播到 mStickyBroadcasts
    if (sticky) {
        // 檢查權限
        if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,
                callingPid, callingUid)
                != PackageManager.PERMISSION_GRANTED) {
            String msg = "Permission Denial: broadcastIntent() requesting a sticky broadcast from pid="
                    + callingPid + ", uid=" + callingUid
                    + " requires " + android.Manifest.permission.BROADCAST_STICKY;
            Slog.w(TAG, msg);
            throw new SecurityException(msg);
        }
        if (requiredPermissions != null && requiredPermissions.length > 0) {
            Slog.w(TAG, "Can't broadcast sticky intent " + intent
                    + " and enforce permissions " + Arrays.toString(requiredPermissions));
            return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION;
        }
        if (intent.getComponent() != null) {
            throw new SecurityException(
                    "Sticky broadcasts can't target a specific component");
        }
        // 確保使用 userId 發送的 sticky 廣播,不會與使用 USER_ALL 發送的 sticky 廣播有沖突
        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");
                        }
                    }
                }
            }
        }
        // 獲取 userId 對應的 sticky 廣播緩存,并把這個stikcy廣播添加/替換到緩存中
        ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
        if (stickies == null) {
            stickies = new ArrayMap<>();
            mStickyBroadcasts.put(userId, stickies);
        }
        ArrayList<Intent> list = stickies.get(intent.getAction());
        if (list == null) {
            list = new ArrayList<>();
            stickies.put(intent.getAction(), list);
        }
        final int stickiesCount = list.size();
        int i;
        // 存在就替換
        for (i = 0; i < stickiesCount; i++) {
            if (intent.filterEquals(list.get(i))) {
                // This sticky already exists, replace it.
                list.set(i, new Intent(intent));
                break;
            }
        }
        // 不存在就添加
        if (i >= stickiesCount) {
            list.add(new Intent(intent));
        }
    }
    int[] users;
    if (userId == UserHandle.USER_ALL) {
        // 如果以 USER_ALL 名字發送,那么獲取所有的啟動的 user
        users = mUserController.getStartedUserArray();
    } else {
        users = new int[] {userId};
    }
    // Figure out who all will receive this broadcast.
    // 現在開始找出誰需要接收這個廣播
    List receivers = null;
    List<BroadcastFilter> registeredReceivers = null;
    // 2. 收集動態和靜態廣播接收器
    // Intent.FLAG_RECEIVER_REGISTERED_ONLY 表示廣播只能發送給動態注冊的廣播接收器
    // 沒有指定這個標志位,那么就需要收集靜態注冊的廣播接收器
    if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
             == 0) {
        // receivers 此時保存的是靜態廣播接收器
        receivers = collectReceiverComponents(
                intent, resolvedType, callingUid, users, broadcastAllowList);
    }
    if (intent.getComponent() == null) {
        if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
            // ...以 USER_ALL 身份,從 shell 發送的廣播,那么需要獲取所有用戶注冊的廣播接收器
        } else {
            // 收集單個用戶注冊的動態廣播接收器
            registeredReceivers = mReceiverResolver.queryIntent(intent,
                    resolvedType, false /*defaultOnly*/, userId);
        }
    }
    // 是否替換即將發送的廣播
    final boolean replacePending =
            (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
    if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing broadcast: " + intent.getAction()
            + " replacePending=" + replacePending);
    // broadcastAllowList 是能接收廣播 app 白名單
    // 如果收集的動態注冊的廣播接收器,不屬于白名單中的 app,那么移除它
    if (registeredReceivers != null && broadcastAllowList != null) {
        for (int i = registeredReceivers.size() - 1; i >= 0; i--) {
            final int owningAppId = UserHandle.getAppId(registeredReceivers.get(i).owningUid);
            if (owningAppId >= Process.FIRST_APPLICATION_UID
                    && Arrays.binarySearch(broadcastAllowList, owningAppId) < 0) {
                registeredReceivers.remove(i);
            }
        }
    }
    // 3. 對于非有序廣播(包括sticky廣播),先"并行"地發送給動態廣播接收器
    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 r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
                callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
                requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions,
                registeredReceivers, resultTo, resultCode, resultData, resultExtras, ordered,
                sticky, false, userId, allowBackgroundActivityStarts,
                backgroundActivityStartsToken, timeoutExempt);
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
        final boolean replaced = replacePending
                && (queue.replaceParallelBroadcastLocked(r) != null);
        // Note: We assume resultTo is null for non-ordered broadcasts.
        if (!replaced) {
            queue.enqueueParallelBroadcastLocked(r);
            queue.scheduleBroadcastsLocked();
        }
        registeredReceivers = null;
        // 注意,對于發送非有序廣播,當把廣播發送給動態接收器后, NR 重置為 0
        NR = 0;
    }
    // 4. 對于有序廣播,按照優先級從高到低的順序,合并靜態和動態廣播接收器到 receivers
    int ir = 0;
    // receivers 收集的是靜態注冊的廣播接收器
    if (receivers != null) {
        // ...
        // NT 表示靜態廣播接收器的數量
        int NT = receivers != null ? receivers.size() : 0;
        int it = 0;
        ResolveInfo curt = null;
        BroadcastFilter curr = null;
        // 注意 NR 的值,前面發送非有序廣播給動態接收器時,NR 重置為 0
        // 如果此時 NR 還不為 0, 那么表示發送的是有序廣播
        // 那么根據廣播的優先級,按照從高到低的順序,把動態廣播接收器和靜態廣播接收器,合并到 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;
            }
        }
    }
    while (ir < NR) {
        if (receivers == null) {
            receivers = new ArrayList();
        }
        receivers.add(registeredReceivers.get(ir));
        ir++;
    }
    if (isCallerSystem) {
        checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
                isProtectedBroadcast, receivers);
    }
    // 5. “序列化”地發送廣播
    // 注意,這里分兩種情況
    // 如果發送的非有序廣播,那么 receivers 只保存了靜態注冊的廣播接收器
    // 如果發送的是有序廣播,那么 receivers 保存了靜態和動態注冊的廣播接收器
    if ((receivers != null && receivers.size() > 0)
            || resultTo != null) {
        BroadcastQueue queue = broadcastQueueForIntent(intent);
        BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
                callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
                requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions,
                receivers, resultTo, resultCode, resultData, resultExtras,
                ordered, sticky, false, userId, allowBackgroundActivityStarts,
                backgroundActivityStartsToken, timeoutExempt);
        final BroadcastRecord oldRecord =
                replacePending ? queue.replaceOrderedBroadcastLocked(r) : null;
        if (oldRecord != null) {
            // 處理替換廣播的情況 ....
        } else {
            queue.enqueueOrderedBroadcastLocked(r);
            queue.scheduleBroadcastsLocked();
        }
    } else {
        // 沒有找到接收廣播的接收器,簡單記錄下這個發送廣播的操作 ...
    }
    return ActivityManager.BROADCAST_SUCCESS;
}

這段代碼設計的并不是很好,很多細節都雜糅到一個方法中。對于一些細節,我進行了詳細的注釋,有興趣的讀者可以自行研究,而本文只關心發送廣播的主要流程。

我不打算按照代碼的邏輯來講解流程,我自己總結了一套流程。

對于非有序廣播(包括 sticky 廣播),發送流程如下

  • 把廣播“并行”地發送給動態廣播接收器。

  • 把廣播“串行”地發送給靜態廣播接收器。

為何要把非有序廣播(包括 sticky 廣播)優先發送給動態接收器?最簡單的理由就是,不需要先拉起進程!因為"快”,所以先發送。

對于有序廣播,發送的流程如下

  • 按照廣播的優先級,從高到低,把動態和靜態廣播接收器,合并到一起。

  • “串行”發送廣播給所有的接收器。

有序廣播,為何不先發送給動態接收器呢?因為它強調一個“有序”,所以要根據優先級來發送。

對于 sticky 廣播,由于它的特性,是需要對它的廣播 Intent 進行緩存的。根據前面注冊廣播接收器的分析,當注冊的廣播接收器匹配到緩存的 sticky 廣播 Intent,那么會立即返回數據給接收方,無論是通過函數的返回值,還是直接調用 BroadcastReceiver#onReceive()。

以上就是“ActivityManagerService廣播怎么注冊與發送”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

庄浪县| 永清县| 绥棱县| 扶风县| 石河子市| 册亨县| 上饶县| 长丰县| 克拉玛依市| 五寨县| 天峨县| 沙田区| 松江区| 双流县| 夏河县| 公主岭市| 深水埗区| 赤城县| 福清市| 大冶市| 开封县| 项城市| 博爱县| 青田县| 都昌县| 新民市| 大同县| 巴青县| 仙桃市| 阿尔山市| 游戏| 儋州市| 平陆县| 大冶市| 甘泉县| 南乐县| 保亭| 平舆县| 眉山市| 巴彦县| 田东县|