I believe that many Android developers are affected by the speed of Android’s build every day. Many people say that I only changed one line of code, but it took 2 minutes to compile, or some people say that I did not change anything, but had to wait so long, which seriously affected the work efficiency. The good news is that Android plug-in version 3.0.0 was launched at Google IO Conference, and we made a special share, introduced the new features of the plug-in, and shared some tips for improving the speed of Android construction with his family. I optimized our project according to the tips given by the god. The result made me very surprised, and the construction speed increased by more than 10 times. In this article, I will share with you my optimization road and the actual effect brought by each step of optimization. Before we talk about tuning measures, let’s look at the build performance of the project before tuning

./gradlew clean app:assembleDebug –profile

We use this command to perform the full-build process to measure build performance.





image.png

As can be seen from the Profile report, before optimization, the time for our project to execute a full-build was 2 minutes and 26 seconds, not always so much, but not much different. Let’s start our optimization journey.

1. Update Android Gradle Plugin to 3.0.0

Android Gradle Plugin version 3.0.0 has been updated with many new features and has solved many performance issues. Upgrading 3.0.0 requires upgrading the Gradle version and adding a Maven library.

Start by upgrading the distributionUrl in gradle-wrapper.properties

DistributionUrl = \ https\://services.gradle.org/distributions/gradle-4.0-milestone-1-all.zipCopy the code

Also add the Maven library address provided by Google

