In my previous posts, I wrote about several gradle tricks increasing productivity. I was also complaining about gradle overhead to our build times.
Recently, I struggled with a new project. At some stage, even the slightest change was making me wait more than one minute – 1:30 to be precise! This was the tipping point. I started optimizing and here is the story of how I did it. To get you curious – I got down to a couple of seconds!
Dev flavor with min sdk set to 21
Of course, I have that. My project has 3 flavors – prod, stage and dev. The app is available from sdk 16, but to make my daily work faster, I use dev flavor with minSdkVersion 21
. There are some perks of working with flavor set to higher min API than the prod one. Especially, when your app has over 65k methods and needs to use multidex
. But that I’ll cover in a different post. The thing is, in my case, minSdkVersion 21
didn’t really help.
No changes – still ages of building – proguard!
First, I noticed that I have to wait for long every time I run the app from Android Studio. That was suspicious. So, I went through my app build.gradle and found this:
buildTypes { release { minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('./proguard-android.txt'), 'proguard-rules.pro' signingConfig signingConfigs.release } debug { minifyEnabled true proguardFiles getDefaultProguardFile('./proguard-android.txt'), file('./proguard-rules.pro'), file('./proguard-debug.pro') signingConfig signingConfigs.debug }
Oh god… I’ve been using the proguard on debug builds all this time. No wonder it consumes so much time on every attempt… I remember I did that to overcome 65k method limit… before I decided to use minSdkVersion 21
. I did that because I couldn’t just set minSdkVersion 21
to prod and stage builds, as they had to be minSdkVersion 16. And I didn’t want to use multidex
… well, that was wrong. I finally got this stuff together and setup my project to user multidex on prod and stage builds, and to use minSdkVersion 21
instead on dev build. In the end, the setup changed to this:
buildTypes { release { minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' testProguardFile 'proguard-debug.pro' signingConfig signingConfigs.release } debug { minifyEnabled false signingConfig signingConfigs.release } }
And the flavors:
prod { multiDexEnabled true } stage { multiDexEnabled true } prod { minSdkVersion 21 }
Setting flavors this way required some additional setup if I wanted to run prod or stage on the device that runs older android, but that I’ll cover some other time. This step, however, got me from 1:30 to 1:10.
Excluding unnecessary artifacts
I came to the conclusion that I didn’t really need to build release apk from dev flavor. So, I excluded and gained additional couple of seconds this way:
android.variantFilter { variant -> if (variant.buildType.name.equals('release') && variant.getFlavors().get(0).name.equals('dev')) { variant.setIgnore(true); } }
Gradle 3.0
I’ve heard reports of builds speeding up with, currently experimentally supported by Android, gradle 3.0. And I decided to give it a try. It requires some work, though… so it might not be worth the efforts for some developers.
./gradlew wrapper --gradle-version=3.0
command executed from the command line, while in the project repo upgraded my gradle wrapper.
Then I had to upgrade android gradle plugin to beta one:
classpath 'com.android.tools.build:gradle:2.2.0-beta3'
It’s worth to upgrade buildToolsVersion to the latest one in the process.
Now… if you encounter the following error – Unsupported major.minor version 52.0
– you have to change your jdk to 1.8+. It probably won’t be enough to alter it in project preferences in Android Studio. You might have to edit the info.plist
file of Android Studio to enforce using 1.8. Or you can upgrade to Android Studio 2.2. It’s in beta stage now but I’ve been using it without any serious concerns so far. And it might be good for you to know that it has its info.plist
already prepared accordingly.
Instant run
When it works, it’s great. It’s especially helpful when working on small changes in layout xml files. It might rerun the app with the changes introduced in seconds. It even keeps your activity, so you don’t have to click through the app again. However, this doesn’t work every time. I’m not sure why but I’m guessing that the activity rerun this way might not be firing all the regular callbacks… but then you can go one step back and restart the activity you’re working on.
Offline work
Last but not least, that was something that after all the previous steps got me down to a couple of seconds! I had no idea how does the gradle offline work affect the build. I had never used the option that might be enabled in Android Studio. I was always online after all, why bother then?
But let’s start from the beginning. I did /.gradlew assembleDevDebug --profile
For some reason, gradle was doing a lot of work under the Dependency resolution section. This section itself took 50 seconds! In there it compiled every possible artifact, including tests. Here is just a small part of the list:
Dependencies Duration
All dependencies 50.059s
:app:_prodDebugCompile 6.382s
:app:_stageDebugCompile 3.628s
:app:_devReleaseCompile 3.317s
:app:_mockDebugApk 3.259s
:app:_stageReleaseCompile 3.144s
:app:_prodDebugApk 3.126s
I’m not sure why it does that, maybe I don’t get something but the bottom line is: offline work makes it mostly go away! Boom – now sit back and enjoy your work life.
What do I do with all that time now?
No more funny sites browsing during the builds, eh? I guess it’s a fair deal – you’ll be done with your work faster! 😉 But seriously, try this and share your thoughts. Did it help you? How much? I’m all curious!
I wanted to thank my fellow colleague Kamil Zych for bearing me during the whole process during the weekend! Beers on me!