Here are 17 tips to Optimize your APP build Speed on Android

A longer build time will slow down the progress of the project, especially for large projects, app build time is ten minutes long, short a few minutes, long construction time has become a development bottleneck, this article according to the official Google documents, combined with some of his own understanding to provide some proposals to boost app build speed optimization.

1. Create a variant for the development environment

There are a lot of configurations that you need when you’re preparing for the release of your app that you don’t need when you’re developing your app. Starting an unnecessary build process can slow down your incremental or clean builds, so you need to build a variant that only keeps the configurations that you need when you’re developing your app. The following example creates a dev and prod variant (prod is the configuration of the Release version).

android { ... defaultConfig {... } buildTypes {... } productFlavors {// When building a variant that uses this flavor, the following configurations
    // override those in the defaultConfig block.
    dev {
      // To avoid using legacy multidex, set minSdkVersion to 21 or higher.
      minSdkVersion 21
      versionNameSuffix "-dev"
      applicationIdSuffix '.dev'
    }

    prod {
      // If you've configured the defaultConfig block for the release version of
      // your app, you can leave this block empty and Gradle uses configurations in
      // the defaultConfig block instead. You still need to create this flavor.
      // Otherwise, all variants use the "dev" flavor configurations.}}}Copy the code

2. Avoid compiling unnecessary resources

To avoid compiling and including resources you’re not testing (such as adding a native language and screen density resource), you can specify only one language and one screen density under your ‘dev’ flavor, as follows:

android {
  ...
  productFlavors {
    dev {
      ...
      // The following configuration limits the "dev" flavor to using
      // English stringresources and xxhdpi screen-density resources.
      resConfigs "en"."xxhdpi"}... }}Copy the code

The above configuration will limitdevThe variant only uses the English String resource and xxhdPI screen density resource.

3. Configure the Crushlytics built by debug to be unavailable

In the Debug build state, if you don’t need to run crash reports, you can make the plugin unavailable to speed up your build, as follows:

android {
  ...
  buildTypes {
    debug {
      ext.enableCrashlytics = false}}Copy the code

Crushlytics is a crash reporting analysis tool. We may not need it during development, so we do not need to open it. In our actual development, crash reporting SDK, data statistics SDK, etc. (such as 友 eng statistics, GrowingIO, Baidu statistics) are not available during development. To speed up the build.

4. Build your Debug version with static build configuration values

In general, use static/hard-coded values for the manifest file or resource file configuration during your debug build. If the value of your manifest or resource file needs to be dynamically updated every time you build it, Instant Run cannot perform code exchange – it must rebuild and install a new APK.

For example, using dynamic version codes, Version names,resources, or other build logic that changes the manifest file, you build the entire APK every time you want to perform a change, even though the actual change may only require a heat swap. If these build configurations need to be configured dynamically, separate them from your release build variant and keep their static values in your Debug build. As shown in the build.gradle file below:

int MILLIS_IN_MINUTE = 1000 * 60
int minutesSinceEpoch = System.currentTimeMillis() / MILLIS_IN_MINUTE

android {
    ...
    defaultConfig {
        // Making either of these two values dynamic in the defaultConfig will
        // require a full APK build and reinstallation because the AndroidManifest.xml
        // must be updated (which is not supported by Instant Run).
        versionCode 1
        versionName "1.0". }// The defaultConfig values above are fixed, so your incremental builds don't
    // need to rebuild the manifest (and therefore the whole APK, slowing build times).
    // But for release builds, it's okay. So the following script iterates through
    // all the known variants, finds those that are "release" build types, and
    // changes those properties to something dynamic.
    applicationVariants.all { variant ->
        if (variant.buildType.name == "release") {
            variant.mergedFlavor.versionCode = minutesSinceEpoch;
            variant.mergedFlavor.versionName = minutesSinceEpoch + "-"+ variant.flavorName; }}}Copy the code

5. Use static version dependencies

When declaring dependencies in build.gradle files, you should avoid using + signs at the end of version numbers, such as: Com. Android. Tools. Build: gradle: 2. + for gradle check update, using dynamic version number will lead to an unknown version update, make it difficult to solve the version of the differences and build more slowly. You should use static or hard-coded version numbers instead. Such as: com. Android. Tools. Build: gradle: 2.2.2.

6. Set On demand to Enable

In order for Gradle to know exactly how to build your APP, build all modules and other dependencies for the system configuration project before each build (even if you only want to build or test one module), which makes building large multi-module projects very slow. Tell Gradle to configure only the Modules you want to build, using the following steps to make the on Demand configuration available

(1) On the menu bar, go to File -> Settings (on a Mac, select Android Studio ->Preferences)

(2) to navigate to the Build, Execution, Deployment – > Compiler

(3) Check box for Configure on demand

(4) click OK

As shown in figure:

on_demand.png

Create the Library module

Examine the code in your app and extract modularizable code from an Android Library Module. Modularizing your code in this way will allow the build system to compile only those modules that have changed and cache their build results for later builds to use. The same configuration of on Demand and parallel Project execution will be more efficient (when you turn these features on).

8 Create Tasks for the custom build logic

After you have created your Build profile, if it shows that a relatively long part of the build time was spent in the configure Project phase, review your build.gradle script. And find code that can be included in a custom Gradle Task by moving some build logic into a Task and running it as needed. The result can be cached for subsequent builds and the build logic can be executed in parallel (if you have project running in parallel). Read Gradle’s official documentation for more details. official Gradle documentation

Tip: If your build contains a lot of custom tasks, you may want to clean up your build.gradle file and use custom Task classes. Add your classes to the project – root/buildSrc/SRC/main/groovy directory, Gradle will automatically include them to the class path, for all the build project. Gradle file.

9, configure dexOptions and enable library pre-dexing

Dex-in-process: The new Android Studio 2.1 release has added a new feature called Dex in Process, which can greatly speed up recompilations and also improve Instant Run performance. See Faster Android Studio Builds with Dex In Process for more details

The Android plugin provides the dexOptions Script block, so you can configure the corresponding DEX build features, which can speed up the build:

(1) preDexLibraaies: Declare if you want to do dex preprocessing on dependent libraries to make your incremental builds faster, since this feature may slow your clean builds, so you may want to turn this feature off on your continuous integration server.

(2) maxProcessCount: Sets the maximum number of threads used when running dex-in-Process, the default is 4.

JavaMaxHeapSize: Set the maximum heap size for the DEX compiler. Instead of setting this property, you should increase the Gradle heap size (this heap size is valid for the DEX compiler when dex-in-process is available).

Example:

android {
  ...
  dexOptions {
    preDexLibraries true
    maxProcessCount 8
    // Instead of setting the heap size for the DEX process, increase Gradle's
    // heap size to enable dex-in-process. To learm more, read the next section.
    // javaMaxHeapSize "2048m"}}Copy the code

You should test these Settings by increasing their value, and then observe the effect through the profile. When you allocate too many resources to the process, there may be a negative effect.

10. Increase the heap size of Gradle and enable dex-in-Process

Dex-in-process allows multiple Dex processes to run in a single VM, which makes incremental and clean builds faster. By default, new projects created with Android Studio2.1 or later have enough memory allocated to enable this feature. If you are not creating projects with Android Studio2.1 or later, You need to set up a heap size of at least 1536MB for Gradle garrisers. The default is as follows:

gradle_heap.png

The following example sets the gradle heap size to 2048MB in gradle.properties:

org.gradle.jvmargs = -Xmx2048m // Set the Gradle heap size to 2GCopy the code

However, if you have a small memory machine, you may need to configure less memory for the IDE. Wondering how to change the amount of resources allocated to the IDE and Gradle’s impact on build performance, See the article profiling your build.

If the Module in your build. Gradle file for android. DexOptions. JavaMaxHeapSize defines a value, then you need to give gradle heap size set has a value of more than 512 MB, javaMaxHeapSize The value must be at least 1536MB. For example, if you set javaMaxHeapSize to 1280MB in build.gradle, you need to set the gradle heap size to at least 1792MB(1280 + 512). build.gradle:

 dexOptions {
        javaMaxHeapSize "1280m"
    }Copy the code

gradle.properties:

org.gradle.jvmargs = -Xmx1792mCopy the code

11. Convert the image to WebP format

WebP is an Image file format that provides lossy compression like JPEG and transparency like PNG, but at the same time has better compression quality than either JPEG or PNG. It reduces the size of Image files without having to compress them at build time, so it can speed up builds. Especially if your APP uses a lot of images. However, when decompressing WebP images, your device’s CPU usage will increase slightly. Android Studio makes it easy to convert your images to WebP.

Tip: In addition, converting project images to webP format is another direction to optimize the size of APK. WebP has been supported since Android native 4.0, which provides the same quality as JPEG and PNG images but smaller size. There are no adaptation issues.

PNG Crunching is forbidden

If you can’t (or don’t want to) convert your PNG images to WebP, you can still speed up your build by disabling automatic compression every time you build your app. To disable this optimization, add the following code to build.gradle:

android {
  ...
  aaptOptions {
    cruncherEnabled false}}Copy the code

13. Use Instant Run

Instant Run significantly reduces the time it takes to update an app by pushing certain code and resource changes without having to build a new app, and in some cases without even restarting the current activity. Use Instant Run by clicking Apply Changes(yellow ⚡️ icon). It turns on by default when you do the following:

  • Build your app with debug build variants
  • Gradle plugin version 2.3.0 or higher
  • Set this in build.gradle at the Module levelminSdkVersion15 or higher
  • Release your app on Android 5.0(API Level 21) or higher with a clickRun

    .

14. Use the build cache

At the time you build your project, the build cache stores certain artifacts (such as AAR packages and remotely dependent pre-dexed) generated by Android Gradle plug-ins. When you use caches, your clean builds are faster because subsequent builds of build systems can simply reuse their caches without recreating them.

New projects using Android Gradle 2.3.0 or later have build caching enabled by default (unless you manually disable it). Read Accelerate Clean Builds with Build Cache.

Prohibit the use of annotation handlers

With Gradle 2.1 you can build Java incrementally. Incremental builds are not available when using annotation handlers. Avoid annotation handlers if you can, allowing you to benefit from building only changed classes. (Improves compile time)

16. Profile your Build

In a large project (or one that implements a lot of custom build logic), you may need to dig deeper into the build process to find bottlenecks by analyzing how long each Gradle Task executes at each stage of the build life cycle. For example, if your build data shows that Gradle spent a lot of time configuring your project, it is recommended that you leave your custom build logic out of the configuration phase. Also, if the mergeDevDebugResources task consumes a lot of build time, this means that you need to convert images to WebP format or disable PNG Crunching(Optimization tip 11 and 12)

Increasing your build speed through build analysis usually requires running your build with analysis on, modifying the build configuration multiple times, and analyzing and observing changes in the results.

To generate and View the build profile, perform the following steps: 1. Open the project with Android Studio and select View -> Tool Windows -> Terminal to open the command line

When you analyze your build, you need to perform a clean Build operation between each build because Gradle skips tasks that have not changed. The second build without changing the input will usually run faster because tasks are not rerun, so running a Cleantask between builds ensures that you analyze the entire build process.

// If you use./gradlew on Mac or Linux
gradlew cleanCopy the code

3. Select one of the product flavors and run the debug build, for example, dev flavor.

gradlew --profile --recompile-scripts --offline --rerun-tasks assembleFlavorDebugCopy the code

Command analysis:

  • --profileOpen profiling:
  • --recompile-scripts: Forces script recompilation to skip cache
  • --offline: Disables Gradle from getting offline dependencies. This ensures that any delays are caused by Gradle trying to update dependencies and do not mislead your analysis data. You should be ready to build a project to make sure Gradle has downloaded and cached dependencies.
  • --rerun-tasks: forces Gradle to return all tasks and ignore any task optimizations.

4, after the building, the project – root/build/reports/profile/directory:

profile_build.png

5 Right click on profile_timestamp.html and select Open in browser to see the graph below. You can observe each TAB in the report to understand your build. For example, Tasks Execution shows the Execution time of each task.

profile_in_brower.png

Task Execution shows the Execution duration of each task.

task_execution.png

6. Optional: Before making any changes to the Project or build configuration, repeat Step 3 several times, but remove the –rerun-tasks flag, Since Gradle tries TO save time, it does not re-execute tasks whose input has not been modified (they are marked as up-to-date under the Task Execution TAB, as shown below: ), you can identify which tasks are not performed, for example, if: app: processDevUniversalDebugManifest has not been marked as the UP – TO – DATE, then it means you build configuration is every build dynamic update of the Manifest file. However, there are some task or need to perform every time, for example: : app: checkDevDebugManifest

profile_up_to_date.png

Now that you have a build analysis report, you can start looking for optimization opportunities by looking at each TAB of the build report. Some build configurations need to be experimented with because they will benefit differently in different projects or workspaces. For example, large projects based on large amounts of code may benefit from using obfuscation, cleaning up useless code, and reducing THE size of APK. However, small projects may benefit from closure confusion (which can be time-consuming). It is also possible to increase or decrease the heap size of Gradle on a machine with less memory.

17. Componentization of projects

For large projects, the above optimization suggestions may have certain effects, but the construction speed is still a little slow, so we can consider the composition, the project is divided into individual components, the development environment each module is an APK, when released, each module is a lib for the main project to use. The length is effective, here will not introduce componentization in detail, now componentization is a trend, if you have energy or strength, componentization is a very good choice.

The last

These are some of the optimizations you can use to solve your app’s slow build times. If you feel your project is slow build times, you can try these optimizations. If you have any questions, please leave them in the comments section. If you have any better optimization suggestions, you can also leave a comment below, I will add to the post.

reference

Optimize Your Build Speed Gradle