buildscript { repositories { ... // You need to add the following repository to download the // new plugin. maven { url 'https://maven.google.com' } } Dependencies {classpath 'com. Android. View the build: gradle: 3.0.0-1'}}Copy the code

Of course, the upgrade process is not only these two steps, the rest of the process you need to solve the problems you have encountered in Sync, because gradle has been upgraded to a large version, with some new functionality changes. Let me just talk about the problem I solved:

  1. The first problem is the change in the way dependencies are used. 3.0.0 introduces two new dependencies: Implementation and API. The difference between the two dependencies will be explained later
  2. The second problem is that the new plugin conflicts with the latest Butterknife plugin version 8.7+, which will cause errors when compiled

    Unable to find method ‘com.android.build.gradle.api.BaseVariant.getOutputs()Ljava/util/List

    The issue is already available on Github and the temporary solution is to drop to version 8.4

  3. The third problem is that BlockCanaryExt plug-in is used in our project. This plug-in will also compile and report errors, so it will be commented out first and wait for the author to solve it. In addition, we observed that the BlockCanary plug-in would seriously affect our compilation efficiency. The time of the plug-in task alone was more than 30 seconds, so if we could remove it or conditionally compile it, it would be greatly optimized.





image.png

After updating Android Gradle plugin and removing BlockCanayEx plugin, we can see the full build performance:





image.png

You can see that the compile time drops by almost a minute. This includes optimization time after removing the BlockCanary plugin





image.png

2. Avoid Legacy Multidex

Google has introduced MultiDex to solve the problem of 64K methods, but the approach of MultiDex is different on different VERSIONS of API, above API 21, because Android adopts a new runtime ART, All classESn. dex files will be merged into a.oat package at installation time. All you need to do is add a line multiDexEnabled True to the compile script. But below the API, you need to introduce the Multidex Support Library. And during compilation, it takes a long time to compile the script to decide which classes to put into the primary dex and which into the secondary dex. Below api21, this is called legacy multidex. During development, we can avoid the compilation performance cost of Legacy Multidex





image.png

We introduced the new Product Flavor: Development. So we compile like this:

./gradlew clean app:assembleDevelopmentDebug –profile





image.png

Compilation time dropped again by close to 40s, which was encouraging.

3. Import as few resources as possible





image.png

In this way, we only introduce English resources and xxhdPI resources in the development environment to reduce the packaging time. As can be seen from the profile below, the reduction of full-build time by 1 second still has some effect.





image.png

4. Disable Png Cruncher

Aapt processes your PNGS to make them smaller in size. This step is called pnG-cruncher and reduces the size of apK. However, in development mode, this is not important, so we can disable PNG Cruncher in development mode





image.png

The devBuild project attribute needs to be worn during build

./gradlew clean app:assembleDevelopmentDebug -PdebBuild –profile

If you are compiling with Android Studio, you can specify it in preferences





image.png

The optimized build time is almost the same, but has been reduced by a fraction of a second





image.png

5. Avoid crashLytics alwaysUpdateBuildId

James also addressed the potential issue of crashLytics affecting compilation time, saying that a new buildId is generated every time crashLytics is compiled after the fabric plug-in is applied. The compilation was slow, and our project also used crashLytics, so optimization was necessary

buildTypes {
        debug {
            signingConfig signingConfigs.debug
            ext.alwaysUpdateBuildId = false
        }
}Copy the code

The result is obvious, with full-build time dropping by about 6s, so it looks like CrashLytics is really affecting compilation performance





image.png

6. Use gradle. Cache

Gradle3.5 has introduced a new cache mechanism. Unlike BuildCache introduced in Android Studio2.3, which caches the output of each task, build cache only pre-dexed external libraries. Google hasn’t done much to improve this mechanic yet, but it’s recommended that we turn it on first.

org.gradle.caching = trueCopy the code

Take a look at a profile with cache enabled





image.png

After enabling cache, I noticed that our build time did decrease, but not as much as I expected. Isn’t the cache mechanism working?

7. Disable dynamic BuildConfig

So far, most of the optimizations we can do have been done, and we have achieved quite good results. Full-build time has decreased from 2m25s to 34s, and the speed has increased over 4x. But at the same time, I noticed that if I change a line of code or resource, I perform an incremental build

./gradlew app:assembleDevelopmentDebug -PdevBuild –profile

It takes 16 seconds, which is weird. In theory, you shouldn’t write one line of code, you should write it in seconds. To clarify this, we added the –info compiler option to find out more

./gradlew app:assembleDeveopmentDebug -PdevBuild –info | grep -A 3 Executing

Executing a task in Executing Executing a –info task will explain why the task is Executing. You can use the Executing Executing parameter to determine the cause of the execution





image.png

Find that the generateXXXBuildConfig and XXXjavaWithjavac tasks are executed because the buildConfig itemValues have changed. Look at build. Gradle

buildConfigField "long", "BUILD_TIMESTAMP", getTimeStamp() + "L"Copy the code

We dynamically changed one of the inputs in BuildConfig, getTimeStamp gets the current time, causing it to change with each build. Of course, this value is useful in a Release environment, but in development, it’s not necessary. It causes buildConfig.java to be re-generated, which in turn causes javac, dex, Package, Sign, and a number of other tasks to be re-executed and optimized

buildConfigField "long", "BUILD_TIMESTAMP", project.hasProperty("devBuild")?" 000000000":getTimeStamp() + "L"Copy the code

So if you’re in a development environment, this value is the same every time. Take a look at the optimized profile





image.png

Full-build takes an amazing 12s, at which point you run incremental compilation without changing a single line of code.





image.png

1.8s, this is what we want, normal effect.

Therefore, avoid using dynamic BuildConfig options in your development environment, as this can affect compilation speed and development efficiency. Not only BuildConfig, but also versionCode, which causes changes to the AndroidManifest file. In turn, resource files are repackaged, affecting compilation efficiency.

It’s amazing. Through 7 steps of optimization, we reduced the time of full-build from 146s to 12s. After optimization, the speed was increased by 12 times.





image.png

There are other optimization tips mentioned in Google IO share, including disabling multi-APK and using Instant Run, etc. Multi-apk is not used and instant Run is not used, so I didn’t elaborate on it. If you’re interested, you can go to YouTube.

In the next part of this post, Jerome, who is responsible for building the AS system, shares the changes brought about by the 3.0 plugin, and how these changes affect the speed of Android builds. I will share them in the next post