Android14 普通应用registerReceiver注册广播报错One of RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED should be

本文详细解析了Android14中普通应用在注册广播时遇到的RECEIVER_EXPORTED或RECEIVER_NOT_EXPORTED错误,介绍了报错原因、解决方法,以及Context.RECEIVER_FLAGS的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android14 普通应用registerReceiver注册广播报错One of RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED should be specified …

一、前言

Android14 普通应用注册广播registerReceiver会报错,提示需要添加一个参数RECEIVER_EXPORTED 或者 RECEIVER_NOT_EXPORTED;系统framework或者系统应用是不会报这个错误的!

这个问题比较好解决,加一个参数就行,但是网上目前没有人对这个问题今天深入分析。

本文对registerReceiver报错进行分析,具体到哪个类哪行代码报错,

分析了解后对于系统其他相关报错能有个认识,或者有分析思路。

RECEIVER_EXPORTED 表示外部应用范围,RECEIVER_NOT_EXPORTED 表示非外部应用范围,是否有作用?

二、Google 对Android14 广播说明

说明图片所示:

在这里插入图片描述

对于Android14 主要说明内容:

For apps targeting Build.VERSION_CODES.UPSIDE_DOWN_CAKE, either RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED must be specified if the receiver is not being registered for system broadcasts or a SecurityException will be thrown. See registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, int) to register a receiver with flags.

Build.VERSION_CODES.UPSIDE_DOWN_CAKE 是Android14的名称,旋转蛋糕。

上面的英文大概意思:

对于以Build为 Android14的应用程序版本代码。
如果接收器没有为系统广播注册,或者将引发SecurityException,
则必须指定RECEIVER_EXPORTED或RECEIVE_NOT_EXPORTED。
请参阅registerReceiver(android.content.BroadcastReceiver,android.contant.IntentFilter,int)注册带有标志的接收器。

相关网址:https://developer.android.google.cn/reference/android/content/Context

搜索 registerReceiver 就可能找到相关代码。

三、Android14 注册广播报错解决和分析

1、报错示例和解决方法

Android普通应用的注册广播的代码:

public class MainActivity extends AppCompatActivity {

    public void testBroadcast(View view) { //点击按钮,监听广播
        LogUtil.debug("");
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("aa");        
        //registerReceiver(mReceiver1, intentFilter);//在这里报错,如果没有try catch,会崩溃
        
        //解决方法:添加一个参数Context.RECEIVER_EXPORTED 或者ContextRECEIVER_NOT_EXPORTED
        //RECEIVER_EXPORTED 表示可以接收应用外部广播,ContextRECEIVER_NOT_EXPORTED 应用内部广播
        registerReceiver(mReceiver2, intentFilter, Context.RECEIVER_EXPORTED);
    }

    private BroadcastReceiver mReceiver1 = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
             LogUtil.debug("action = " + action);
        }
    }

上面代码就包含了报错的代码示例和正确的写法。

在Android13或者更早的版本,上面的代码都是没有问题的;只有Android14 会报错。

2、报错分析

报错的堆栈信息:

在这里插入图片描述

报错最主要相关日志:

		 Caused by: java.lang.SecurityException: com.demo.android14demo: One of RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED should be specified when a receiver isn't being registered exclusively for system broadcasts
		at android.app.IActivityManager.registerReceiverWithFeature
        at android.app.ContextImpl.registerReceiverInternal(ContextImpl.java:1852)
        at android.app.ContextImpl.registerReceiver(ContextImpl.java:1792)
        at android.app.ContextImpl.registerReceiver(ContextImpl.java:1780)
        at android.content.ContextWrapper.registerReceiver(ContextWrapper.java:755)
        at com.demo.android14demo.MainActivity.testBroadcast(MainActivity.java:25)
     Caused by: android.os.RemoteException: Remote stack trace:
        at com.android.server.am.ActivityManagerService.registerReceiverWithFeature(ActivityManagerService.java:13927)

从报错日志看,MainActivity 注册广播后,ContextImpl.registerReceiverInternal 之后就报错了;

难道报错信息在 ContextImpl.java 代码里面?

查看 ContextImpl.java 源码,并没有发现报错判断和相关关键字,所以是在往下的逻辑进行的报错。

其实就是 IActivityManager的实现类 ActivityManagerService 里面报错的:

release\frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java

