返回顶部
首页 > 资讯 > 精选 >ActivityManagerService广播怎么注册与发送
  • 165
分享到

ActivityManagerService广播怎么注册与发送

2023-07-05 08:07:41 165人浏览 八月长安
摘要

今天小编给大家分享一下ActivityManagerService广播怎么注册与发送的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一

今天小编给大家分享一下ActivityManagerService广播怎么注册与发送的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

注册广播接收器

广播接收器可以分为动态和静态,静态广播接收器就是在 AndroidManifest.xml 中注册的,而动态的广播接收器是在代码中通过 Context#reGISterReceiver() 注册的。

静态广播接收器,在发送广播时,服务端会从 PKMS 中收集,而动态的广播接收器,需要接收方发送给服务端。因此,下面只分析动态广播接收器的注册过程

// ContextImpl.javapublic 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.javapublic 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 );                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.javapublic 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 ,                null, AppOpsManager.OP_NONE, null, false, false, getUserId());    } catch (RemoteException e) {        throw e.rethrowFromSystemServer();    }}
// ActivityManagerService.javapublic 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 ,            null , null );}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 , 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广播怎么注册与发送”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网精选频道。

--结束END--

本文标题: ActivityManagerService广播怎么注册与发送

本文链接: https://lsjlt.com/news/350517.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • ActivityManagerService广播怎么注册与发送
    今天小编给大家分享一下ActivityManagerService广播怎么注册与发送的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一...
    99+
    2023-07-05
  • ActivityManagerService广播注册与发送示例解析
    目录引言注册广播接收器发送广播结束引言 最近,帮同事解决了两个问题,一个问题是 app 接收开机广播的速度太慢,另一个问题是app有时无法接收到广播。同事不知道如何解决这个问题,是...
    99+
    2023-03-02
    ActivityManagerService广播注册发送 ActivityManagerService 广播
  • ActivityManagerService广播并行发送与串行发送怎么实现
    这篇文章主要讲解了“ActivityManagerService广播并行发送与串行发送怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“ActivityManagerService广播并...
    99+
    2023-07-05
  • ActivityManagerService广播并行发送与串行发送示例解析
    目录"并行"广播的发送“串行”广播的发送广播发送给正在启动的进程广播 ANR结束"并行"广播的发送 本文以 Acti...
    99+
    2023-03-02
    ActivityManagerService广播并行发送 ActivityManagerService广播串行发送
  • linux怎么发送广播消息
    在Linux系统中,可以使用`wall`命令来发送广播消息。`wall`命令用于向所有登录到系统的用户发送消息,格式如下:```wa...
    99+
    2023-09-04
    linux
  • 在android怎么发送广播消息
    在Android中,可以通过以下步骤来发送广播消息:1. 创建一个`Intent`对象,用于描述广播消息的内容和目标。```java...
    99+
    2023-09-04
    android
  • Android入门:广播发送者与广播接收者详细介绍
    一、广播发送者&广播接收者介绍 1.广播接收者 广播接收者简单地说就是接收广播意图的Java类,此Java类继承BroadcastReceiver类,重写: publ...
    99+
    2022-06-06
    Android
  • Android怎么使用广播发送消息
    本文小编为大家详细介绍“Android怎么使用广播发送消息”,内容详细,步骤清晰,细节处理妥当,希望这篇“Android怎么使用广播发送消息”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。具体效果如下activit...
    99+
    2023-06-30
  • Android中怎么利用广播实现静态注册
    Android中怎么利用广播实现静态注册,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。而Android中的广播机制则更为灵活,因为每个应用程序可以对自己感兴趣的广播进行注册,这...
    99+
    2023-06-04
  • Android静态,动态注册与跨平台接收广播
    Android静态,动态注册与跨平台接收广播 静态注册 在activity_main.xml 中添加一个button,用来发送广播的点击事件 ...
    99+
    2022-06-06
    跨平台 动态 Android
  • 怎么在Android中利用Intent发送广播消息
    这篇文章给大家介绍怎么在Android中利用Intent发送广播消息,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。Android Intent发送广播消息Intent的另一种用途是发送广播消息,应用程序和Android...
    99+
    2023-05-31
    android intent roi
  • 有序广播怎么在Android应用中进行发送
    有序广播怎么在Android应用中进行发送?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。Android系统提供了两种广播类型,一种是有序广播,一种是有序广播。(1)无序广播是完...
    99+
    2023-05-31
    android roi
  • 广东域名注册怎么备案
    广东域名注册备案需要以下步骤:1. 在广东省的互联网信息办公室网站上注册账号并登录。2. 在网站上提交备案申请,填写相关信息,包括域...
    99+
    2023-06-06
    广东域名 域名
  • 使用django怎么实现发送验证码注册邮箱
    这篇文章将为大家详细讲解有关使用django怎么实现发送验证码注册邮箱,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。视图代码lis = []#设置一个空列表用来存放发送的...
    99+
    2023-06-14
  • 利用Java怎么在用户注册时发送激活邮件
    这期内容当中小编将会给大家带来有关利用Java怎么在用户注册时发送激活邮件,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。1.RegisterController.javapackage com....
    99+
    2023-05-31
    java 在用 ava
  • Android广播机制原理与开发的方法是什么
    今天小编给大家分享一下Android广播机制原理与开发的方法是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。广播机制简介...
    99+
    2023-07-05
  • Vue怎么实现简易注册页面和发送验证码功能
    本篇内容介绍了“Vue怎么实现简易注册页面和发送验证码功能”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1. 效果展示2. 增强版验证码及邮...
    99+
    2023-06-21
  • 怎么理解spark的计算器与广播变量
    这篇文章给大家介绍怎么理解spark的计算器与广播变量,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。一.计算器1.官网2.解释计数器只支持加,计算器字task里面3.测试4.结果截图WEBUI4.应用场景数据很多有的数...
    99+
    2023-06-02
  • Android中使用Receiver怎么实现动态注册与静态注册
    Android中使用Receiver怎么实现动态注册与静态注册?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。广播接收器注册一共有两种形式 : 静态注册和动态注册.两者及其接...
    99+
    2023-05-31
    android receiver roi
  • Python怎么实现直播弹幕自动发送功能
    这篇文章主要讲解了“Python怎么实现直播弹幕自动发送功能”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Python怎么实现直播弹幕自动发送功能”吧!前言先打开一个直播间按F12打开开发者...
    99+
    2023-06-29
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作