Android12⾃动适配exported深⼊解析避坑
众所周知,从 Android 12 开始,使⽤了 TargetSDK 31 之后,四⼤组件如果使⽤了 intent-filter, 但是没显性质配置 exported App 将会⽆法安装,甚⾄编译不通过。
⽐如启动的 Activity 就需要设置 exported 为 true ,⾄于其他组件是否设置为 true 则看它是否需要被其它应⽤调⽤。
然⽽这个事情的状态是这样的:
如果出现问题的 AndroidManifest ⽂件是你本地的,那⼿动修改即可;
但如果出现问题的是第三⽅远程依赖,并且对⽅并没有提供源码和更新,你就⽆法直接修改;
如果第三⽅依赖太多,查找哪些出了问题⼗分费时费⼒。
脚本
所以在之前的 ⼀⽂中提供了⼀套脚本,专门⽤于适配 Android 12 下缺少 android:exported ⽆法编译或者安装的问题,但是在这期间收到了不少问题反馈:
ls.build:gradle:4.0.0 以及其下版本
⼀下脚本经过测试最⾼可到⽀持的版本: gradle:4.0.0 & gradle-6.1.1-all.zip
ls.build:gradle:4.0.0 以上版本
以下脚本经过测试⽀持的版本: gradle:4.1.0 & gradle-6.5.1-all.zip 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 XmlParr(fal, true).par(manifestFile) def exportedTag = "android:exported" ///指定 space def androidSpace = l.Namespace('/apk/res/android', 'android') def nodes = xml.application[0].'*'.findAll { //挑选要修改的节点,没有指定的 exported 的才需要增加 (it.name() == 'activity' || it.name() == 'receiver' || it.name() == 'rvice') && it.ported) == null } ///添加 exported ,默认 fal nodes.each { def isMain = fal it.each { if (it.name() == "intent-filter") { it.each { if (it.name() == "action") { if (it.attributes().get(androidSpace.name) == "android.intent.action.MAIN") { isMa
in = true println("......................") } } } } } it.attributes().put(exportedTag, "${isMain}") } PrintWriter pw = new PrintWriter(manifestFile) pw.l.XmlUtil.rialize(xml)) pw.clo() } }}
4
5
6
7
8
9
10
11
12
13
14数学大全
15
16
17
18
19
20
21
22
23
24
25
26
27自然垄断行业
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
android.applicationVariants.all { variant -> variant.outputs.each { output -> def processManifest
= ProcessManifestProvider().get() processManifest.doLast { task -> def outputDir = task.multiApkManifestOutputDirectory File outputDirectory if (outputDir instanceof File) { outputDirectory = outputDir } el { outputDirectory = ().asFile } File manifestOutFile = file("$l") println("----------- ${manifestOutFile} ----------- ") if (ists() && manifestOutFile.canRead() && manifestOutFile.canWrite()) { def manifestFile = manifestOutFile ///这⾥第⼆个参数是 fal ,所以 namespace 是展开的,所以下⾯不能⽤ androidSpace ,⽽是⽤ nameTag def xml = new XmlParr(fal, fal).par(manifestFile) def exportedTag = "android:exported" def nameTag = "android:name" ///指定 space //def androidSpace = l.Namespace('/apk/res/android', 'android') def nodes = xml.application[0].'*'.findAll { //挑选要修改的节点,没有指定的 exported 的才需要增加 //如果 exportedTag 拿不到可以尝试 it.ported) (it.name() == 'activity' || it.name() == 'receiver' || it.name() == 'rvice') && it.attribute(exportedTag) == null } ///添加 exported ,默认 fal nodes.each { def isMain = fal 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("......................") } } } } } it.attributes().put(exportedTag, "${isMain}") } PrintWriter pw = new PrintWriter(manifestFile) pw.l.XmlUtil.rialize(xml)) pw.clo() } } }}
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
这段脚本你可以直接放到 adle 下执⾏,也可以单独放到⼀个 gradle ⽂件之后 apply 引⼊,它的作⽤就是:在打包过程中检索所有没有设置 exported 的组件,给他们动态配置上 exported ,这⾥有个特殊需要注意的是,因为启动 Activity 默认就是需要被 Launcher 打开的,所以 "android.intent.action.MAIN" 需要 exported 设置为 true 。(PS:更正规应该是⽤
LAUNCHER 类别,这⾥故意⽤ MAIN)⽽后综合问题,具体反馈的问题有 :
label 直接写死中⽂,不是引⽤ @string 导致的在 3.x 的版本可以正常运⾏,但不能打包 ;
XmlParr 类找不到,这个⾸先确定 AGP 版本和 Gradle 版本是否匹配,具体可见 ,另外可以通过 groovy.util.XmlParr 或者l.XmlParr 全路径指定使⽤ ,如果是 gradle ⽂件⾥显⽰红⾊并不会影响运⾏;运⾏报错提⽰ android:exported needs,这个就是今天需要输⼊聊的;
基于上述脚本测试和反馈,⽬前的结论是:
从 gradle:4.2.0 & gradle-6.7.1-all.zip 开始,TargetSDK 31 下脚本会有异常,因为在 processDebugMainManifest (带有Main) 的阶段,会直接扫描依赖库的 l 然后抛出直接报错,从⽽进不去 processDebugManifest 任务阶段就编译停⽌,所以实际上脚本并没有成功运⾏。
所以此时拿不到 mergerd_manifest 下的⽂件,因为 mergerd_manifest 下 l 也还没创建成功,没办法进⼊ task ,也就是该脚本⽬前只能针对 gradle:4.1.0 以及其下版本安装 apk 到 Android12 的机器上, 有 intent-filter 但没有 exoprted 的适配问题,基于这个问题,不知道各位是否有什么好的建议?
新脚本
⽽⽬前基于这个问题,这⾥提供了如下脚本,在 gradle:4.2.0 & gradle-6.7.1-all.zip 以及 7.0 的版本上,该脚本的作⽤是在运⾏时⾃动帮你打印出现问题的 aar 包依赖路径和组建名称:
Error: android:exported needs to be explicitly specified for <xxxx>. Apps targeting Android 12 and higher are required to specify an explicit value for `androi 1
如下图所⽰,因为⽬前官⽅如红⾊信息内容其实指向并不正确,容易误导问题⽅向,所以通过上述脚本打印,可以快速查找到问题所在的点,然后通过 tool:replace 临时解决。android.applicationVariants.all { variant -> variant.outputs.each { output -> //println("=============== ${BuildType().UpperCa()} ===============") //println("=============== ${FlavorName()} ===============") def vn if (FlavorName() != null && FlavorName() != "") { vn = variant.name; } el { if (BuildType().name == "relea") { vn = "Relea" } el { vn = "Debug" } } def taskName = "process${vn}MainManifest"; try { println("=============== taskName ${taskName} ===============") Tasks().getByName(taskName) } catch (Exception e) { return } ///你的⾃定义名字 Tasks().getByName(taskName).doFirst { //def method = it.getClass().getMethods() it.getManifests().getFiles().each { if (it.exists() && it.canR
ead()) { def manifestFile = it def exportedTag = "android:exported" def nameTag = "android:name" ///这⾥第⼆个参数是 fal ,所以 namespace 是展开的,所以下⾯不能⽤ androidSpace ,⽽是⽤ nameTag def xml = new XmlParr(fal, fal).par(manifestFile) if (xml.application != null && xml.application.size() > 0) { def nodes = xml.application[0].'*'.findAll { //挑选要修改的节点,没有指定的 exported 的才需要增加 //如果 exportedTag 拿不到可以尝试 it.ported) (it.name() == 'activity' || it.name() == 'receiver' || it.name() == 'rvice') && it.attribute(exportedTag) == null } if (nodes.application != null && nodes.application.size() > 0) { nodes.each { def t = it it.each { if (it.name() == "intent-filter") { println("$manifestFile \n .....................${t.attributes().get(nameTag)}......................") } } } } } } } } }}
1
2
3
4
5
6
7
胆虚
8
9
10
11
12
史学四大家
13
14
黑芝15
16
17
18
两斤有多重
19
20
21
22
23
24
25
26
27内侍省
28
29
qq直播间
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56