23

How to define apk output directory when using gradle?

I would like to have possibility to upload apk to shared folder after each build.

4 Answers 4

25

thats work for me:

android.applicationVariants.all { variant ->
    def outputName = // filename
    variant.outputFile = file(path_to_filename)
}

or for Gradle 2.2.1+

android {
    applicationVariants.all { variant ->
        variant.outputs.each { output ->
            output.outputFile = new File(path_to_filename, output.outputFile.name)
        }
    }
}

but "clean" task will not drop that apk, so you should extend clean task as below:

task cleanExtra(type: Delete) {
    delete outputPathName
}

clean.dependsOn(cleanExtra)

full sample:

apply plugin: 'android'

def outputPathName = "D:\\some.apk"

android {
    compileSdkVersion 19
    buildToolsVersion "19.0.3"

    defaultConfig {
        minSdkVersion 8
        targetSdkVersion 19
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            runProguard false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
        }
    }

    applicationVariants.all { variant ->
        variant.outputFile = file(outputPathName)
    }
}

dependencies {
    compile 'com.android.support:appcompat-v7:19.+'
    compile fileTree(dir: 'libs', include: ['*.jar'])
}

task cleanExtra(type: Delete) {
    delete outputPathName
}

clean.dependsOn(cleanExtra)
6
  • 4
    This doesn't work for Gradle 2.2.1+ versions: > No such property: outputFile for class: com.android.build.gradle.internal.api.ApplicationVariantImpl_Decorated
    – goRGon
    Commented Sep 28, 2015 at 18:28
  • 1
    This doesn't work for Gradle 4.1: Cannot set the value of read-only property 'outputFile'
    – 3c71
    Commented Sep 3, 2017 at 20:42
  • With Gradle 4.1 :- 1. instead of "variant.outputs.each" use "variant.outputs.all" and 2. instead of "variant.outputFile" use "variant.outputFileName"
    – Napolean
    Commented Oct 26, 2017 at 8:51
  • 1
    Can't set directory with outputFileName, can you?
    – 3c71
    Commented Nov 11, 2017 at 10:24
  • you cannot set a specific directory/folder with outputFileName However you can use relative paths in it (.e.g. "..\\..\\${buildType}\\${variant.flavorName}.apk"
    – EMalik
    Commented Mar 2, 2018 at 23:55
8

I found the solution that works with the latest Gradle plugin:

def archiveBuildTypes = ["release", "debug"];

applicationVariants.all { variant ->
    variant.outputs.each { output ->
        if (variant.buildType.name in archiveBuildTypes) {
            // Update output filename
            if (variant.versionName != null) {
                String name = "MY_APP-${variant.versionName}-${output.baseName}.apk"
                output.outputFile = new File(output.outputFile.parent, name)
            }
            // Move output into DIST_DIRECTORY
            def taskSuffix = variant.name.capitalize()
            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."
                    print "Copying APK&mappings.txt from: ${buildDir}\n"
                    from("${buildDir}") {
                        include "**/mapping/${variant.buildType.name}/mapping.txt"
                        include "**/apk/${output.outputFile.name}"
                    }
                    into DIST_DIRECTORY
                    eachFile { file ->
                        file.path = file.name // so we have a "flat" copy
                    }
                    includeEmptyDirs = false
                }
                tasks[assembleTaskName].finalizedBy = [copyAPKTask]
            }
        }
    }
}
3
  • On which version is this supported? Latest gradle 4.1 reports unknown property 'archivedBuildTypes'. And next is read-only property 'outputFile'
    – 3c71
    Commented Sep 5, 2017 at 12:03
  • 1
    My gradle build failed with error: Cannot set the value of read-only property 'outputFile' for ApkVariantOutputImpl_Decorated{apkData=Main{type=MAIN, fullName=oneOfMyVariantDebug, filters=[]}} of type com.android.build.gradle.internal.api.ApkVariantOutputImpl
    – HendraWD
    Commented Jun 11, 2018 at 4:57
  • @3c71 I think you have typo there. It is not archivedBuildTypes but archiveBuildTypes
    – HendraWD
    Commented Jun 11, 2018 at 5:26
2

For Gradle version 2.2.1 +, you can do this:

def outputPathName = "app/app-release.apk"
applicationVariants.all { variant ->
        variant.outputs.each { output ->
            output.outputFile = new File(outputPathName)
        }
    }
1
  • 8
    This doesn't work for Gradle 4.1: Cannot set the value of read-only property 'outputFile'. Gradle 4.1 now saves APK in sub-folders named after the variant.
    – 3c71
    Commented Sep 3, 2017 at 20:43
1

This solution is work for classpath 'com.android.tools.build:gradle:3.1.2' and distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip. Put the following code inside your android scope of app-level build.gradle file. When you use command ./gradlew assembleDebug or ./gradlew assembleRelease, the output folder will be copied to the distFolder.

// Change all of these based on your requirements
def archiveBuildTypes = ["release", "debug"];
def distFolder = "/Users/me/Shared Folder(Personal)/MyApplication/apk/"
def appName = "MyApplication"

applicationVariants.all { variant ->
    variant.outputs.all { output ->
        if (variant.buildType.name in archiveBuildTypes) {
            // Update output filename
            if (variant.versionName != null) {
                String name = "$appName-${variant.versionName}-${output.baseName}.apk"
                outputFileName = new File(name)
            }
            def taskSuffix = variant.name.capitalize()
            def assembleTaskName = "assemble${taskSuffix}"
            if (tasks.findByName(assembleTaskName)) {
                def copyAPKFolderTask = tasks.create(name: "archive${taskSuffix}", type: org.gradle.api.tasks.Copy) {
                    description "Archive/copy APK folder to a shared folder."
                    def sourceFolder = "$buildDir/outputs/apk/${output.baseName.replace("-", "/")}"
                    def destinationFolder = "$distFolder${output.baseName.replace("-", "/")}"
                    print "Copying APK folder from: $sourceFolder into $destinationFolder\n"
                    from(sourceFolder)
                    into destinationFolder
                    eachFile { file ->
                        file.path = file.name // so we have a "flat" copy
                    }
                    includeEmptyDirs = false
                }
                tasks[assembleTaskName].finalizedBy = [copyAPKFolderTask]
            }
        }
    }
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.