背景
由于Google play的政策,提审aab的时候需要适配android12,适配android12最大的工作就是在AndroidManifesst.xml文件中声明的四大组件,都要显式声明exported字段。如果手动添加,在遇到第三方sdk时就会比较麻烦,所以只能使用gradle脚本自动添加,而网上给的gradle脚本例子都是只对apk生效,所以自己手动改造一下,以支持aab。
步骤
定义修改manifest文件的方法
遍历AndroidManifest.xml文件的每一个节点,识别到activity,receiver,service节点时,如果未声明exported字段则添加上去,入口类为true,其他默认为false
def fixManifest(File mainManifestFile) {
try {
def xml = new XmlParser(false, true).parse(mainManifestFile)
def exportedTag = "android:exported"
def androidSpace = new groovy.xml.Namespace('http://schemas.android.com/apk/res/android', 'android')
def nodes = xml.application[0].'*'.findAll {
(it.name() == 'activity' || it.name() == 'receiver' || it.name() == 'service') && it.attribute(androidSpace.exported) == null
}
nodes.each {
def exportedValue = false
it.each {
if (it.name() == "intent-filter") {
it.each {
if (it.name() == "action") {
if (it.attributes().get(androidSpace.name) == "android.intent.action.MAIN") {
exportedValue = true
}
}
}
}
}
it.attributes().put(exportedTag, "${exportedValue}")
}
PrintWriter pw = new PrintWriter(mainManifestFile)
pw.write(groovy.xml.XmlUtil.serialize(xml))
pw.close()
} catch (Exception e) {
}
}
打包流程中在特定task中插入代码段
- 打包debug/release apk时,会执行processDebugResources/processReleaseResources task将合并后的manifest打包进最终产物中,可以在gradle执行该task之前拦截,调用上面定义的方法修改manifest
- 打包debug/release aab时,会执行processDebugManifest/processReleaseManifest task将所有依赖的aar或library module中AndroidManifest.xml中的节点,合并到项目的AndroidManifest.xml中,可以在gradle执行该task之后拦截,调用上面定义的方法修改manifest
配置variant
获取打包的variant名称,以及打apk/bundle包时,manifest输出目录
project.afterEvaluate {
android.applicationVariants.all { variant ->
variant.outputs.all { output ->
def s1 = output.processManifest.manifestOutputDirectory.get().asFile.absolutePath
def s2 = output.processManifest.bundleManifestOutputDirectory.get().asFile.absolutePath
def variantName = variant.name.capitalize()
checkPackageType(project, variantName, s1, s2)
}
}
}
效果
查看打包后的aab manifest文件,第三方activity被自动添加上exported字段
完整代码
project.afterEvaluate {
android.applicationVariants.all { variant ->
variant.outputs.all { output ->
def s1 = output.processManifest.manifestOutputDirectory.get().asFile.absolutePath
def s2 = output.processManifest.bundleManifestOutputDirectory.get().asFile.absolutePath
def variantName = variant.name.capitalize()
addManifestTask(project, variantName, s1, s2)
}
}
}
def addManifestTask(Project project, String variantName, manifestPath, bundleManifestPath) {
try {
def mergeResourcesTask = project.getTasks().getByName(String.format("process%sManifest", variantName))
mergeResourcesTask.doLast {
File mainManifestFile = new File(bundleManifestPath + "/AndroidManifest.xml")
fixManifest(mainManifestFile)
}
def processResourcesTask = project.getTasks().getByName(String.format("process%sResources", variantName))
processResourcesTask.doFirst {
File mainManifestFile = new File(manifestPath + "/AndroidManifest.xml")
fixManifest(mainManifestFile)
}
} catch (Exception e) {
}
}
def fixManifest(File mainManifestFile) {
try {
def xml = new XmlParser(false, true).parse(mainManifestFile)
def exportedTag = "android:exported"
def androidSpace = new groovy.xml.Namespace('http://schemas.android.com/apk/res/android', 'android')
def nodes = xml.application[0].'*'.findAll {
(it.name() == 'activity' || it.name() == 'receiver' || it.name() == 'service') && it.attribute(androidSpace.exported) == null
}
nodes.each {
def exportedValue = false
it.each {
if (it.name() == "intent-filter") {
it.each {
if (it.name() == "action") {
if (it.attributes().get(androidSpace.name) == "android.intent.action.MAIN") {
exportedValue = true
}
}
}
}
}
it.attributes().put(exportedTag, "${exportedValue}")
}
PrintWriter pw = new PrintWriter(mainManifestFile)
pw.write(groovy.xml.XmlUtil.serialize(xml))
pw.close()
} catch (Exception e) {
}
}