赞
踩
众所周知,从 Android 12 开始,使用了 TargetSDK 31 之后,四大组件如果使用了 intent-filter,但是没显性质配置 exported App 将会无法安装,甚至编译不通过。
比如启动的 Activity 就需要设置 exported 为 true,至于其他组件是否设置为 true 则看它是否需要被其它应用调用。
然而这个事情的状态是这样的:
如果出现问题的 AndroidManifest 文件是您本地的,那手动修改即可;
但如果出现问题的是第三方远程依赖,并且对方并没有提供源码和更新,您就无法直接修改;
如果第三方依赖太多,查找哪些出了问题十分费时费力。
这块网上各个大佬之前都是采取脚本的方式解决的,原理其实也可简单,就是在打包过程中检索所有没有设置 exported 的组件,给他们动态配置上 exported,这里有个特殊需要注意的是,因为启动 Activity 默认就是需要被 Launcher 打开的,所以 "android.intent.action.MAIN" 需要 exported 设置为 true 。
脚本如下:
以下脚本经过测试最高可到支持的版本: 「gradle:4.0.0 & gradle-6.1.1-all.zip」
/**
* 修改 Android 12 因为 exported 的构建问题
*/
- android.applicationVariants.all { variant ->
- variant.outputs.all { output ->
- output.processResources.doFirst { pm ->
- String manifestPath = output.processResources.manifestFile
- def manifestFile = new File(manifestPath)
- def xml = new XmlParser(false, true).parse(manifestFile)
- def exportedTag = "android:exported"
- ///指定 space
- def androidSpace = new groovy.xml.Namespace('http://schemas.android.com/apk/res/android', 'android')
-
- def nodes = xml.application[0].'*'.findAll {
- //挑选要修改的节点,没有指定的 exported 的才需要增加
- (it.name() == 'activity' || it.name() == 'receiver' || it.name() == 'service') && it.attribute(androidSpace.exported) == null
-
- }
- ///添加 exported,默认 false
- nodes.each {
- def isMain = false
- it.each {
- if (it.name() == "intent-filter") {
- it.each {
- if (it.name() == "action") {
- if (it.attributes().get(androidSpace.name) == "android.intent.action.MAIN") {
- isMain = true
- println("......................MAIN FOUND......................")
- }
- }
- }
- }
- }
- it.attributes().put(exportedTag, "${isMain}")
- }
-
- PrintWriter pw = new PrintWriter(manifestFile)
- pw.write(groovy.xml.XmlUtil.serialize(xml))
- pw.close()
- }
- }
-
- }
com.android.tools.build:gradle:4.0.0 以上版本
以下脚本经过测试支持的版本: 「gradle:4.1.0 & gradle-6.5.1-all.zip」
/**
* 修改 Android 12 因为 exported 的构建问题
*/
- android.applicationVariants.all { variant ->
- variant.outputs.each { output ->
- def processManifest = output.getProcessManifestProvider().get()
- processManifest.doLast { task ->
- def outputDir = task.multiApkManifestOutputDirectory
- File outputDirectory
- if (outputDir instanceof File) {
- outputDirectory = outputDir
- } else {
- outputDirectory = outputDir.get().asFile
- }
- File manifestOutFile = file("$outputDirectory/AndroidManifest.xml")
- println("----------- ${manifestOutFile} ----------- ")
-
- if (manifestOutFile.exists() && manifestOutFile.canRead() && manifestOutFile.canWrite()) {
- def manifestFile = manifestOutFile
- ///这里第二个参数是 false ,所以 namespace 是展开的,所以下面不能用 androidSpace,而是用 nameTag
- def xml = new XmlParser(false, false).parse(manifestFile)
- def exportedTag = "android:exported"
- def nameTag = "android:name"
- ///指定 space
- //def androidSpace = new groovy.xml.Namespace('http://schemas.android.com/apk/res/android', 'android')
-
- def nodes = xml.application[0].'*'.findAll {
- //挑选要修改的节点,没有指定的 exported 的才需要增加
- //如果 exportedTag 拿不到可以尝试 it.attribute(androidSpace.exported)
- (it.name() == 'activity' || it.name() == 'receiver' || it.name() == 'service') && it.attribute(exportedTag) == null
-
- }
- ///添加 exported,默认 false
- nodes.each {
- def isMain = false
- it.each {
- if (it.name() == "intent-filter") {
- it.each {
- if (it.name() == "action") {
- //如果 nameTag 拿不到可以尝试 it.attribute(androidSpace.name)
- if (it.attributes().get(nameTag) == "android.intent.action.MAIN") {
- isMain = true
- println("......................MAIN FOUND......................")
- }
- }
- }
- }
- }
- it.attributes().put(exportedTag, "${isMain}")
- }
-
- PrintWriter pw = new PrintWriter(manifestFile)
- pw.write(groovy.xml.XmlUtil.serialize(xml))
- pw.close()
-
- }
-
- }
- }
- }
这段脚本你可以直接放到 app/build.gradle 下执行,也可以单独放到一个 gradle 文件之后 apply 引入
但是问题是到了gradle 4.2+、gradle-6.7.1-all.zip 的时候,这些脚本就不能用了,原因「从 gradle:4.2.0 & gradle-6.7.1-all.zip 开始,TargetSDK 31 下脚本会有异常,因为在 processDebugMainManifest (带有Main) 的阶段,会直接扫描依赖库的 AndroidManifest.xml 然后抛出直接报错,从而进不去 processDebugManifest 任务阶段就编译停止,所以实际上脚本并没有成功运行 ,针对这个,额,又有个大佬做了这块的处理,下面贴上大佬的解决方法:
manifest-exported-plugin
大佬的插件叫这个名字,使用方法:
依赖方式
添加jitpack仓库
build.gradle
Gradle7.0 以下
- buildscript {
- repositories {
- // ...
- maven { url 'https://jitpack.io' }
- }
- }
Gradle7.0+,并且已经对依赖方式进行过调整,则可能需要添加到如下位置:settings.gradle
- pluginManagement {
- repositories {
- //...
- maven { url 'https://jitpack.io' }
- }
- }
- Gradle
- dependencies {
- classpath 'com.github.xiachufang:manifest-exported-plugin:1.1.1'
- }
使用方式
添加插件
在主app Model中添加:
- apply plugin: 'com.xiachufang.manifest.exported'
- 或
- plugins {
- id 'com.xiachufang.manifest.exported'
- }
- build.gradle
-
- apply plugin: 'com.xiachufang.manifest.exported'
- ...
-
- exported {
- // 是否写入主Model
- enableMainManifest false
- // 规则
- ruleFile new File("$projectDir/xxx.json")
- // 输出文件,默认-app/build/exported/outManifestLog.md
- outPutFile null
- }
配置参数说明
enableMainManifest
是否对主 model-AndroidManifest 进行修改
对于主model,属于业务可控的,建议开发者自行调整。
插件默认不会对主 model-AndroidManifest 进行修改,如果发现可用匹配上述规则的,即会进行修正。
开发者可根据日志中的提示,进行修改。
注意:这个操作会对Manifest的展示样式造成一定影响,建议一般不要打开。
outPutFile
日志输出目录,默认 app/build/exported/outManifest.md
ruleFile
具体的规则json文件, 格式如下:
- {
- "actionRules": [
- "android.intent.action.MAIN"
- ],
- "whiteNames": [],
- "blackPackages": [],
- "blackNames": [],
- "blackIgnores": []
- }
actionRules
默认判断规则,用于当前没有配置 exported 的修改逻辑,如果当前存在exported,则跳过。
具体判断逻辑:
如果 intent-filter - action 对应的 android:name 与 actionRules 中任意一条匹配,则将 exported 修改为 true ,否则为false 。
whiteNames
白名单类名,如果遇到此类,并且使用了 intent-filter ,则会将 exported 修改为 true
blackPackages
黑名单 包名合集,对于此包名下的类,如果使用了 intent-filter ,则会将 exported 直接修改为 false
blackNames
黑名单 类名合集,如果遇到此合集中的类,并且使用了 intent-filter ,则会将 exported 直接修改为 true 。判断逻辑会与上面 blackPackages 一起判断,两者满足其一即可。
blackIgnores
黑名单 下要忽视的类,在黑名单判断时,如果遇到此类,则使用默认规则 actionRules 判断。
注意的是:
对于主model下的 manifest ,默认不进行适配(可开关 enableManifest ),会通过日志进行输出,建议大家自行对比调整。
为什么默认不对主 model 进行适配?
对于业务 model ,我们建议开发者自行适配,这属于我们可控范围,适配来说主要就是为了不可控的,即第三方 aar
修改之后,会影响原有的 manifest 代码风格,需要重新格式化一下,相比默认的,增加了不少空格,暂时不知道怎么解决。
原理简述
在ProgressXXXMainManifest任务之前进行插入,通过对manifest进行修改,从而实现exported的适配。
通常默认情况下,会在build文件夹下生成个export文件夹,其中有个outManifestLog.md,这个就等于是个插件执行日志之类的东西,嗯,就这样,反正挺6的就是
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。