如何在 gradle for Android 项目中更改 proguard 映射文件名
Posted
技术标签:
【中文标题】如何在 gradle for Android 项目中更改 proguard 映射文件名【英文标题】:How to change the proguard mapping file name in gradle for Android project 【发布时间】:2015-05-10 23:52:32 【问题描述】:我有基于 gradle 的 android 项目,我想在它为我的构建生成后更改 mapping.txt 文件名。怎么办?
更新
如何在 build.gradle 中完成?由于我可以访问我的风味和其他僵硬的东西,我想根据风味/构建变体版本创建映射文件名。
【问题讨论】:
我的提议对你有帮助吗? 【参考方案1】:这是对我有帮助的解决方案:
compileSdkVersion 30
JavaVersion.VERSION_1_8
kotlin_version = '1.5.31'
com.android.tools.build:gradle:7.0.2
buildTypes
release
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
applicationVariants.all variant ->
// Generating apk file for each flavour release build
variant.outputs.all
outputFileName = "$variant.flavorName-$variant.versionCode.apk"
// Generating mapping file for each flavour release build
if (variant.getBuildType().isMinifyEnabled())
variant.assembleProvider.get().doLast
def files = variant.getMappingFileProvider().get().getFiles()
for (file in files)
if (file != null && file.exists())
def newName = "mapping-$variant.flavorName-$variant.versionCode.txt"
def newFile = new File(file.parent, newName)
newFile.delete()
file.renameTo(newName)
【讨论】:
【参考方案2】:适用于 Android Studio Gradle 插件版本 4.1.0 及更高版本(自 2020 年 5 月左右起)
此版本修复了以下警告:
警告:API 'variant.getMappingFile()' 已过时,已替换为 'variant.getMappingFileProvider()'。
applicationVariants.all variant ->
variant.assembleProvider.get().doLast
def mappingFileProvider = variant.getMappingFileProvider().get()
if (mappingFileProvider != null)
try
def mappingFiles = mappingFileProvider.getFiles()
for (mappingFile in mappingFiles)
if (mappingFile != null && mappingFile.exists())
def newMappingFileName = "$archivesBaseName-$variant.baseName-$mappingFile.name"
project.logger.lifecycle("Renaming '$mappingFile.name' to '$newMappingFileName'")
def newMappingFile = new File(mappingFile.parent, newMappingFileName)
newMappingFile.delete()
mappingFile.renameTo(newMappingFile)
catch (Exception ignored)
project.logger.lifecycle("No mapping files found to rename")
适用于 Android Studio Gradle 插件版本 3.3.0(2019 年 1 月)至 2020 年 5 月左右
这克服了之前的问题,即 Android 3.0/Android Gradle Plugin 3.0 已弃用 BuildType.isMinifyEnabled()
并且 gradle 插件已弃用 variant.getAssemble()。
applicationVariants.all variant ->
variant.assembleProvider.get().doLast
if (variant.mappingFile != null && variant.mappingFile.exists())
def mappingFilename = "$archivesBaseName-$variant.baseName-mapping.txt"
(new File(variant.mappingFile.parent, mappingFilename)).delete()
variant.mappingFile.renameTo(variant.mappingFile.parent +
"/" + mappingFilename)
【讨论】:
【参考方案3】:截至今天(2020 年 5 月) 以前使用 variant.mappingFile
的解决方案不再适用于新的 Android Gradle 插件 (Android Studio) 3.6 及更高版本。
而不是variant.mappingFile
返回null
并在日志中显示以下内容:
警告:API 'variant.getMappingFile()' 已过时,已替换为 'variant.getMappingFileProvider()'。
我正在分享我的工作解决方案,它使用新的 api:
applicationVariants.all variant ->
variant.assembleProvider.get().doLast
def mappingFiles = variant.getMappingFileProvider().get().files
for (file in mappingFiles)
if (file != null && file.exists())
def nameMatchingApkFile = "$archivesBaseName-$variant.baseName-$file.name"
def newMappingFile = new File(file.parent, nameMatchingApkFile)
newMappingFile.delete() //clean-up if exists already
file.renameTo(newMappingFile)
注意,variant.getBuildType().isMinifyEnabled() 没有被使用,因为我们使用的是 DexGuard。
上面的代码使映射文件的名称与apk的文件名匹配。
以防万一,如果您需要更改 apk 名称 - 可以使用以下方法:
android
defaultConfig
//resulting apk will looks like: "archive base name" + -<flavour>-<buildType>.apk
archivesBaseName = "$applicationId-$versionName"
【讨论】:
【参考方案4】:对我有用的完整解决方案
applicationVariants.all variant ->
def variantType = variant.buildType.name
if (variantType == "release")
variant.assemble.doLast
def mappingFile = variant.mappingFile
mappingFile.renameTo(mappingFile.parent + "/mapping-$variant.name.txt")
【讨论】:
【参考方案5】:这是igorpst's answer 的变体,但重命名了mapping.txt 以匹配apk 的名称,包括应用程序版本名称。我已经将它与代码结合起来,用this answer 中描述的版本号来命名APK。我不得不窥探 gradle 源代码才能找到 $variant.baseName
apply plugin: 'com.android.application'
android
compileSdkVersion 21
buildToolsVersion "21.1.2"
defaultConfig
applicationId "com.company.app"
minSdkVersion 13
targetSdkVersion 21
versionCode 14 // increment with every release
versionName '1.4.8' // change with every release
archivesBaseName = "MyCompany-MyAppName-$versionName"
applicationVariants.all variant ->
if (variant.getBuildType().isMinifyEnabled())
variant.assemble.doLast
(new File(variant.mappingFile.parent, "$archivesBaseName-$variant.baseName-mapping.txt")).delete();
variant.mappingFile.renameTo(variant.mappingFile.parent +
"/$archivesBaseName-$variant.baseName-mapping.txt")
【讨论】:
【参考方案6】:variant.assemble
现在已弃用,建议的解决方案结合了以前的修改:
archivesBaseName = "MyCompany-MyAppName-$versionName"
...
applicationVariants.all variant ->
variant.assembleProvider.get().doLast
if (variant.mappingFile != null && variant.mappingFile.exists())
def mappingFilename = "$archivesBaseName-$variant.baseName-mapping.txt"
(new File(variant.mappingFile.parent, mappingFilename)).delete()
variant.mappingFile.renameTo(variant.mappingFile.parent +
"/" + mappingFilename)
如果使用 app bundle (aab) 而不是 apk,请将其添加到 android 部分:
afterEvaluate
bundleRelease.doLast
android.applicationVariants.all variant ->
if (variant.buildType.name == 'release')
tasks.create(name: "renameMappingFile")
if (variant.mappingFile != null && variant.mappingFile.exists())
variant.mappingFile.renameTo(variant.mappingFile.parent + "/$variant.baseName-$versionName-$new Date().format('yyyy-MM-dd_HHmm')-mapping.txt")
在最后一个示例中也将bundleRelease
换成assembleRelease
换成apks。
更新:但是,如果您尝试直接在手机上构建正常调试,则最后一个不起作用。错误:
无法为 org.gradle.api.Project 类型的项目 ':app' 获取未知属性 'bundleRelease'。
【讨论】:
【参考方案7】:所有这些答案都使用副本来重命名文件。 但是我不想移动文件,我只想更改它的名称,记住构建类型和风格。
我根据此处其他发帖人的代码进行了一些修改。 由于 Minify 可能为 false,在仍然使用 proguard 的同时,我只检查文件是否存在。
下面的代码就完成了。
android
applicationVariants.all variant ->
variant.assemble.doLast
def mappingFolderUrl = "$project.buildDir.path/outputs/mapping/"
if (variant.buildType.name)
mappingFolderUrl += variant.buildType.name + "/"
if (variant.flavorName)
mappingFolderUrl += variant.flavorName + "/"
def mappingFileUrl = mappingFolderUrl + "mapping.txt"
logger.lifecycle("mappingFile path: $mappingFileUrl")
File mappingFile = file(mappingFileUrl)
if (mappingFile.exists())
def newFileName = mappingFolderUrl + "mapping-$variant.name.txt"
mappingFile.renameTo(newFileName)
注意
您也可以使用此代码来移动文件。
.renameTo() 方法需要一个完整路径,如果你改变路径,我想你有效地将文件移动到另一个地方。
【讨论】:
【参考方案8】:applicationVariants.all variant ->
variant.outputs.each output ->
if (variant.getBuildType().isMinifyEnabled())
variant.assemble.doLast
copy
from variant.mappingFile
into "$rootDir/mapping"
rename String fileName ->
"mapping-$variant.name-$new Date().format('yyyy_MM_dd').txt"
【讨论】:
【参考方案9】:Pinhassi 的上述解决方案效果很好,并且符合最新的 Gradle 更改。不过有几件事我必须改变:
-
模块名称是硬编码的(“app”),这并不理想,因为在很多情况下(包括我的)这都不正确。最好动态检测模块名称。
映射文件也只符合 Windows 文件系统,因为它具有向后转义的斜杠 ("\")。如果您使用的是 Linux 或 Mac 等 *NIX 系统,则需要将那些替换为正向非转义斜杠(“/”)
稍微更改了 .apk 文件的重命名以包含项目名称,并在末尾添加了日期/时间戳。
这是完成的代码:
import java.util.regex.Matcher
import java.util.regex.Pattern
buildTypes
release
debuggable false
minifyEnabled true
proguardFiles 'proguard.cfg'
// Rename the apk file and copy the ProGuard mapping file to the root of the project
applicationVariants.all variant ->
if (variant.getBuildType().name.equals("release"))
def formattedDate = new Date().format('yyyyMMddHHmmss')
def projectName = ""
variant.outputs.each output ->
def fullName = output.outputFile.name
projectName = fullName.substring(0, fullName.indexOf('-'))
// $variant.name has the value of "paidRelease"
output.outputFile = new File((String) output.outputFile.parent, (String) output.outputFile.name.replace(".apk", "-v$variant.versionName-$formattedDate.apk"))
def mappingFile = "$rootDir/$projectName/build/outputs/mapping/$getCurrentFlavor()/release/mapping.txt"
println("mappingFile: $mappingFile")
if (variant.getBuildType().isMinifyEnabled())
variant.assemble.doLast
copy
from "$mappingFile"
into "$rootDir"
rename String fileName ->
"mapping-$variant.name.txt"
debug
debuggable true
def getCurrentFlavor()
Gradle gradle = getGradle()
String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
Pattern pattern;
if( tskReqStr.contains( "assemble" ) )
pattern = Pattern.compile("assemble(\\w+)(Release|Debug)")
else
pattern = Pattern.compile("generate(\\w+)(Release|Debug)")
Matcher matcher = pattern.matcher( tskReqStr )
if( matcher.find() )
return matcher.group(1).toLowerCase()
else
println "NO MATCH FOUND"
return "";
【讨论】:
【参考方案10】:自上次更新以来,variant.mappingFile 不再可用。 (我使用 ProGuard 4.7 版,AndroidStudio 2.0)
这是(部分)我的 build.gradle 文件:
import java.util.regex.Matcher
import java.util.regex.Pattern
def getCurrentFlavor()
Gradle gradle = getGradle()
String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
Pattern pattern;
if( tskReqStr.contains( "assemble" ) )
pattern = Pattern.compile("assemble(\\w+)(Release|Debug)")
else
pattern = Pattern.compile("generate(\\w+)(Release|Debug)")
Matcher matcher = pattern.matcher( tskReqStr )
if( matcher.find() )
return matcher.group(1).toLowerCase()
else
println "NO MATCH FOUND"
return "";
buildTypes
release
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
minifyEnabled true
applicationVariants.all variant ->
variant.outputs.each output ->
output.outputFile = new File(output.outputFile.parent, "$variant.name_v$variant.versionName.apk")
def mappingFile = "$rootDir\\app\\build\\outputs\\mapping\\$getCurrentFlavor()\\release\\mapping.txt"
println("mappingFile: $mappingFile")
if (variant.getBuildType().isMinifyEnabled())
variant.assemble.doLast
copy
from "$mappingFile"
into "$rootDir"
rename String fileName ->
"mapping-$variant.name.txt"
debug
minifyEnabled false
useProguard false
applicationVariants.all variant ->
variant.outputs.each output ->
output.outputFile = new File(output.outputFile.parent, "$variant.name_v$variant.versionName.apk")
【讨论】:
谢谢..!它的工作,但不明白这个问题 实际上在将 gradle 1.5.0 更新到 2.1.0 后,我在“from variant.mappingFile”行收到了空指针异常,如果你能解释一下就好了 是的,我也明白了【参考方案11】:更简单的解决方案。
applicationVariants.all variant ->
if (variant.getBuildType().isMinifyEnabled())
variant.assemble.doLast
copy
from variant.mappingFile
into "$rootDir/proguardTools"
rename String fileName ->
"mapping-$variant.name.txt"
【讨论】:
太好了,谢谢。如果您有多种产品风格并且不想包含 buildType,您也可以使用rename String fileName -> "mapping-$variant.getFlavorName().txt"
。
很好的解决方案!但为什么variant.outputs
上有冗余循环?循环内没有对output
的引用。
如果您只需要重命名现有映射文件而不将其复制到新位置,请使用此代码片段:def mappingFile = variant.mappingFile mappingFile.renameTo(mappingFile.parent + "/newName.txt")
脚本也在这篇博文中:medium.com/@xabaras/…
当我生成 App 包而不是 APK 时,您是否有工作脚本?因为那时没有组装任务,而是运行捆绑任务。并且没有绑定 doLast 的 bundle 属性。【参考方案12】:
感谢Sergii Pechenizkyi 帮助我找到了这个好的解决方案。
为了实现每种风格的 proguard 映射文件的复制,我们可以创建“根”任务 copyProguardMappingTask
和每个风格的动态任务数
def copyProguardMappingTask = project.tasks.create("copyProguardMapping")
applicationVariants.all variant ->
variant.outputs.each output ->
...
if (variant.getBuildType().isMinifyEnabled())
def copyProguardMappingVariantTask = project.tasks.create("copyProguardMapping$variant.name.capitalize()", Copy)
def fromPath = variant.mappingFile;
def intoPath = output.outputFile.parent;
copyProguardMappingVariantTask.from(fromPath)
copyProguardMappingVariantTask.into(intoPath)
copyProguardMappingVariantTask.rename('mapping.txt', "mapping-$variant.name.txt")
copyProguardMappingVariantTask.mustRunAfter variant.assemble
copyProguardMappingTask.dependsOn copyProguardMappingVariantTask
之后我们应该在组装我们的项目后运行这个任务。我使用 jenkins,我的任务选项看起来像
gradle clean assembleProjectName copyProguardMapping
它就像一个魅力。
【讨论】:
【参考方案13】:在你的 proguard-rules.pro 文件中使用这个命令:
-printmapping path/to/your/file/file_name.txt
文件将写入root/path/to/your/file
部分,名称为file_name.txt
。
如果你想为不同的风格设置不同的设置,你可以为它们定义许多 proguard-rules
我又找到了一个想法,但我不确定它是否正确。
您可以在风味中定义您的路径:
productFlavors
test1
applicationId "com.android.application.test"
project.ext."$namePath" = 'path/one/mapp.txt'
test2
project.ext."$namePath" = 'path/two/mapp.txt'
接下来您可以在$asseblevariant.name.capitalize()
任务之前定义新任务,如下所示:
android.applicationVariants.all variant ->
def envFlavor = variant.productFlavors.get(0).name
def modifyProguardPath = tasks.create(name: "modifyProguardFor$variant.name.capitalize()", type: Exec)
def pathToMap = project."$envFlavorTest1"
doFirst
println "==== Edit start: $pathToMap ===="
doLast
println "==== Edit end: $pathToMap ===="
executable "$rootDir/utils/test.bash"
args pathToMap
project.tasks["assemble$variant.name.capitalize()"].dependsOn(modifyProguardPath);
在脚本$root/utils/test.bash
- 你可以修改proguard-rules.pro
。
但我认为存在更好的解决方案。
【讨论】:
好的,我会尝试,但也许你知道如何在 build.gradle 中做到这一点?由于我可以访问我的风味和其他僵硬的东西,我想根据风味/构建变体版本创建映射文件名。 我从不以编程方式执行此操作,但我可以检查一些想法。如果我找到可行的解决方案,我会通知你 康拉德,谢谢,这条指令“-printmapping build/outputs/mapping/mymapping.txt”有效,但我不能为每个构建变体创建不同的 proguard 规则,因为我有一个那里有很多说明,我会避免在不同的文件中复制它。我仍在寻找从 build.gradle 脚本中执行此操作的方法。 @olegflo 嗨,我找到了一些可行的解决方案,但这不是最好的方法。如果我的回答对你有帮助,请给我点赞 看起来不错,谢谢,但我终于找到了适合我的解决方案,看起来有点不同。以上是关于如何在 gradle for Android 项目中更改 proguard 映射文件名的主要内容,如果未能解决你的问题,请参考以下文章
Android开发:《Gradle Recipes for Android》阅读笔记1.5
Android开发:《Gradle Recipes for Android》阅读笔记(翻译)2.2——将Eclipse开发的项目导入到AndroidStudio
Android开发:《Gradle Recipes for Android》阅读笔记(翻译)2.3——用Eclipse ADT导出App
Android开发:《Gradle Recipes for Android》阅读笔记(翻译)3.1——使用Build Types