一、Manifest 是什么

AndroidManifest.xml 位于 src/main/ 下,是每个 Android 应用必须有且唯一的声明文件。它在应用还没运行时就被系统读取,用来回答几个关键问题:

  • 这个应用有哪些组件(Activity / Service / BroadcastReceiver / ContentProvider)?
  • 它需要哪些权限(网络、相机、定位……)?
  • 哪个 Activity 是启动入口
  • 哪些组件允许被其他应用调用
  • 兼容哪些设备和系统版本?

一个最小示例:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.HelloWorld">

        <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
            android:name=".DetailsActivity"
            android:exported="false" />
    </application>
</manifest>

二、核心元素逐个拆

1. <manifest> 根元素

<manifest xmlns:android="..." xmlns:tools="...">
  • xmlns:android:Android 属性命名空间,必需
  • xmlns:tools:工具命名空间,用于 Manifest 合并控制、抑制 Lint 警告等(仅构建期生效,不打包进 APK)。

AGP 7+ 之后,包名通过 Gradle 的 namespace 声明,不再写在 Manifest 顶部的 package 属性里。老项目里残留的 package="..." 可以删掉。

2. <application> 应用级配置

常用属性:

属性 作用
android:icon / android:roundIcon 应用图标
android:label 应用名称
android:theme 全局默认主题
android:supportsRtl 是否支持从右到左布局(阿拉伯语等)
android:allowBackup 是否允许系统自动备份用户数据
android:networkSecurityConfig 网络安全配置(如禁止明文 HTTP)
android:usesCleartextTraffic 是否允许明文流量

3. 组件声明与 android:exported(重点)

四大组件都必须在 Manifest 里声明(动态注册的广播除外)。其中最关键、也最容易踩坑的是 android:exported

  • true:组件可被外部应用启动或访问。
  • false:仅本应用内部可用。

从 Android 12(API 31)起:只要组件包含 <intent-filter>,就必须显式声明 android:exported,否则应用直接安装失败。

这是一项强制安全要求。很多人升级 targetSdk 后第一个遇到的就是这个"装不上"的报错。

原则:默认 false,只有确实需要被外部调用的组件才设 true。一个含 intent-filter 却误设 exported="true" 的内部组件,可能被恶意应用利用,造成越权调用。

4. Intent Filter 与启动入口

<intent-filter> 决定组件能响应哪些 Intent:

  • 启动入口:必须同时含 MAIN action 和 LAUNCHER category,应用才会出现在桌面。
  • 深度链接:用 VIEW + <data> 声明 scheme/host/path,让网页或其他应用能直接跳到你的指定页面。
<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="https" android:host="example.com" android:pathPrefix="/product"/>
    </intent-filter>
</activity>

延伸阅读:Intent 与组件通信

5. 权限与特性

<!-- 声明需要的权限 -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

<!-- 声明硬件特性,required=false 表示没有也能装 -->
<uses-feature android:name="android.hardware.camera" android:required="false"/>

Manifest 里声明权限只是"申请名额"。从 Android 6.0(API 23)起,危险权限(相机、定位、读写存储等)还必须在运行时动态请求,光在 Manifest 里写是不够的。

6. 包可见性(Android 11+)

从 Android 11(API 30)开始,出于隐私考虑,你的应用默认看不到设备上装了哪些其他应用。如果你要查询、跳转到特定应用,需要用 <queries> 声明:

<queries>
    <!-- 我要查询能处理"分享图片"的应用 -->
    <intent>
        <action android:name="android.intent.action.SEND"/>
        <data android:mimeType="image/*" />
    </intent>
    <!-- 我要查询某个特定包 -->
    <package android:name="com.example.otherapp"/>
</queries>

忘了声明 <queries>,会出现"在低版本能跳转、升到 Android 11 就跳不动"的诡异 bug。


三、备份与数据迁移规则

android:allowBackup="true" 很方便,但也意味着用户数据可能被备份到云端。敏感数据(令牌、密钥、PII)必须排除。

Auto Backup(Android 6.0+)

通过 android:fullBackupContent="@xml/backup_rules" 指定规则:

<!-- res/xml/backup_rules.xml -->
<full-backup-content>
    <exclude domain="sharedpref" path="debug_prefs.xml"/>
    <include domain="file" path="user/"/>
</full-backup-content>

数据提取规则(Android 12+)

android:dataExtractionRules="@xml/data_extraction_rules",分别控制"云备份"和"设备间迁移":

<!-- res/xml/data_extraction_rules.xml -->
<data-extraction-rules>
    <cloud-backup>
        <exclude domain="database" path="cache.db"/>
    </cloud-backup>
    <device-transfer>
        <include domain="sharedpref" path="user_prefs.xml"/>
    </device-transfer>
</data-extraction-rules>

注意:永远不要把密钥、令牌、加密材料放进可备份目录。 含个人信息的日志和缓存默认应排除备份,否则可能造成隐私泄露。


四、Manifest 合并与 tools: 命名空间

最终的 Manifest 是"主工程 + 所有依赖库的 Manifest"合并而成的。冲突时用 tools: 干预:

属性 作用
tools:node="remove" 移除某个从库里合并进来的节点
tools:replace="label,icon" 用本工程的值覆盖库的同名属性
tools:ignore="..." 抑制特定 Lint 警告

排查合并冲突:打开 AndroidManifest.xml,切到底部 Merged Manifest 标签页,能看到每个属性最终来自哪个文件。


参考来源:

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