在Android Gradle项目中复制APK文件

时间:2014-01-29 14:51:58

标签: android gradle android-gradle

我正在尝试向我的Android项目build.gradle添加自定义任务,以将最终的APK和Proguard的mapping.txt复制到另一个目录中。我的任务取决于assembleDevDebug任务:

task publish(dependsOn: 'assembleDevDebug') << {
    description 'Copies the final APK to the release directory.'

    ...
}

根据文档,我可以看到如何使用标准Copy任务类型执行文件复制:

task(copy, type: Copy) {
    from(file('srcDir'))
    into(buildDir)
}

但是假设您知道要复制的文件的名称和位置。

如何找到作为assembleDevDebug任务一部分构建的APK文件的确切名称和位置?这可以作为财产吗?感觉好像我应该能够将文件声明为我的任务的输入,并将它们声明为assemble任务的输出,但我的Gradle-fu不够强大。

我有一些自定义逻辑可以将版本号注入APK文件名,因此我的publish任务不能只假设默认名称和位置。

6 个答案:

答案 0 :(得分:28)

如果您可以获取与devDebug关联的变体对象,则可以使用getOutputFile()查询它。

因此,如果您想要发布所有变体,您可以这样:

def publish = project.tasks.create("publishAll")
android.applicationVariants.all { variant ->
  def task = project.tasks.create("publish${variant.name}Apk", Copy)
  task.from(variant.outputFile)
  task.into(buildDir)

  task.dependsOn variant.assemble
  publish.dependsOn task
}

现在您可以致电gradle publishAll并发布所有变体。

映射文件的一个问题是Proguard任务没有为您提供文件位置的getter,因此您当前无法查询它。我希望能解决这个问题。

答案 1 :(得分:9)

以下代码是我用来将apk和proguard映射存档到zip文件中的每个变体的'release'构建类型:

def releasePath = file("${rootDir}/archive/${project.name}")

def releaseTask = tasks.create(name: 'release') {
    group 'Build'
    description "Assembles and archives all Release builds"
}

android.applicationVariants.all { variant ->
    if (variant.buildType.name == 'release') {
        def build = variant.name.capitalize()

        def releaseBuildTask = tasks.create(name: "release${build}", type: Zip) {
            group 'Build'
            description "Assembles and archives apk and its proguard mapping for the $build build"
            destinationDir releasePath
            baseName variant.packageName
            if (!variant.buildType.packageNameSuffix) {
                appendix variant.buildType.name
            }
            if (variant.versionName) {
                version "${variant.versionName}_${variant.versionCode}"
            } else {
                version "$variant.versionCode"
            }
            def archiveBaseName = archiveName.replaceFirst(/\.${extension}$/, '')
            from(variant.outputFile.path) {
                rename '.*', "${archiveBaseName}.apk"
            }
            if (variant.buildType.runProguard) {
                from(variant.processResources.proguardOutputFile.parent) {
                    include 'mapping.txt'
                    rename '(.*)', "${archiveBaseName}-proguard_\$1"
                }
            }
        }
        releaseBuildTask.dependsOn variant.assemble

        variant.productFlavors.each { flavor ->
            def flavorName = flavor.name.capitalize()
            def releaseFlavorTaskName = "release${flavorName}"
            def releaseFlavorTask
            if (tasks.findByName(releaseFlavorTaskName)) {
                releaseFlavorTask = tasks[releaseFlavorTaskName]
            } else {
                releaseFlavorTask = tasks.create(name: releaseFlavorTaskName) {
                    group 'Build'
                    description "Assembles and archives all Release builds for flavor $flavorName"
                }
                releaseTask.dependsOn releaseFlavorTask
            }
            releaseFlavorTask.dependsOn releaseBuildTask
        }
    }
}

它创建如下任务:

  • 发布 - 汇总和归档所有发布版本
  • releaseFree - 汇总并归档所有免费版本的发布版本
  • releaseFreeRelease - Assembles and archives apk及其FreeRelease build的proguard映射
  • releasePaid - 为所有版本的Paid
  • 组装和归档所有版本构建
  • releasePaidRelease - 用于PaidRelease构建的Assembles and archives apk及其proguard映射

archive / projectName / packageName-buildType-versionName_versionCode.zip的内容将是:

  • 的packageName-buildType-versionName_versionCode.apk
  • 的packageName-buildType-versionName_versionCode-proguard_mapping.txt

答案 2 :(得分:6)

这是我在proguard运行时复制mappings.txt的方式

tasks.whenTaskAdded { task ->
    if (task.name.startsWith("proguard")) {//copy proguard mappings
        task << {
            copy {
                from buildDir.getPath() + "/proguard"
                into '../proguard'
                include '**/mapping.txt'
            }
            println "PROGUARD FILES COPIED"
        }

    } 
}

答案 3 :(得分:6)

我在这里有一些好的指示,但也很难按我的意愿完成。这是我的最终版本:

def archiveBuildTypes = ["distribute"];
def archiveFlavors = ["googleplay"]

android.applicationVariants.all { variant ->
    if (variant.buildType.name in archiveBuildTypes) {
        variant.productFlavors.each { flavor ->
            if (flavor.name in archiveFlavors) {
                def taskSuffix = variant.name.capitalize()
                def version = "${android.defaultConfig.versionCode} (${android.defaultConfig.versionName})" // assumes that versionName was especified here instead of AndroidManifest.xml
                def destination = "${rootDir}/${project.name}/archive/${version}"

                def assembleTaskName = "assemble${taskSuffix}"
                if (tasks.findByName(assembleTaskName)) {
                    def copyAPKTask = tasks.create(name: "archive${taskSuffix}", type:org.gradle.api.tasks.Copy) {
                        description "Archive/copy APK and mappings.txt to a versioned folder."
                        from ("${buildDir}") {
                            include "**/proguard/${flavor.name}/${variant.buildType.name}/mapping.txt"
                            include "**/apk/${variant.outputFile.name}"
                        }
                        into destination
                        eachFile { file->
                            file.path = file.name // so we have a "flat" copy
                        }
                        includeEmptyDirs = false
                    }
                    tasks[assembleTaskName].finalizedBy = [copyAPKTask]
                }
            }
        }
    }
}

答案 4 :(得分:4)

def publish = project.tasks.create("publishAll")// publish all task
applicationVariants.all { variant ->
    if (variant.buildType.name.equals("release")) {// Only Release  
        File outDir = file("//192.168.4.11/Android/Release")
        File apkFile = variant.outputs[0].outputFile
        File mapFile = variant.mappingFile

        def task = project.tasks.create("publish${variant.name.capitalize()}Apk", Copy)
        task.from apkFile, mapFile
        task.into outDir
        task.rename "mapping.txt", "${apkFile.name.substring(0, apkFile.name.length() - 3)}mapping.txt"// Rename mapping.txt
        task.doLast{
            println ">>>publish ${variant.name} success!" +
                    "\ndir: ${outDir}" +
                    "\napk: ${apkFile.name}"
        }

        task.dependsOn variant.assemble
        publish.dependsOn task
    }
}

答案 5 :(得分:0)

通常android插件会将apks放在APP / build / apk目录中。

所以,运行assembleDebug然后ls APP/build/apk,您应该看到:

  • APP-调试unaligned.apk
  • APP-释放unaligned.apk
  • APP-释放unsigned.apk
  • APP-release.apk 等