如何在 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 -&gt; "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

Android开发:《Gradle Recipes for Android》阅读笔记(翻译)5.1——单元测试

VS生成Cordova for Android应用之Gradle