- 移动开发
- 质量保障
- 构建工具
【免费下载链接】booster
🚀Optimizer for mobile applications
导读:在 Android 多模块工程中,权限往往分散声明在各个依赖 AAR 的
AndroidManifest.xml里,手工汇总极易遗漏。booster 的booster-task-list-permission模块通过在构建期解析合并后的 Manifest,将整个工程实际生效的 Android 权限(含uses-permission、uses-permission-sdk-23、uses-permission-sdk-m三种声明形式)一次性打印出来。读完本文,你将掌握该模块的安装接入方式、listPermissions与按变体粒度任务的用法,并理解其从 Gradle 任务注册到 SAX 解析的完整实现原理。
模块定位:回答"我的 APK 里到底申请了哪些权限"
booster-task-list-permission是 Booster 任务体系中的一个小而实用的模块,其职责(见 模块 README)是:
显示本工程依赖的 AAR 中使用的 Android 权限列表。
它解决的实际问题是:主工程AndroidManifest.xml中写的权限只是最终权限的"入口",构建时 Gradle 的 Manifest Merger 会把所有直接/传递依赖 AAR 中声明的<uses-permission>一并合并进最终产物。因此在提审、合规自查或排查权限滥用时,需要一份基于合并后 Manifest的权限清单,而该模块正是以这一真实产物为数据源,而不是简单扫描主模块源码。
该模块在仓库中与两个同族模块互为对照:booster-task-list-artifact(列出工程 artifacts)、booster-task-list-shared-library(列出 native 动态库)。三者共同展示了 Booster 通过 Gradle task 扩展构建信息可视化的思路。
快速开始:一个 Gradle 任务拿到全部权限
接入方式与 Booster 其他模块一致:将该模块加入工程的 buildscript classpath 后,在工程根目录执行:
./gradlew listPermissions执行后任务会把权限清单直接打印到控制台,输出形如:
- android.permission.ACCESS_FINE_LOCATION - android.permission.INTERNET - android.permission.READ_EXTERNAL_STORAGE每条权限前带-前缀,便于直接复制到文档或提审清单中。如果合并后的 Manifest 中没有任何权限声明,任务将静默结束、不输出任何条目(源码中通过ifNotEmpty仅在集合非空时才遍历打印,见 ListPermission.kt)。
按变体粒度查看:list<Variant>Permissions
与单一入口任务listPermissions配套,该模块还会为工程中的每一个 Android Variant注册一个专属任务,任务名遵循list${VariantName}Permissions命名规则(源码见 ListPermissionVariantProcessor.kt)。
| 任务名 | 作用范围 | 说明 |
|---|---|---|
listPermissions | 全部分支 | 聚合任务,依赖于所有变体任务 |
listDebugPermissions | debug变体 | 仅打印 debug 构建下合并 Manifest 的权限 |
listReleasePermissions | release变体 | 仅打印 release 构建下合并 Manifest 的权限 |
list<VariantName>Permissions | 任意自定义变体 | flavor 场景下按flavor + buildType维度生成,例如listChinaDebugPermissions |
这里有一个值得注意的工程实践:listPermissions任务名是硬编码常量(private const val TASK_NAME = "listPermissions"),而变体任务名由variant.name.capitalized()动态拼装,capitalized来自 Booster 的booster-kotlinx扩展库。两个任务都注册在BOOSTER任务分组下(it.group = BOOSTER),执行./gradlew tasks时会在 Booster 分组中看到它们,并带有 "List the permissions declared in AndroidManifest.xml" 的描述信息。
背后的机制:VariantProcessor SPI 与任务注册
该模块并非在 plugin 里写死逻辑,而是遵循 Booster 的任务扩展规范,通过Java SPI将自身挂载到 Gradle 构建生命周期中:
- 实现
VariantProcessor接口(定义见 VariantProcessor.kt),该接口是 Booster 的 task 管理 SPI,提供beforeProcess(variantBuilder)与process(variant)两个扩展点; - 使用
@AutoService(VariantProcessor::class)注解标注ListPermissionVariantProcessor(ListPermissionVariantProcessor.kt),编译期自动生成META-INF/services注册文件; - Booster 插件在
apply时通过 ServiceLoader.kt 的loadVariantProcessors(project)从 buildscript classloader 加载全部VariantProcessor实例; - 随后在 BoosterPlugin.kt 的
onVariants回调中,对每个 variant 调用processor.process(variant),此时每个变体的任务便完成了注册。
在process(variant)内部,注册逻辑有一个优雅的"先查后建"处理:先尝试tasks.named(TASK_NAME)查找是否已存在聚合任务,若抛出UnknownTaskException(即尚未注册)才tasks.register创建;随后为当前变体注册ListPermission类型的任务,并通过listPermissions.dependsOn(it)建立聚合任务依赖变体任务的关系,保证执行listPermissions时所有变体的权限都会依次被收集(源码见 ListPermissionVariantProcessor.kt)。
任务的数据源通过 AGP 的Variant Artifacts API注入:
it.mergedManifest.set(variant.artifacts.get(SingleArtifact.MERGED_MANIFEST))即直接消费 AGP 提供的MERGED_MANIFEST产物,该产物正是最终合并了所有依赖 AAR 声明的 Manifest 文件。这意味着无论权限来自主工程还是某个传递依赖的 AAR,只要最终进入了合并结果,就会被统计到,这正是该模块比"扫描主模块源码"更可靠的原因。
核心实现:SAX 流式解析合并后的 Manifest
任务本体ListPermission继承 Gradle 的DefaultTask,通过@get:InputFile声明mergedManifest作为输入文件(ListPermission.kt),并在此基础上配置了一个非验证、支持命名空间感知的 SAXParserFactory:
factory.isXIncludeAware = false factory.isNamespaceAware = true factory.setFeature("http://xml.org/sax/features/namespace-prefixes", true) factory.setFeature("http://xml.org/sax/features/xmlns-uris", true) factory.isValidating = false这些配置的含义与必要性:
isNamespaceAware = true:AndroidManifest 使用android:命名空间,必须开启命名空间感知才能正确解析android:name属性;namespace-prefixes与xmlns-uris特性:确保 SAX 回调中能同时拿到qName与前缀映射,从而可靠地读取命名空间属性;isValidating = false与isXIncludeAware = false:关闭 DTD 校验与 XInclude 处理,避免无谓的校验开销和潜在的安全面,保证解析只做"读取属性"这一件事。
@TaskAction方法打开 Manifest 输入流,交给PermissionUsageHandler进行流式解析(内存友好,不将整个 XML DOM 载入),解析完成后对权限集合排序并逐行打印。
权限收集规则:三种 Manifest 声明形式全覆盖
解析的核心逻辑集中在 PermissionUsageHandler.kt 中,它继承DefaultHandler,重写startElement并在遇到三种元素时收集android:name属性:
| Manifest 元素 | 语义 | 是否被收集 |
|---|---|---|
uses-permission | 无条件声明权限,安装即生效 | ✅ |
uses-permission-sdk-23 | 仅 Android 6.0(API 23)及以上生效,低版本忽略 | ✅ |
uses-permission-sdk-m | 同uses-permission-sdk-23(旧命名,M = Marshmallow) | ✅ |
收集结果保存在mutableSetOf<String>()中,天然去重——同一个权限无论被多少 AAR 重复声明,最终清单中只会出现一次;由于使用Set,最终输出顺序不确定,因此任务在打印前会调用.sorted()保证输出稳定可读。
值得强调的是,该 Handler 只关心权限声明(uses-permission*标签),并不解析<permission>(自定义权限的定义)或<uses-feature>(功能需求),这是模块定位决定的:它回答的是"申请了哪些系统权限",而非"定义了哪些自定义权限"。
源码地图与进阶扩展
想深入阅读或二次开发,可对照以下文件:
- 模块 README —— 模块用途与快速开始命令;
- ListPermissionVariantProcessor.kt —— 任务注册、命名规则、聚合依赖关系;
- ListPermission.kt —— Gradle task 定义、SAXFactory 配置与输出逻辑;
- PermissionUsageHandler.kt —— 权限元素提取与去重;
- VariantProcessor.kt —— 任务扩展 SPI 接口定义,模块通过它接入构建生命周期。
如果你需要自定义类似的"清单类"任务(例如列出 AAR 中的 Activity、Service 或广播接收器),完全可以照搬本模块的骨架:实现VariantProcessor并通过@AutoService注册,用SingleArtifact.MERGED_MANIFEST作为输入,再写一个针对目标元素的 SAX Handler 即可。Booster 官方推荐的扩展流程与此一致(实现VariantProcessor接口并以 SPI 注册,见 CLAUDE.md)。
小结
booster-task-list-permission用最简的接入成本(一条 Gradle 命令)解决了 Android 工程权限清单统计的常见痛点:以 AGP 合并后的 Manifest 为唯一数据源,借助 SAX 流式解析覆盖三种权限声明形式,通过 VariantProcessor SPI 与任务依赖机制同时提供全局视图(listPermissions)和变体级视图(list<Variant>Permissions)。无论是日常自查、提审准备,还是对依赖 AAR 做合规审计,它都能成为构建流程中一个随时可用的信息工具。
- 移动开发
- 质量保障
- 构建工具
【免费下载链接】booster
🚀Optimizer for mobile applications
相关推荐
gogcli 实战:用 `gog classroom coursework list` 高效列出 Google Classroom 作业清单
gogcli 实战:用 gog classroom coursework list 高效列出 Google Classroom 作业清单 本文基于 gogcli
Laravel权限管理终极指南:批量导入导出权限的高效解决方案
Laravel权限管理终极指南:批量导入导出权限的高效解决方案 Laravel permission 是 Laravel 生态中最受欢迎的权限管理包之一,它为开
后端认证鉴权Android 官方培训课程:在应用清单中声明权限的完整指南
Android 官方培训课程:在应用清单中声明权限的完整指南 本指南围绕 Android 官方培训课程中的 声明权限(Declaring Permissions
文档教程移动开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考