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:
- 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
-
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
-
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