当前位置: 首页 > news >正文

android15哪些广播可以会走冷启动或者用于保活呢?

答:

1.只有静态注册的广播能触发冷启动且不被拦截,包括系统广播和符合条件的第三方广播。

2.第三方静态广播理论上可行(实际上不行,国内手机会拦截掉),但需满足:

2.1使用显式广播或豁免的隐式广播。

2.3声明exported="true"及必要权限。

2.3目标系统版本非Android 8.0+严格限制场景。

3.动态广播完全无法触发冷启动,因其注册信息随进程退出被清除

背景:从发送广播到触发冷启动的路径 scheduleReceiverColdLocked()

ContextImpl. sendBroadcast(Intent intent)->ActivityManagerService.broadcastIntentWithFeature() 开始发送广播→ActivityManagerService.broadcastIntentWithFeature()->ActivityManagerService.broadcastIntentLocked()→→ActivityManagerService.broadcastIntentLocked()->ActivityManagerService.broadcastIntentLockedTraced()→→→ActivityManagerService.broadcastIntentLockedTraced()->BroadcastQueue.enqueueBroadcastLocked()→→→→BroadcastQueue.enqueueBroadcastLocked()->BroadcastQueueModernImpl.enqueueBroadcastLocked() 主要冻结和skip策略存在拦截广播→→→→→BroadcastQueueModernImpl.enqueueBroadcastLocked()->BroadcastQueueModernImpl.updateRunningListLocked()→→→→→→BroadcastQueueModernImpl.updateRunningListLocked()->BroadcastQueueModernImpl.scheduleReceiverColdLocked() 冷启动后发广播→→→→→→→BroadcastQueueModernImpl.scheduleReceiverColdLocked()->ActivityManagerService.startProcessLocked()→→→→→→→→ActivityManagerService.startProcessLocked()->ProcessList.startProcessLocked() →→→→→→→→→ProcessList.startProcessLocked()->ZygoteProcess.start()// 最终通过Zygote fork进程→→→→→→→→→→ZygoteProcess.start()->BroadcastQueueModernImpl.onApplicationAttachedLocked()->BroadcastQueueModernImpl.updateRunningListLocked() 后面的流程和热启动发送广播一样了→→→→→BroadcastQueueModernImpl.updateRunningListLocked()->BroadcastQueueModernImpl.scheduleReceiverWarmLocked() 直接热启动发广播→→→→→→BroadcastQueueModernImpl.scheduleReceiverWarmLocked()->BroadcastQueueModernImpl.dispatchReceivers()→→→→→→→BroadcastQueueModernImpl.dispatchReceivers()->ActivityThread.scheduleRegisteredReceiver→→→→→→→→ActivityThread.scheduleRegisteredReceiver->LoadedApk.ReceiverDispatcher.InnerReceiver.performReceive()→→→→→→→→→LoadedApk.ReceiverDispatcher.InnerReceiver.performReceive()->LoadedApk.ReceiverDispatcher.Args.getRunnable()→→→→→→→→→→LoadedApk.ReceiverDispatcher.Args.getRunnable()->BroadcastReceiver.onReceive(mContext, intent):App就收到广播了哈

下一步:重点看下触发 scheduleReceiverColdLocked() 的条件是啥?

源码分析

1.ActivityManagerService.broadcastIntentLockedTraced()

由于冷启动的逻辑在于ActivityManagerService.broadcastIntentLockedTraced()->BroadcastQueue.enqueueBroadcastLocked()->BroadcastQueueModernImpl.enqueueBroadcastLocked()->BroadcastQueueModernImpl.updateRunningListLocked()->BroadcastQueueModernImpl.scheduleReceiverColdLocked(),故需要重点看下broadcastIntentLockedTraced哪些逻辑可以走到 enqueueBroadcastLocked

