在Android Gradle项目中复制APK文件

我试图添加一个自定义任务到我的Android项目的build.gradle复制最终APK和Proguard的mapping.txt到不同的目录。 我的任务取决于assembleDevDebug任务:

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

我可以看到如何使用标准的Copy任务types来执行文件复制,如下所示:

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

但假定你知道你想要复制的文件的名称和位置。

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

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

如果你能得到与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到文件位置,所以你目前不能查询它。 我希望得到这个修复。

下面的代码就是我用来将apk和proguard映射到一个压缩文件中的每个变体的'release'构buildtypes:

 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 } } } 

它创build如下的任务:

  • 发布 – 汇编和归档所有发布版本
  • releaseFree – 将所有发布版本汇编并归档,以免味
  • releaseFreeRelease – 为FreeRelease构build组装并存档apk和它的proguard映射
  • releasePaid – 将所有发布版本汇编并归档为Paid风味
  • releasePaidRelease – 为PaidRelease构buildassembly和归档apk和它的proguard映射

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

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

这就是我每次运行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" } } } 

我在这里有一些好的指针,但也很难做到我想要的。 这是我的最终版本:

 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] } } } } } 
 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 } } 

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

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

  • APP-debuggingunaligned.apk
  • APP释放,unaligned.apk
  • APP释放,unsigned.apk
  • APP-release.apk等