Android 开发进程 0.35 升级编译版本Android12
Android12升级
工作需要升级到编译版本31 在这里记录一下遇到的问题。
错误:Manifest merger failedManifest merger failed 这个问题通常搜到的答案是manifest文件格式错误,但这是由于升级编译版本的原因,在Android SDK 31中,需要明确声明组件的 exported属性 android:exported="true" 官方文档如下:

这意味这我们的manifest中每个组件的字段都需要添加exported的属性,包括所有依赖的库和module,主工程和module可以自己修改,但如果依赖的库没有写规范,结果会编译成功后在Android12版本上不能正确安装,而远程依赖的库和插件是不好修改的。此时要用到Android构建特性,Android在打包过程中,所有的组件和依赖产生的manifest文件将集合到一起,同时主manifest即我们app的manifest文件将会被重写,所以可以用gradle文件Groovy脚本实现。
具体在GitHub上有实现,笔者在此只是转载:https://github.com/phamtdat/AndroidSnippets/blob/master/Android12AndroidManifestAddMissingAndroidExported/build.gradle
具体代码如下:
import org.w3c.dom.Element
import org.w3c.dom.Node
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult
import javax.xml.transform.TransformerFactory
import javax.xml.transform.Transformer
def addAndroidExportedIfNecessary(File manifestFile) {
def manifestAltered = false
def reader = manifestFile.newReader()
def document = groovy.xml.DOMBuilder.parse(reader)
def application = document.getElementsByTagName("application").item(0)
if (application != null) {
println "Searching for activities, services and receivers with intent filters..."
application.childNodes.each { child ->
def childNodeName = child.nodeName
if (childNodeName == "activity" || childNodeName == "activity-alias" ||
childNodeName == "service" || childNodeName == "receiver") {
def attributes = child.getAttributes()
if (attributes.getNamedItem("android:exported") == null) {
def intentFilters = child.childNodes.findAll {
it.nodeName == "intent-filter"
}
if (intentFilters.size() > 0) {
println "found ${childNodeName} ${attributes.getNamedItem("android:name").nodeValue} " +
"with intent filters but without android:exported attribute"
def exportedAttrAdded = false
for (def i = 0; i < intentFilters.size(); i++) {
def intentFilter = intentFilters[i]
def actions = intentFilter.childNodes.findAll {
it.nodeName == "action"
}
for (def j = 0; j < actions.size(); j++) {
def action = actions[j]
def actionName = action.getAttributes().getNamedItem("android:name").nodeValue
if (actionName == "com.google.firebase.MESSAGING_EVENT") {
println "adding exported=false to ${attributes.getNamedItem("android:name")}..."
((Element) child).setAttribute("android:exported", "false")
manifestAltered = true
exportedAttrAdded = true
}
}
}
if (!exportedAttrAdded) {
println "adding exported=true to ${attributes.getNamedItem("android:name")}..."
((Element) child).setAttribute("android:exported", "true")
manifestAltered = true
}
}
}
}
}
}
if (manifestAltered) {
document.setXmlStandalone(true)
Transformer transformer = TransformerFactory.newInstance().newTransformer()
DOMSource source = new DOMSource(document)
FileWriter writer = new FileWriter(manifestFile)
StreamResult result = new StreamResult(writer)
transformer.transform(source, result)
println "Done adding missing android:exported attributes to your AndroidManifest.xml. You may want to" +
"additionally prettify it in Android Studio using [command + option + L](mac) or [CTRL+ALT+L](windows)."
} else {
println "Hooray, your AndroidManifest.xml did not need any change."
}
}
def getMissingAndroidExportedComponents(File manifestFile) {
List<Node> nodesFromDependencies = new ArrayList<>()
def reader = manifestFile.newReader()
def document = groovy.xml.DOMBuilder.parse(reader)
def application = document.getElementsByTagName("application").item(0)
if (application != null) {
println "Searching for activities, services and receivers with intent filters..."
application.childNodes.each { child ->
def childNodeName = child.nodeName
if (childNodeName == "activity" || childNodeName == "activity-alias" ||
childNodeName == "service" || childNodeName == "receiver") {
def attributes = child.getAttributes()
if (attributes.getNamedItem("android:exported") == null) {
def intentFilters = child.childNodes.findAll {
it.nodeName == "intent-filter"
}
if (intentFilters.size() > 0) {
println "found ${childNodeName} ${attributes.getNamedItem("android:name").nodeValue} " +
"with intent filters but without android:exported attribute"
def exportedAttrAdded = false
for (def i = 0; i < intentFilters.size(); i++) {
def intentFilter = intentFilters[i]
def actions = intentFilter.childNodes.findAll {
it.nodeName == "action"
}
for (def j = 0; j < actions.size(); j++) {
def action = actions[j]
def actionName = action.getAttributes().getNamedItem("android:name").nodeValue
if (actionName == "com.google.firebase.MESSAGING_EVENT") {
println "adding exported=false to ${attributes.getNamedItem("android:name")}..."
((Element) child).setAttribute("android:exported", "false")
exportedAttrAdded = true
}
}
}
if (!exportedAttrAdded) {
println "adding exported=true to ${attributes.getNamedItem("android:name")}..."
((Element) child).setAttribute("android:exported", "true")
}
nodesFromDependencies.add(child)
}
}
}
}
}
return nodesFromDependencies
}
def addManifestFileComponents(File manifestFile, List<Node> components) {
def reader = manifestFile.newReader()
def document = groovy.xml.DOMBuilder.parse(reader)
def application = document.getElementsByTagName("application").item(0)
if (application != null) {
println "Adding missing components with android:exported attribute to ${manifestFile.absolutePath} ..."
components.each { node ->
Node importedNode = document.importNode(node, true)
application.appendChild(importedNode)
}
}
if (components.size() > 0) {
document.setXmlStandalone(true)
Transformer transformer = TransformerFactory.newInstance().newTransformer()
DOMSource source = new DOMSource(document)
FileWriter writer = new FileWriter(manifestFile)
StreamResult result = new StreamResult(writer)
transformer.transform(source, result)
println "Added missing app-dependencies components with android:exported attributes to your " +
"AndroidManifest.xml.You may want to additionally prettify it in Android Studio using " +
"[command + option + L](mac) or [CTRL+ALT+L](windows)."
}
println "----"
}
task doAddAndroidExportedIfNecessary {
doLast {
def root = new File(project.rootDir, "")
if (root.isDirectory()) {
def children = root.listFiles()
for (def i = 0; i < children.size(); i++) {
File child = children[i]
if (child.isDirectory()) {
File srcDirectory = new File(child, "src")
if (srcDirectory.exists() && srcDirectory.isDirectory()) {
def srcChildren = srcDirectory.listFiles()
for (def j = 0; j < srcChildren.size(); j++) {
File manifestFile = new File(srcChildren[j], "AndroidManifest.xml")
if (manifestFile.exists() && manifestFile.isFile()) {
println "found manifest file: ${manifestFile.absolutePath}"
addAndroidExportedIfNecessary(manifestFile)
println "-----"
}
}
}
}
}
}
}
}
task doAddAndroidExportedForDependencies {
doLast {
List<Node> missingComponents = new ArrayList<>()
def root = new File(project.rootDir, "")
if (root.isDirectory()) {
def children = root.listFiles()
for (def i = 0; i < children.size(); i++) {
File child = children[i]
if (child.isDirectory()) {
File mergedManifestsDirectory = new File(child, "build/intermediates/merged_manifests")
if (mergedManifestsDirectory.exists() && mergedManifestsDirectory.isDirectory()) {
def manifestFiles = mergedManifestsDirectory.listFiles().findAll { directoryChild ->
directoryChild.isDirectory() &&
(new File(directoryChild, "AndroidManifest.xml")).exists()
}.stream().map { directoryWithManifest ->
new File(directoryWithManifest, "AndroidManifest.xml")
}.toArray()
if (manifestFiles.size() > 0) {
File mergedManifest = manifestFiles[0]
if (mergedManifest.exists() && mergedManifest.isFile()) {
missingComponents = getMissingAndroidExportedComponents(mergedManifest)
if (missingComponents.size() > 0) {
File srcDirectory = new File(child, "src")
if (srcDirectory.exists() && srcDirectory.isDirectory()) {
def srcChildren = srcDirectory.listFiles()
for (def j = 0; j < srcChildren.size(); j++) {
File manifestFile = new File(srcChildren[j], "AndroidManifest.xml")
if (manifestFile.exists() && manifestFile.isFile()) {
addManifestFileComponents(manifestFile, missingComponents)
}
}
}
}
}
}
}
}
}
}
}
}
具体使用方法将此代码加入app中的gradle中,也可以抽出为独立gradle文件,在build后执行doAddAndroidExportedIfNecessary 任务,如果成功后会在app中的manifest文件中找到遍历出来未明确表明的exported得组件声明。
如果失败的话可以尝试将SDK版本降到30在执行构建任务再提高编译版本。之后就可以开启12的特新测试了。在此感谢提供脚本的大佬。
Android 开发进程 0.35 升级编译版本Android12的更多相关文章
- Android开发进程0.1 轮播图 Scrollview Fragment
轮播图的实现 轮播图通过banner可以较为便捷的实现 1.添加本地依赖,在dependence中搜索相关依赖 2.添加banner的view组件 3.创建适配器GlideImageLoader ex ...
- Android开发学习总结(一)——搭建最新版本的Android开发环境
Android开发学习总结(一)——搭建最新版本的Android开发环境(转) 最近由于工作中要负责开发一款Android的App,之前都是做JavaWeb的开发,Android开发虽然有所了解,但是 ...
- Qt for Windows:Qt 5.4.0 MinGW 静态编译版本制作 (转)
大致流程: 1.安装Qt(源码版)以及其他必要的环境 2.编译/安装 3.配置 4.使用 ----------正文分割线---------- 1.安装Qt(源码版) 1.1 下载Qt(两个地址二选一即 ...
- 转:Android开发实践:用脚本编译Android工程
转自: http://ticktick.blog.51cto.com/823160/1365947 一般情况下,我们都是使用Eclipse+ADT插件或者Android studio软件来编译Andr ...
- android开发--数据库(更新或者降低版本)
Andoird的SQLiteOpenHelper类中有一个onUpgrade方法. 1. 帮助文档里说的"数据库升级"是指什么? 你开发了一个应用,当前是1.0版本.该程序用到了数 ...
- 【转】Android开发学习总结(一)——搭建最新版本的Android开发环境
最近由于工作中要负责开发一款Android的App,之前都是做JavaWeb的开发,Android开发虽然有所了解,但是一直没有搭建开发环境去学习,Android的更新速度比较快了,Android1. ...
- Android开发指南--0 总览
无意间发现一个网站,主打IOS方面的教程,然而作为一个Android开发者,我就找了下网站里有没有Android的教程,还真有,这里就翻译一下. 翻译目标教程:https://www.raywende ...
- android开发(0):android studio的下载安装与简单使用 | sdk的安装与编译运行
android studio,简称AS,是集成开发环境,所谓集成,就是集编辑.编译.调试.打包等于一体.简单来说,通过AS,就可以开发出在android系统上运行的APP. 我使用的是macos系统. ...
- Android 开发 8.0版本启动Service的方法
前言 google在更新Android8.0后对Service的权限越发收紧.导致目前想要启动服务必需实现服务的前台化(否则在服务启动5秒后,系统将自动报错).下面我们就来看看如何在8.0上启动服务 ...
随机推荐
- .net core api 对于FromBody的参数验证
前言 在framework的mvc中,经常会使用 Model.State . ModelState.IsValid 配合着特性进行参数验证,通过这种方式可以降低controller的复杂度,使用方便. ...
- 【java虚拟机】jvm调优
转自:https://www.cnblogs.com/starhu/p/6400348.html?utm_source=itdadao&utm_medium=referral 堆大小设置JVM ...
- 栈(Stack)
特点: 栈最大的特点就是后进先出(LIFO).对于栈中的数据来说,所有操作都是在栈的顶部完成的,只可以查看栈顶部的元素,只能够向栈的顶部压入数据,也只能从栈的顶部弹出数据. 实现: 利用一个单链表来实 ...
- Dockerfile 实践及梳理
Dockerfile 是一个文本文件,我们可以通过组合一条条的指令 (Instruction),来构建满足我们需求的 Docker 镜像 文档 Best practices for writing D ...
- 一次PHP大马提权
记一次PHP提权 发现 PHP大马:指木马病毒:PHP大马,就是PHP写的提取站点权限的程序:因为带有提权或者修改站点功能,所以称为叫木马. 自从师哥那里听说过之后,一直感叹于PHP大马的神奇...但 ...
- MySQL密码重置方法
MySQL数据库的安装和配置,配置Mysql按照bin目录到Path中 使用命令行窗口连接MYSQL数据库:mysql –u用户名 –p密码 对于密码的重置有以下两种方法(卸载重新安装当然也可以): ...
- SpringBoot2.x+mybatis plus3.x集成Activit7版本
最近在写一个开源项目ruoyi-vue-pro,暂时负责Activiti7工作流的搭建,接这个任务一个原因,是比较好奇Activiti7版本与先前的5.6版本究竟有什么区别,因为先前在工作当中,最开始 ...
- 对 RESTful 的理解
REST 全称 Representation State Transfor (资源表现层状态改变) 实际上是指客户端通过http/https协议手段来改变URI的状态转化,达到请求不同的资源的目的. ...
- MySQL——MySQL客户端命令
1. mysql: (1)用于数据库连接 (2)用于管理数据库: a: 命令接口自带命令 b: SQL语句: DDL: 数据库定义语言 DCL: 数据库控制语言 DML: 数据库操作语言 2. mys ...
- [考试总结]noip模拟40
最近真的是爆炸啊... 到现在还是有不少没改出来.... 所以先写一下 \(T1\) 的题解.... 送花 我们移动右端点,之后我们用线段树维护全局最大值. 之后还要记录上次的位置和上上次的位置. 之 ...