为什么您的应用在 Android 12 上突然无法启动?
\n\n您将 targetSdkVersion 更新到 31 或更高版本,构建了 APK,启动时——却遇到了崩溃,并显示一条神秘消息:
android:exported needs to be explicitly specified for <activity>. \nApps targeting Android 12 and higher are required to specify \nan explicit value for `android:exported` when the corresponding \ncomponent has an intent filter defined.\n\n熟悉吗?如果您浏览过 Stack Overflow,您会发现这个问题已经积累了数千次浏览。开发者们惊慌失措,用户们则困惑不解。但不要急着删除项目。在本文中,我们将弄清楚 android:exported 到底是什么,为什么 Google 收紧了要求,以及如何用 Kotlin 快速修复这个错误。我们开始吧!
什么是 android:exported,它有什么用?
\n\nandroid:exported 是 AndroidManifest.xml 中的一个属性,它控制其他应用(或系统本身)是否可以启动您的组件(Activity、Service、BroadcastReceiver 或 ContentProvider)。如果该属性设置为 true——则该组件可从外部访问。如果设置为 false——则只能在您的应用内部访问。
在 Android 12(API 31)之前,此属性是可选的。如果您没有指定它,系统会自行决定该组件是否可访问:
\n- \n
- 如果 Activity 有
<intent-filter>——系统会认为exported = true。 \n - 如果没有过滤器——则
exported = false。 \n
但这种“自动”行为是一个安全漏洞。想象一下:您编写了一个用于登录应用的 Activity,却不小心添加了 intent-filter。任何第三方应用都可以调用您的 Activity 并窃取令牌。因此 Google 决定:从 Android 12 开始,对于任何带有 <intent-filter> 的组件,您必须显式指定 android:exported。
错误在实践中是什么样子的?
\n\n假设您有一个简单的清单文件:
\n\n<activity android:name=".MainActivity">\n <intent-filter>\n <action android:name="android.intent.action.MAIN" />\n <category android:name="android.intent.category.LAUNCHER" />\n </intent-filter>\n</activity>\n\n如果 targetSdkVersion = 31 或更高,您在编译或启动时会遇到错误。解决方案是添加 android:exported:
<activity \n android:name=".MainActivity"\n android:exported="true">\n <intent-filter>\n <action android:name="android.intent.action.MAIN" />\n <category android:name="android.intent.category.LAUNCHER" />\n </intent-filter>\n</activity>\n\n对于从桌面启动的主 Activity,exported 必须为 true——否则启动器将无法打开它。
如何正确修复:分步指南
\n\n步骤 1. 打开 AndroidManifest.xml
\n找到所有带有 <intent-filter> 的 <activity>、<service>、<receiver> 和 <provider>。为每一个添加 android:exported。
步骤 2. 确定属性值
\n这是一个简单的规则:
\n- \n
- true——如果组件应该可以从其他应用访问(例如,用于通过 Google 登录的 Activity、用于系统事件的 BroadcastReceiver、带有 LAUNCHER 过滤器的主 Activity)。 \n
- false——如果组件仅在您的应用内部使用(例如,通过您自己代码中的按钮点击打开的 Activity)。 \n
步骤 3. 不同场景的示例
\n\n场景 1:主 Activity(启动器)
\n