/** * 处理广播的核心逻辑,决定是否将广播加入队列(enqueueBroadcastLocked) * @return 广播处理状态码(如BROADCAST_SUCCESS) */@GuardedBy("this")final int broadcastIntentLockedTraced(...) {    // 1. SDK沙箱进程权限检查(若为沙箱进程且广播被禁止,直接抛出异常)    if (Process.isSdkSandboxUid(realCallingUid) && !sdkSandboxManagerLocal.canSendBroadcast(intent)) {        throw new SecurityException("SDK sandbox广播禁止发送: " + intent.getAction());    }    // 2. 结果接收器有效性检查(若resultTo非空但resultToApp为空,尝试补全system进程)    if ((resultTo != null) && (resultToApp == null)) {        if (resultTo.asBinder() instanceof BinderProxy) {            Slog.wtf(TAG, "无效的resultToApp配置"); // 直接警告但不拦截        } else {            resultToApp = getProcessRecordLocked("system", SYSTEM_UID); // 补全为system进程        }    }    // 3. Instant App限制检查(禁止使用FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS)    if (callerInstantApp) {        intent.setFlags(intent.getFlags() & ~Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);    }    // 4. 用户状态检查(若目标用户未运行且非系统广播,直接返回失败)    if (!mUserController.isUserOrItsParentRunning(userId)             && !(callingUid == SYSTEM_UID || Intent.ACTION_SHUTDOWN.equals(action))) {        return ActivityManager.BROADCAST_FAILED_USER_STOPPED;    }    // 5. 广播选项(BroadcastOptions)权限检查    if (brOptions != null) {        // 5.1 临时白名单权限检查        if (brOptions.getTemporaryAppAllowlistDuration() > 0                 && !checkBackgroundPermission(realCallingPid, realCallingUid)) {            throw new SecurityException("缺少后台启动权限");        }        // 5.2 后台限制检查        if (brOptions.isDontSendToRestrictedApps()                 && isBackgroundRestrictedNoCheck(callingUid, callerPackage)) {            return ActivityManager.START_CANCELED;        }    }    // 6. 受保护广播检查(仅允许系统进程发送)    if (!isCallerSystem && isProtectedBroadcast) {        throw new SecurityException("非系统进程禁止发送受保护广播: " + action);    }    // 7. 特殊广播处理(如包管理相关广播)    switch (action) {        case Intent.ACTION_PACKAGE_ADDED:            // 处理包安装逻辑(不影响广播入队)            break;        case "com.android.launcher.action.INSTALL_SHORTCUT":            // 直接拦截过时广播            return ActivityManager.BROADCAST_SUCCESS;    }    // 8. 粘性广播处理(需BROADCAST_STICKY权限)    if (sticky) {        if (!checkPermission(BROADCAST_STICKY, callingPid, callingUid)) {            throw new SecurityException("缺少粘性广播权限");        }        // 存储粘性广播到mStickyBroadcasts    }    // 9. 接收者解析(动态注册+静态注册)    List receivers = collectReceiverComponents(intent, ...); // 静态接收者    List<BroadcastFilter> registeredReceivers = mReceiverResolver.queryIntent(...); // 动态接收者    // 10. 最终入队条件判断(关键逻辑)    if ((receivers != null && receivers.size() > 0) || resultTo != null) {        BroadcastRecord r = new BroadcastRecord(...); // 创建广播记录        queue.enqueueBroadcastLocked(r); // 符合条件,入队    } else {        // 无接收者时记录统计信息        addBroadcastStatLocked(intent.getAction(), callerPackage, 0, 0, 0);    }    return ActivityManager.BROADCAST_SUCCESS;}

上述中:合并动态和静态接收者是为了统一管理广播分发逻辑,而冷启动的触发完全由系统根据进程状态和注册类型自动判断,开发者无需显式干预

符合/不符合enqueueBroadcastLocked的条件总结

条件分类

具体场景

是否入队

返回值/异常

基础检查通过

无权限/状态拦截,且存在有效接收者或resultTo

✅ 符合

BROADCAST_SUCCESS

SDK沙箱权限不足

沙箱进程发送未授权的广播

❌ 不符合

抛出SecurityException

用户状态无效

目标用户未运行且非系统/关机广播

❌ 不符合

BROADCAST_FAILED_USER_STOPPED

后台权限不足

广播选项要求临时白名单但调用方无权限

❌ 不符合

抛出SecurityException

受保护广播拦截

非系统进程发送受保护广播(ACTION_BOOT_COMPLETED)

❌ 不符合

抛出SecurityException

粘性广播权限不足

缺少BROADCAST_STICKY权限

❌ 不符合

抛出SecurityException

特殊广播拦截

发送过时广播(如INSTALL_SHORTCUT)

❌ 不符合

BROADCAST_SUCCESS(静默拦截)

无接收者且无resultTo

广播无任何接收者且未指定resultTo

❌ 不符合

记录统计信息后返回BROADCAST_SUCCESS

2.BroadcastQueueModernImpl.enqueueBroadcastLocked()

由于冷启动的逻辑在于BroadcastQueue.enqueueBroadcastLocked()->BroadcastQueueModernImpl.enqueueBroadcastLocked()->BroadcastQueueModernImpl.updateRunningListLocked()->BroadcastQueueModernImpl.scheduleReceiverColdLocked(),故重点分析下enqueueBroadcastLocked函数哪些条件被return,又是哪些条件才能真正走到 updateRunningListLocked() 函数​​​​​​​

public void enqueueBroadcastLocked(@NonNull BroadcastRecord r) {    if (Flags.deferOutgoingBroadcasts() && isProcessFreezable(r.callerApp)) {        final BroadcastProcessQueue queue = getOrCreateProcessQueue(                r.callerApp.processName, r.callerApp.uid);        if (queue.getOutgoingBroadcastCount() >= mConstants.MAX_FROZEN_OUTGOING_BROADCASTS) {            r.callerApp.killLocked("Too many outgoing broadcasts in cached state",                    ApplicationExitInfo.REASON_OTHER,                    ApplicationExitInfo.SUBREASON_EXCESSIVE_OUTGOING_BROADCASTS_WHILE_CACHED,                    true /* noisy */);            return; // 当前进程的待处理广播数超过阈值,则广播被拦截,即不会冷启动进程也不会发送广播        }        queue.enqueueOutgoingBroadcast(r);        mHistory.onBroadcastFrozenLocked(r);        mService.mOomAdjuster.mCachedAppOptimizer.freezeAppAsyncImmediateLSP(r.callerApp);        return; // 若 Flags.deferOutgoingBroadcasts() 启用且调用进程可冻结,则广播被拦截,即不会冷启动进程也不会发送广播    }    ...    for (int i = 0; i < r.receivers.size(); i++) {        final Object receiver = r.receivers.get(i);        final BroadcastProcessQueue queue = getOrCreateProcessQueue(                getReceiverProcessName(receiver), getReceiverUid(receiver));        // If this receiver is going to be skipped, skip it now itself and don't even enqueue        // it.        final String skipReason = mSkipPolicy.shouldSkipMessage(r, receiver);        if (skipReason != null) {            setDeliveryState(null, null, r, i, receiver, BroadcastRecord.DELIVERY_SKIPPED,                    "skipped by policy at enqueue: " + skipReason);            continue; // 被SkipPolicy策略拦截,则广播被拦截,即不会冷启动进程也不会发送广播        }        ...        updateRunnableList(queue); // 广播冷启动的逻辑在于updateRunningListLocked函数        enqueueUpdateRunningList();    }

拦截点

触发条件

影响范围

后续动作

广播超限

进程冻结且广播超限

全局拦截(杀死进程)

冻结未超限

进程冻结但广播未超限

全局拦截(暂存广播)

解冻后可能投递

BroadcastSkipPolicy策略跳过

接收者不满足策略

单个接收者拦截

继续处理其他接收者

除了冻结带来的广播拦截,我们还需要看下 BroadcastSkipPolicy的拦截策略细节​​​​​​​

/** * 检查给定的广播记录(BroadcastRecord)是否允许发送到指定的广播过滤器(BroadcastFilter)。 * 若不允许,返回拦截原因;否则返回null表示允许投递。 *  * @param r 待发送的广播记录,包含广播内容、发送方信息等 * @param filter 目标广播过滤器,包含接收方权限、状态等条件 * @return 拦截原因字符串(若需拦截),否则返回null */private @Nullable String shouldSkipMessage(@NonNull BroadcastRecord r,        @NonNull BroadcastFilter filter) {    // 1. 兼容性变更检查:若广播要求接收方满足特定兼容性变更但未满足,则拦截    if (r.options != null && !r.options.testRequireCompatChange(filter.owningUid)) {        return "Compat change filtered: broadcasting " + r.intent.toString()                + " to uid " + filter.owningUid + " due to compat change "                + r.options.getRequireCompatChangeId();    }    // 2. 关联性检查:发送方与接收方是否允许通信(如跨应用限制)    if (!mService.validateAssociationAllowedLocked(r.callerPackage, r.callingUid,            filter.packageName, filter.owningUid)) {        return "Association not allowed: broadcasting "                + r.intent.toString()                + " from " + r.callerPackage + " (pid=" + r.callingPid                + ", uid=" + r.callingUid + ") to " + filter.packageName + " through "                + filter;    }    // 3. Intent防火墙检查(自定义或系统默认)    if (IntentFirewallStub.getInstance().isEnable()) {        if (IntentFirewallStub.getInstance().shouldPreventBroadcast(r.intent, r.callerPackage,                r.callingUid, r.callingPid, r.resolvedType,                filter.packageName,                filter.receiverList.uid)) {            return "IntentFirewall blocked: broadcasting "                    + r.intent.toString()                    + " from " + r.callerPackage + " (pid=" + r.callingPid                    + ", uid=" + r.callingUid + ") to " + filter.packageName + " through "                    + filter;        }    } else if (!mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,            r.callingPid, r.resolvedType, filter.receiverList.uid)) {        return "Firewall blocked: broadcasting "                + r.intent.toString()                + " from " + r.callerPackage + " (pid=" + r.callingPid                + ", uid=" + r.callingUid + ") to " + filter.packageName + " through "                + filter;    }    // 4. 接收方所需权限检查(发送方是否具备)    if (filter.requiredPermission != null) {        int perm = checkComponentPermission(filter.requiredPermission,                r.callingPid, r.callingUid, -1, true);        if (perm != PackageManager.PERMISSION_GRANTED) {            return "Permission Denial: broadcasting "                    + r.intent.toString()                    + " from " + r.callerPackage + " (pid="                    + r.callingPid + ", uid=" + r.callingUid + ")"                    + " requires " + filter.requiredPermission                    + " due to registered receiver " + filter;        }        // 关联AppOps权限检查        final int opCode = AppOpsManager.permissionToOpCode(filter.requiredPermission);        if (opCode != AppOpsManager.OP_NONE                && mService.getAppOpsManager().noteOpNoThrow(opCode, r.callingUid,                r.callerPackage, r.callerFeatureId, "Broadcast sent to protected receiver")                != AppOpsManager.MODE_ALLOWED) {            return "Appop Denial: broadcasting "                    + r.intent.toString()                    + " from " + r.callerPackage + " (pid="                    + r.callingPid + ", uid=" + r.callingUid + ")"                    + " requires appop " + AppOpsManager.permissionToOp(                            filter.requiredPermission)                    + " due to registered receiver " + filter;        }    }    // 5. 接收方进程状态检查(是否已终止或崩溃)    if ((filter.receiverList.app == null || filter.receiverList.app.isKilled()            || filter.receiverList.app.mErrorState.isCrashing())) {        return "Skipping deliver [" + r.queue.toString() + "] " + r                + " to " + filter.receiverList + ": process gone or crashing";    }    // 6. Instant App可见性检查    final boolean visibleToInstantApps =            (r.intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0;    if (!visibleToInstantApps && filter.instantApp            && filter.receiverList.uid != r.callingUid) {        return "Instant App Denial: receiving "                + r.intent.toString()                + " to " + filter.receiverList.app                + " (pid=" + filter.receiverList.pid                + ", uid=" + filter.receiverList.uid + ")"                + " due to sender " + r.callerPackage                + " (uid " + r.callingUid + ")"                + " not specifying FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS";    }    // 7. 接收方是否允许Instant App发送广播    if (!filter.visibleToInstantApp && r.callerInstantApp            && filter.receiverList.uid != r.callingUid) {        return "Instant App Denial: receiving "                + r.intent.toString()                + " to " + filter.receiverList.app                + " (pid=" + filter.receiverList.pid                + ", uid=" + filter.receiverList.uid + ")"                + " requires receiver be visible to instant apps"                + " due to sender " + r.callerPackage                + " (uid " + r.callingUid + ")";    }    // 8. 广播所需权限检查(接收方是否具备)    if (r.requiredPermissions != null && r.requiredPermissions.length > 0) {        for (String requiredPermission : r.requiredPermissions) {            int perm = checkComponentPermission(requiredPermission,                    filter.receiverList.pid, filter.receiverList.uid, -1, true);            if (perm != PackageManager.PERMISSION_GRANTED) {                return "Permission Denial: receiving "                        + r.intent.toString()                        + " to " + filter.receiverList.app                        + " (pid=" + filter.receiverList.pid                        + ", uid=" + filter.receiverList.uid + ")"                        + " requires " + requiredPermission                        + " due to sender " + r.callerPackage                        + " (uid " + r.callingUid + ")";            }        }    }    // 9. 排除性权限检查(接收方不能具备某些权限)    if (r.excludedPermissions != null && r.excludedPermissions.length > 0) {        for (String excludedPermission : r.excludedPermissions) {            int perm = checkComponentPermission(excludedPermission,                    filter.receiverList.pid, filter.receiverList.uid, -1, true);            if (perm == PackageManager.PERMISSION_GRANTED) {                return "Permission Denial: receiving "                        + r.intent.toString()                        + " to " + filter.receiverList.app                        + " (pid=" + filter.receiverList.pid                        + ", uid=" + filter.receiverList.uid + ")"                        + " excludes " + excludedPermission                        + " due to sender " + r.callerPackage                        + " (uid " + r.callingUid + ")";            }        }    }    // 10. 排除性包名检查(接收方不能属于某些包)    if (r.excludedPackages != null && r.excludedPackages.length > 0) {        if (ArrayUtils.contains(r.excludedPackages, filter.packageName)) {            return "Skipping delivery of excluded package "                    + r.intent.toString()                    + " to " + filter.receiverList.app                    + " (pid=" + filter.receiverList.pid                    + ", uid=" + filter.receiverList.uid + ")"                    + " excludes package " + filter.packageName                    + " due to sender " + r.callerPackage                    + " (uid " + r.callingUid + ")";        }    }    // 11. 广播所需AppOps检查(接收方是否允许操作)    if (r.appOp != AppOpsManager.OP_NONE            && mService.getAppOpsManager().noteOpNoThrow(r.appOp,            filter.receiverList.uid, filter.packageName, filter.featureId,            "Broadcast delivered to registered receiver " + filter.receiverId)            != AppOpsManager.MODE_ALLOWED) {        return "Appop Denial: receiving "                + r.intent.toString()                + " to " + filter.receiverList.app                + " (pid=" + filter.receiverList.pid                + ", uid=" + filter.receiverList.uid + ")"                + " requires appop " + AppOpsManager.opToName(r.appOp)                + " due to sender " + r.callerPackage                + " (uid " + r.callingUid + ")";    }    // 12. 导出性检查(接收方未声明RECEIVER_EXPORTED时拦截非系统广播)    final int originalCallingUid = r.sticky ? r.originalStickyCallingUid : r.callingUid;    if (!filter.exported && checkComponentPermission(null, r.callingPid,            originalCallingUid, filter.receiverList.uid, filter.exported)            != PackageManager.PERMISSION_GRANTED) {        return "Exported Denial: sending "                + r.intent.toString()                + ", action: " + r.intent.getAction()                + " from " + r.callerPackage                + " (uid=" + originalCallingUid + ")"                + " due to receiver " + filter.receiverList.app                + " (uid " + filter.receiverList.uid + ")"                + " not specifying RECEIVER_EXPORTED";    }    // 13. 权限审核检查(若接收方需权限审核但未完成)    if (!requestStartTargetPermissionsReviewIfNeededLocked(r, filter.packageName,            filter.owningUserId)) {        return "Skipping delivery to " + filter.packageName + " due to permissions review";    }    return null; // 所有检查通过,允许投递}

拦截类型

触发条件

拦截原因示例

兼容性变更拦截

接收方未满足广播要求的兼容性变更

Compat change filtered: broadcasting...

关联性拦截

发送方与接收方不允许通信(如跨应用限制)

Association not allowed: broadcasting...

防火墙拦截

自定义或系统防火墙规则阻止广播

IntentFirewall blocked: broadcasting...

发送方权限不足

发送方缺少接收方要求的权限或AppOps权限

Permission Denial: broadcasting... requires [权限]

接收方进程异常

接收方进程已终止或崩溃

Skipping deliver...: process gone or crashing

Instant App可见性拦截

广播未标FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS

且接收方为Instant App

Instant App Denial: receiving... not specifying FLAG...

接收方权限不足

接收方缺少广播要求的权限

Permission Denial: receiving... requires [权限]

排除性权限拦截

接收方具备广播排除的权限

Permission Denial: receiving... excludes [权限]

排除性包名拦截

接收方属于广播排除的包名

Skipping delivery of excluded package...

AppOps操作拦截

接收方未通过广播要求的AppOps检查

Appop Denial: receiving... requires appop [操作]

导出性拦截

接收方未声明RECEIVER_EXPORTED且非系统广播

Exported Denial: sending... not specifying RECEIVER_EXPORTED

权限审核拦截

接收方需权限审核但未完成

Skipping delivery... due to permissions review

3. BroadcastQueueModernImpl.updateRunningListLocked()

是否暖启动? (processWarm)

├─ 是 → 尝试解冻 → 解冻后仍为暖启动? → 是 → 调用 scheduleReceiverWarmLocked

│                                   └─ 否 → 降级为冷启动

└─ 否 → 冷启动并发控制

​​​​​​​

private void updateRunningListLocked() {    BroadcastProcessQueue queue = mRunnableHead;    ...    // We might not have heard about a newly running process yet, so    // consider refreshing if we think we're cold    updateWarmProcess(queue);    final boolean processWarm = queue.isProcessWarm();    ...    boolean completed;    if (processWarm) {        ...    } else {        completed = scheduleReceiverColdLocked(queue);    }}

下一步看下 processWarm 的判断条件

4. BroadcastProcessQueue.isProcessWarm()

暖启动需满足以下条件:

1. 进程对象({@code app})已初始化且未被销毁;

2. 进程的单向通信线程(Oneway Thread)已就绪;

3. 进程未被主动杀死(如OOM或系统策略触发)。​​​​​​​

/** * 判断当前进程是否处于暖启动(Warm Start)状态。 * <p> * 暖启动需满足以下条件: * 1. 进程对象({@code app})已初始化且未被销毁; * 2. 进程的单向通信线程(Oneway Thread)已就绪; * 3. 进程未被主动杀死(如OOM或系统策略触发)。 * </p> * * @return {@code true} 若进程满足暖启动条件,{@code false} 若进程需冷启动或已失效 * @see #scheduleReceiverWarmLocked 暖启动逻辑的关联方法 * @see #scheduleReceiverColdLocked 冷启动逻辑的关联方法 *  * @implNote  * 1. 线程有效性:仅检查线程是否存在,未验证线程活跃性(如是否阻塞); * 2. 竞态条件:若进程在检查过程中被杀死,可能短暂返回错误状态,需结合调用方重试机制; * 3. 性能:轻量级检查,无复杂计算,适用于高频调用场景。 */public boolean isProcessWarm() {    // 检查进程对象是否有效(非空且未被销毁)    // 注意:app.isKilled()需确保线程安全,避免与kill操作竞态    return (app != null) &&            // 检查单向通信线程是否就绪(Binder线程池初始化完成)           (app.getOnewayThread() != null) &&            // 确认进程未被标记为已杀死           !app.isKilled();}

我们可以看到:若应用未运行,AMS会触发冷启动流程。那么是不是动态广播和静态广播只要应用未启动就会走冷启动流程呢?

答:不是的哈。

静态注册:默认允许冷启动(即使应用未运行),因为静态广播常驻系统

动态注册:仅当进程活跃时有效,应用退出后动态注册的接收者会被自动移除,不会触发冷启动

5. 应用退出后动态广播的注册信息回被自动移除,故不会触发冷启动流程

cleanUpApplicationRecordLocked()

→ app.onCleanupApplicationRecordLSP()  // ProcessRecord的内部方法

→ AMS.removeReceiverLocked()      // 实际移除动态广播接收器​​​​​​​

void removeReceiverLocked(ReceiverList rl) {    mRegisteredReceivers.remove(rl.receiver.asBinder());    for (int i = rl.size() - 1; i >= 0; i--) {        mReceiverResolver.removeFilter(rl.get(i));    }}

应用退出后动态广播的注册信息回被自动移除,故不会触发冷启动流程

6.总结

发送广播 → AMS.broadcastIntentLocked()

→ 匹配静态接收者(PackageManagerService)

→ 若进程未运行 → scheduleReceiverColdLocked()

→ startProcessLocked() → Zygote fork进程

→ 进程启动后通过LoadedApk.ReceiverDispatcher投递广播

故:

1.只有静态注册的广播能触发冷启动且不被拦截,包括系统广播和符合条件的第三方广播。

2.第三方静态广播理论上可行(实际上不行,国内手机会拦截掉),但需满足:

2.1使用显式广播或豁免的隐式广播。

2.3声明exported="true"及必要权限。

2.3目标系统版本非Android 8.0+严格限制场景。

3.动态广播完全无法触发冷启动,因其注册信息随进程退出被清除

http://www.dtcms.com/a/323243.html

相关文章:

  • 探索Trae:使用Trae CN爬取 Gitbook 电子书
  • 【Doris】实时分析型数据库
  • 走遍美国5 The Right Magic 钓鱼秘决
  • 【Python 语法糖小火锅 · 第 3 涮】
  • 【RabbitMQ】高级特性—TTL、延迟队列详解
  • Java 中的编译与反编译:全面解析与实践指南
  • drippingblues靶机
  • 四边形(梯形、平行四边形、矩形、菱形和正方形)
  • [贪心]田忌赛马
  • Aurora接口FPGA设计
  • QT Creator 5.14.2安装
  • 卷板矫平机:给一张钢板做“拉伸放松操”
  • 北大回应录取通知书被指存在语句问题
  • Claude Code 与 Cursor 技术对比:架构差异与适用场景分析
  • 四、RuoYi-Cloud-Plus 部署时nacos配置服务启动
  • NVIDIA Jetson实战笔记
  • 相册管理系统介绍
  • <PLC><汇川><字符转换>在汇川PLC中,如何进行字符串的转换与比较?
  • 实数与复数及欧拉公式关系
  • WeTok Powerful Discrete Tokenization for High-Fidelity Visual Reconstruction
  • DAY 37 作业(补)
  • vue3上传的文件在线查看
  • Mistral Small 3.1 架构深度解析:高效小型模型的巅峰之作
  • 华数杯C题:可调控生物节律的LED光源研究——数学建模与Python实战
  • 应用层Http协议(1)
  • 大玄古籍制作软件【详细教程20:txt文档config自动化配置】,排版软件,自动排版,排版设计,个人出书,一键排版
  • MATLAB中文乱码的解决方法
  • 吴恩达机器学习笔记(4)—多变量线性回归:梯度下降(附代码)
  • STM32学习笔记6-TIM-2输出比较功能
  • Python(13) -- 面向对象