public Intent registerReceiverWithFeature(... IIntentReceiver receiver,
IntentFilter filter, String permission, int userId, int flags) {
			//(1)判断是否设置了RECEIVER_EXPORTED 或者 RECEIVER_NOT_EXPORTED 的flag
            final boolean explicitExportStateDefined =
                    (flags & (Context.RECEIVER_EXPORTED | Context.RECEIVER_NOT_EXPORTED)) != 0;
			//(2)动态接收广播,这里有判断 callingUid 应用等级,可能是系统应用没有报错的原因
            boolean requireExplicitFlagForDynamicReceivers = CompatChanges.isChangeEnabled(
                    DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED, callingUid);

            if (!onlyProtectedBroadcasts) { //(3)非保护广播
                if (receiver == null && !explicitExportStateDefined) {
                    // sticky broadcast, no flag specified (flag isn't required)
                    flags |= Context.RECEIVER_EXPORTED;
                } else if (requireExplicitFlagForDynamicReceivers && !explicitExportStateDefined) {//(4)未设置flag和动态注册的广播---抛出异常
                    throw new SecurityException(
                            callerPackage + ": One of RECEIVER_EXPORTED or "
                                    + "RECEIVER_NOT_EXPORTED should be specified when a receiver "
                                    + "isn't being registered exclusively for system broadcasts");
                    // Assume default behavior-- flag check is not enforced
                } else if (!requireExplicitFlagForDynamicReceivers && (
                        (flags & Context.RECEIVER_NOT_EXPORTED) == 0)) {
                    // Change is not enabled, assume exported unless otherwise specified.
                    flags |= Context.RECEIVER_EXPORTED;
                }
            } else if ((flags & Context.RECEIVER_NOT_EXPORTED) == 0) {
                flags |= Context.RECEIVER_EXPORTED;
            }
        }

}

Android14注册广播报错是在 ActivityManagerService.java 里面一些列判断之后抛出异常报错的。

四、其他

1、Android14 registerReceiver注册广播报错总结

普通应用需要再最后添加参数Context.RECEIVER_EXPORTED 或者 Context.RECEIVER_NOT_EXPORTED;

Android13 或者更低版本测试了并没有这个问题。

其实 Android13 的ActivityManagerService.java也是有大致的流程,也会抛出那个异常过程,

至于为啥没有报错,应该是具体判断里面的流程中,某个属性值的返回有差异所以没进入到抛出异常的过程。

2、Android13 开始AndroidManifest.xml里面的四大组件都是要定义exported属性的,否则会编译报错

activity、service、receiver、provider 这个四大组件都是必须设置 exported 属性,否则会报错

        <activity android:name=".MainActivity" android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

即使是Android Studio新建的项目,activity也要添加exported属性,否则编译错误。

3、Context.RECEIVER_EXPORTED 和Context.RECEIVER_NOT_EXPORTED作用?

从字面意思看:EXPORTED表示外部应用,NOT_EXPORTED表示非外部应用,即本应用内。

所以Context.RECEIVER_EXPORTED表示可以接收外部的广播,

Context.RECEIVER_NOT_EXPORTED表示只能接收应用内的广播?

从目前实际测试效果看,并非如此。

测试系统自动广播和app自定义广播,demo示例接收广播,Action代码:

IntentFilter mFilter = new IntentFilter();
mFilter.addAction("action.mytest"); //自定义action
mFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);//网络变化广播
mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); //wifi开关广播
mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
mFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
registerReceiver(mReceiver1, mFilter, Context.RECEIVER_EXPORTED);

监听广播不管设置Context.RECEIVER_EXPORTED还是Context.RECEIVER_NOT_EXPORTED,

都是可以接收到网络变化的广播和其他应用自定义发出的广播。

所以从实际来看:

Context.RECEIVER_EXPORTED和Context.RECEIVER_NOT_EXPORTED其实没啥区别,效果是一样的。

也就是说 Context.RECEIVER_NOT_EXPORTED 限制只接受应用内广播,并不会起作用。

但是最好还是都设置成 Context.RECEIVER_EXPORTED ,因为我看了 ActivityManagerService.java 的源码,

发现 Context.RECEIVER_NOT_EXPORTED 的情况有更多判断,甚至有抛出异常的可能!

Android 14 ActivityManagerService.java:

public class ActivityManagerService extends IActivityManager.Stub {

...
    public Intent registerReceiverWithFeature(...IntentFilter filter) {
        enforceNotIsolatedCaller("registerReceiver");
        ...
        }


/* package */ void enforceNotIsolatedCaller(String caller) {
        if (UserHandle.isIsolated(Binder.getCallingUid())) { //Isolated 表示远程的,外部的
            throw new SecurityException("Isolated process not allowed to call " + caller);
        }
    }
}

从目前情况看,并不会抛出这个异常(外部应用不允许监听)。

Android13 ActivityManagerService.java代码中,

设置了Context.RECEIVER_NOT_EXPORTED 才会进入判断 enforceNotIsolatedCaller :

 if ((flags & Context.RECEIVER_NOT_EXPORTED) != 0) {
            enforceNotIsolatedCaller("registerReceiver");
        }

其实上面代码只是分析了Android14报错,但是并没有分析Android13 的代码为啥不会报错,

具体是哪里的差异,在源码中并未看到,有兴趣的同学自己可以进行研究看看。

4、ActivityManagerService.java 的源码

Android13 ActivityManagerService.java:

http://aospxref.com/android-13.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

Android14 ActivityManagerService.java:

http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

Android 系统其他代码也可以使用网址查看:http://aospxref.com 。

寄语:明天五一,劳动节快乐。

评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

峥嵘life

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值