Gradle – yay! Right?
Not quite right. Gradle often gives me a headache. I was so happy to switch from Eclipse to Android Studio back in the early preview days. It was a lot faster then. Sadly, I got a feeling it became much slower within time when it comes to build time. It’s mainly gradle’s fault. Hopefuly this gets better with Android Studio 2.0, I haven’t tested it yet. It’s in early beta stage right now.
Something in return
Like every difficult relationships, the one with gradle has it’s bright ups. Let me show you what do you get in return for the terrible build time. At least the part of it I know about.
Basics
Every project you start comes with 2 build.gradle files. One applies to the whole project and the second to the app module. Later you can add more modules, like some internal libraries. Every module will get it’s own build.gradle file.
You have them all listed nicely when switching to Android grouping style in project inspector.
App version
Tired of incrementing version code or naming each version you build or release? Gradle can help you with that. If you’re also using git, and I hope you do, I can show you an easy way to automate that.
Gradle uses groovy programing language. Try putting this code on the top of build.gradle file of your app module:
import java.text.SimpleDateFormat def gitSha() { return "git --git-dir=${rootDir}/.git --work-tree=${rootDir} rev-parse --short HEAD".execute().text.trim() } def buildTime() { def df = new SimpleDateFormat("ddMMyyyy") df.setTimeZone(TimeZone.getTimeZone("UTC")) return df.format(new Date()) } def gitCount() { return "git --git-dir=${rootDir}/.git --work-tree=${rootDir} rev-list HEAD --first-parent --count".execute().text.trim() } def gitTag() { return "git --git-dir=${rootDir}/.git --work-tree=${rootDir} describe --tags --abbrev=0".execute().text.trim() } def getAutoVersionName() { return this.hasProperty("publish") ? "${gitTag()}" : "${gitTag()}-${gitSha()}-${gitCount()}-${buildTime()}" } def getAutoVersionCode() { return gitCount().toInteger() }
Then change those 2 lines to use your newly written functions instead making you type it all over again every build:
defaultConfig { versionCode getAutoVersionName() versionName getAutoVersionCode() }
How does it work? Well, the naming convention is up to you. With some groovy you can modify it. This is just a sample of automation process that uses count of commits as a version code.
You’re ready to build the apk the cool way. I’m using console for that. Let’s start from the kind of build you don’t intend to upload to Google Play. Enter your project directory in console and input command:
./gradlew assembleProdRelease
If everything goes fine, you’re going to see BUILD SUCCESSFUL at the end. But where is the APK? How is the file named?
Look for apk under {project_directory}/{app_module}/build/outputs/apk
The file should be named app-prod-release.apk
That’s still “meh” for me. How am I suppose to know that? Maybe I want to change file name as well? In this case I paste this code into the app build.gradle inside android {} section:
android.applicationVariants.all { variant -> variant.outputs.each { output -> output.outputFile = new File(output.outputFile.parent, "myappname-${variant.baseName}.apk") } // print the APK path after assemble is done variant.assemble.doLast { println "\nAPK: ${variant.outputs[0].outputFile}" println "Version: ${getAutoVersionName()} (${getAutoVersionCode()})" } }
Now build again and see the console output. There are additional lines for you there:
APK: /Users/jacekkwiecien/Documents/myapp/app/build/outputs/apk/myappname-prod-release.apk Version: 0.3.6-406ee19-195-22122015 (195) BUILD SUCCESSFUL
Now you know where to look. Also the file is named nicely. Cool, right? 195 is version code. The longer string is a version name. What does it contain? Whatever we put in there. Let’s look again on the function that generates the name:
def getAutoVersionName() { return this.hasProperty("publish") ? "${gitTag()}" : "${gitTag()}-${gitSha()}-${gitCount()}-${buildTime()}" }
This is a non-publish build, so we look on this part only:
"${gitTag()}-${gitSha()}-${gitCount()}-${buildTime()}"
First we have a git tag, if it’s there. I use tags to input version names myself when I publish the app. Later git sha comes, which is the sha of the latest commit. Finally we have git commit count and current time.
So what if it’s a build we want to publish?
First – there is an important thing to remember. Version code should be always bigger than the previous build. It doesn’t have to be latest + 1 though. If you want to release APK to Google Play – always build from the master branch, otherwise you might end up with the code lower than the latest one.
On the master branch, or whatever branch you picked to be the release branch? Good, we can choose the version name for the upcoming release. Let’s assume the latest version was named 0.3.6. In this case we want the next one to be 0.3.7. We want to set a git tag for that. To set the tag input following in the command line:
git tag -a 0.3.7
This will ask you for some comment message. You can input anything there, it’s not very relevant. Let’s just retype 0.3.7 in there. Your latest commit is tagged. We can build the APK now. Please note that we’re adding a publish param to the command.
./gradlew assembleProdDebug -Ppublish=true
The output:
APK: /Users/jacekkwiecien/Documents/myapp/app/build/outputs/apk/myappname-prod-release.apk Version: 0.3.7 (195) BUILD SUCCESSFUL
Now the users in the Google Play will see 0.3.7 as the version name instead some hard to read name that we used for non-publish builds.
What’s next?
Liked the version automation? Stay tuned for more. In the next part I’m going to write about easier but also very helpful features. Even if you lack some understanding of this, give it a shot just by pasting the code into your configuration file. Still good thing to brag about around your colleagues 😉