Skip to content

Android 12 的 android:exported 配置问题与解决方案

问题描述

当应用程序针对 Android 12(API 级别 31)或更高版本时,如果组件包含 Intent Filter 但没有显式声明 android:exported 属性,会出现编译错误:

"Manifest merger failed with multiple errors, see logs"

具体错误信息为:

android:exported needs to be explicitly specified for <activity>. 
Apps targeting Android 12 and higher are required to specify an explicit value 
for `android:exported` when the corresponding component has an intent filter defined.

这是 Android 12 引入的新安全要求,旨在防止无意中导出应用程序组件。

根本原因

从 Android 12 开始,任何包含 <intent-filter> 的 Activity、Service、BroadcastReceiver 或 ContentProvider 都必须显式声明 android:exported 属性,明确指示该组件是否可供其他应用程序访问。

解决方案

方法 1:为所有包含 Intent Filter 的组件添加 exported 属性

AndroidManifest.xml 中,为所有包含 <intent-filter> 的组件添加 android:exported 属性:

xml
<!-- 启动 Activity(通常需要设置为 true) -->
<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>

<!-- 其他 Activity(根据需求设置为 true 或 false) -->
<activity
    android:name=".OtherActivity"
    android:exported="false">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
    </intent-filter>
</activity>

<!-- Service 组件 -->
<service
    android:name=".MyService"
    android:exported="false"/>

<!-- BroadcastReceiver 组件 -->
<receiver 
    android:name=".MyReceiver"
    android:exported="false">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

方法 2:查找并修复第三方库的问题

如果错误来自第三方库,需要定位问题并覆盖其配置:

  1. 使用合并清单视图定位问题

    • 打开 AndroidManifest.xml
    • 点击底部的 "Merged Manifest" 标签页
    • 查找缺少 android:exported 属性的组件
  2. 在应用的清单文件中覆盖库的配置

xml
<!-- 覆盖第三方库的 Activity 配置 -->
<activity
    android:name="com.third.party.LibraryActivity"
    android:exported="false"
    tools:node="merge"
    tools:overrideLibrary="com.third.party.library" />

<!-- 覆盖第三方库的 Receiver 配置 -->
<receiver
    android:name="com.third.party.LibraryReceiver"
    android:exported="false"
    tools:node="merge"
    tools:overrideLibrary="com.third.party.library" />

方法 3:更新测试依赖库

某些测试库(如 androidx.test)可能导致此问题,更新到最新版本:

gradle
dependencies {
    androidTestImplementation 'androidx.test:core:1.4.0'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    androidTestImplementation "androidx.compose.ui:ui-test-junit4:1.3.3"
}

方法 4:临时降级 targetSdkVersion(不推荐)

如果急需发布应用但无法立即修复所有问题,可以临时降级:

gradle
android {
    compileSdkVersion 31
    defaultConfig {
        targetSdkVersion 30  // 临时降级,非长久之计
        // ...
    }
}

WARNING

降级 targetSdkVersion 只是临时解决方案,从 2022 年开始,Google Play 要求新应用必须针对 Android 12 或更高版本。

常见问题排查

1. 如何确定哪些组件需要修复?

使用以下方法之一:

  • 方法 A:查看构建日志中的详细错误信息
  • 方法 B:检查 app/build/intermediates/merged_manifest/ 下的合并清单文件
  • 方法 C:临时将 targetSdkVersion 设为 30 以生成合并清单,然后分析问题

2. exported 应该设置为 true 还是 false?

  • 设置为 true:组件可供其他应用访问(如主启动器 Activity)
  • 设置为 false:组件仅供应用内部使用(大多数情况)

TIP

遵循最小权限原则,只有在确实需要与其他应用交互时才将 exported 设为 true。

代码示例

以下是包含正确 exported 配置的完整示例:

xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.app">

    <application>
        <!-- 主启动器 Activity -->
        <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>

        <!-- 内部使用的 Activity -->
        <activity
            android:name=".InternalActivity"
            android:exported="false" />

        <!-- 处理 deep link 的 Activity -->
        <activity
            android:name=".DeepLinkActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="example" />
            </intent-filter>
        </activity>

        <!-- 内部 Service -->
        <service
            android:name=".MyService"
            android:exported="false" />

        <!-- BroadcastReceiver -->
        <receiver
            android:name=".MyReceiver"
            android:exported="false">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

总结

Android 12 的 exported 要求是一项重要的安全改进,确保了开发者必须明确声明组件的可见性。通过系统性地检查所有包含 Intent Filter 的组件并正确设置 exported 属性,可以顺利解决编译错误并提高应用的安全性。

INFO

始终优先考虑更新第三方库到最新版本,因为库的维护者通常会在新 Android 版本发布后及时修复兼容性问题。