Producing better named Android APKs with Gradle

One thing that’s nagged me when using the Android Gradle build system is that by default the APK files produced will have the nondescript name of app-debug.apk and cousins. This stems from the default and preferred project setup where the application code is contained in the “app” directory, and the name of the directory by default is used for the APK name. But if you’ve ever had to distribute your APK artifacts to other people this simple name won’t cut it.

We can do better!

First, we can recognize that the top level directory most often also is the name of the application. I.e. the directory structure will most often look like

So, in most cases we could use the name of the root project to get the most suitable name for the APKs. However, we still want to easily override this. A suitable way is by reading a value from the gradle.properties file.

And then we also want to add the version number, as that’s a fairly important piece of data.

All of this can fairly easily be done in Gradle. Start by creating a file name artifacts.gradle in the top level directory of the project. Put this Groovy snippet into it.

For Android Studio 2:

For Android Studio 3:

 

Then in your app/build.gradle file add this call

Now your APKs will have names such as MyAwesomeApplication-debug-1.0.0.apk instead. And if you want to provide a name different from the top level directory you just need to add an applicationName property to gradle.properties, as such:

A demonstration of this (complete with product flavors) can be seen in this simple example project.

All code licensed under CC0.

Edited 2017-09-15: removed “unaligned” check

Edited 2017-10-31: updated for Android Studio 3.0

This Post Has 19 Comments

  1. The lambda lines are messed up:
    variant.outputs.each { output ->
    should be:
    variant.outputs.each { output ->

    1. Thanks, it’s fixed now.

      1. Broken again

        1. Yes, sorry, this seems to be impossible to solve with the WordPress plugins we’re using. All “>” characters gets escaped.

  2. Thanks for this trick Erik , I’ve just added to current project and for sure I’ll be using for the future ones.
    I’ve decided to use name format as “MyApp-release-v2.0.0-b11.apk” where b11 is the versionCode(build number) in order to avoid increasing version number for each internal release.

    newApkName = “${appName}-${output.baseName}-v${variant.versionName}-b${variant.versionCode}.apk”

  3. Hi, First of all thanks for the awesome code. I am using the above the code, it is working fine for me but the unaligned apk name is not changing. Its still gives the previous name “app-release-unaligned.apk”

    Please help me in this.Thanks in advance.
    Below is my code:
    android.applicationVariants.all { variant ->
    def appName
    //Check if an applicationName property is supplied; if not use the name of the parent project.
    if (project.hasProperty(“applicationName”)) {
    appName = applicationName
    } else {
    appName = parent.name
    }

    variant.outputs.each { output ->
    def newApkName
    //If there’s no ZipAlign task it means that our artifact will be unaligned and we need to mark it as such.
    if (output.zipAlign) {
    newApkName = “${appName}-${output.baseName}-v${variant.versionName}.apk”
    } else {
    newApkName = “${appName}-${output.baseName}-v${variant.versionName}-unaligned.apk”
    }
    output.outputFile = new File(output.outputFile.parent, newApkName)
    }

    }

    1. It seems that the way the plugin handles the unaligned artifacts have changed. From a quick glance they now seem to be created as an output of the “Package” task.

      I whipped together this which will retain them, but also make a copy of them with the correct name.

      import java.nio.file.Files
      import java.nio.file.StandardCopyOption

      //Make a copy of the unaligned apk file with a better name.
      afterEvaluate { project ->
      def appName
      //Check if an applicationName property is supplied; if not use the name of the parent project.
      if (project.hasProperty(“applicationName”)) {
      appName = applicationName
      } else {
      appName = parent.name
      }

      project.tasks.findAll { it.name.startsWith(‘package’) }.each { Task theTask ->

      if (theTask.variantName) {
      def variant = android.applicationVariants.find {
      it.name == theTask.variantName
      }
      if (variant) {
      def capitalizedName = theTask.variantName.capitalize()
      def newApkName = “${appName}-${variant.baseName}-${variant.versionName}-unaligned.apk”
      def outputFile = new File(theTask.outputFile.parent, newApkName)

      def copyTask = project.tasks.create(name: “copyUnalignedArtifact${capitalizedName}”) {
      description ‘Creates a copy of the unaligned apk with a more suitable name.’
      inputs.file theTask.outputFile
      outputs.file outputFile
      doLast {
      Files.copy(theTask.outputFile.toPath(), outputFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
      }

      }

      //Inject the new task between the “package” and “zipalign” task.
      copyTask.dependsOn theTask
      project.tasks[“zipalign${capitalizedName}”].dependsOn copyTask
      }
      }
      }
      }

  4. Another approach is to rename the gradle sub-project (but not the directory), by putting the following in the root settings.gradle:

    rootProject.children.each {
    it.name = (‘app’ == it.name ? ‘YourAppName’ : it.name)
    }

  5. Erik, I appreciate your taking the trouble to explain this to me. However, I copied and pasted your artifacts.gradle file to my system and immediately encountered problems due to > above. I suspect the web system in use here is to blame. The error is:

    artifacts.gradle: 10: expecting ‘}’, found ‘-‘ @ line 10, column 35.

    Changing the HTML escape for it to the greater than sign and removing the space before the hyphen fixed the problem in this and the first line of artifacts.gradle.

  6. Thanks!

  7. Thank you

  8. I’m confused on where to place apply from: “../artifacts.gradle” in the build.gradle file? at the top, bottom, in the build types?
    I have put it in all of those places but the name of the apk file doesn’t change.

  9. And for Gradle 3.0.0. Change “output.outputFile =” to “outputFileName =”, and “variant.outputs.each” to “variant.outputs.all”

    android.applicationVariants.all { variant ->
    def appName
    //Check if an applicationName property is supplied; if not use the name of the parent project.
    if (project.hasProperty(“applicationName”)) {
    appName = applicationName
    } else {
    appName = parent.name
    }

    variant.outputs.all { output ->
    def newApkName = “${appName}-${output.baseName}-${variant.versionName}.apk”
    outputFileName = new File(output.outputFile.parent, newApkName)
    }
    }

    1. Thanks, I’ve updated.

      Note that you only need to assign the file name string to the outputFileName field.

  10. Thank you very much. It helped me a lot.

  11. Thanks. It helped me.
    Only statement needed some learnig was
    apply from: “../artifacts.gradle”

    Yes, I am not pro in Android Prog yet!

  12. Excellent material!

Leave a Reply

Close Menu