The most comprehensive compile and build optimization for Android Studio! ! !

Summary: Android Xiaobai's Growth Path_Knowledge System Summary【Continuously updated...】

problem background

The company's project uses Android Studio and Gradle to compile. Every time the code is modified (even if it is a line modification), it takes three to four minutes or even longer to compile and run again. It lasted more than ten minutes during the first compilation, which greatly affected the development efficiency. As the saying goes, if a worker wants to do a good job, he must first sharpen his tools. This is a wave of optimization for compilation speed, let's start step by step!

Gradle build process

First understand the Gradle construction process, which is divided into three stages as a whole:

  • Initialization phase: Gradle supports single-project and multi-project construction. In the initialization phase, setting.gradleGradle reads the modules that need to participate in the construction and creates a Project instance for each module.
  • Configuration phase: configure the project module and the scripts it needs to execute, that is, build.gradleother files
  • Execution phase: start executing the configured script task

After understanding these processes in general, we can start to optimize them from these processes

Relevant instructions before optimization

Information about the computer currently used as verification:

  • Computer name: MacBook Pro
  • System: Mac
  • Memory: 8GB 1867MHz DDR3
  • Processor: Dual Core Intel Core i5 2.7GHz
  • AS version: 4.1.1

Three angles to verify compilation speed:

  • rebuild all projects, all compile
  • Add a method to trigger java recompilation
  • Modify an xml to trigger resource recompilation

Compare data acquisition methods: rebuild three times to get the lowest value, modify method or xml try five times to get the lowest value

Related instructions:

  • Because the computer sometimes freezes or other reasons affect the compilation, it will take a long time to compile a certain time, so the average value cannot be used as a reference
  • Compilation is generally faster than once, because Android studio comes with a cache
  • The development stage itself will not change the configuration all the time, so taking the minimum value can basically simulate daily use
  • Because the main project module is relatively large, the code of the main project module is used for verification. If the component code is modified, it will generally take less time
  • The scheme used for each data statistics inherits all the previous optimization schemes
  • Modify method and xml with apply changes

Optimization

From the overall construction process, we can know that we need to optimize from three aspects as a whole:

  • Initialization speed optimization
  • Configuration speed optimization
  • Execution speed optimization

Among them, the execution process accounts for the largest proportion, so the focus is on execution speed optimization

Initialization speed optimization

Generally, the initialization process has fewer tasks and is already fast, but some processing can still be done to achieve the best state:

  • When the degree of componentization is high, some components do not need to be introduced in the process of developing a specific function. At this time, the setting.gradlecomponent modules that do not need to be introduced can be removed in , which can reduce the initialization time
  • setting.gradleTry not to write too much code before the include

Configuration speed optimization

The configuration phase is mainly to build.gradleanalyze each, so you can pay attention to the following points:

  • Import modules on demand, reducing build.gradleparsing
  • build.gradleDo as little time-consuming operations as possible, such as reading the system time and dynamically configuring the name composition of the apk
  • It is not necessary to perform tasks in the development stage. You can write judgments to avoid the configuration of these tasks, such as some bytecode stubs, performance monitoring and the like

Execution speed optimization

There are a large number of tasks that need to be performed at this stage, so there are many optimization points

Configure Gradle

Enable parallel compilation

After it is turned on, multiple tasks will be executed in parallel, greatly reducing compilation time, just gradle.propertiesadd in:

org.gradle.parallel=true

increase compile memory

Since everyone's computer configuration is different, the specific setting of how much memory needs to be reasonably configured according to individual circumstances. Generally, there gradle.propertiesare already relevant configurations in the computer, and the configuration can be modified, for example

org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8

build.gradleAt the same time , modify it in the main project module :

dexOptions {
    
    
	javaMaxHeapSize "4g"
	}

It is worth noting that javaMaxHeapSizethe value of , needs to be org.gradle.jvmargsless than the set value 512m, and the org.gradle.jvmargshigher the value of , the better. According to the verification, it is best to configure it as 1/3 of the system memory, and at most it should not exceed 1/2. It is shown in some documents that javaMaxHeapSizeconfiguration is no longer required in higher versions javaMaxHeapSize, only configuration is required org.gradle.jvmargs. After consulting a lot of information, it is not clear, so it is configured for the time being.

Enable on-demand builds

Modules that have not changed are no longer compiled, which is very suitable for already componentized projects. gradle.propertiesAdd:

org.gradle.configureondemand=true

Enable build caching

Directly use the previously generated cache and no longer build it. It will be displayed after the task at build time FROM CACHE. gradle.propertiesAdd:

org.gradle.caching=true

Enable incremental annotation compilation

Support incremental compilation of annotations, and will not re-trigger compilation (need to be removed in higher versions of gradle), gradle.propertiesadd in:

android.enableSeparateAnnotationProcessing=true

Data comparison (parallel compilation is enabled before optimization, so the following time does not include optimization of parallel compilation):

rebuild Modification method modify xml
Before configuration optimization 4m46s 46s 22s
After configuration optimization 2m39s 42s 20s
income 44% reduction 8% reduction 9% reduction

Configure the AS

Turn on offline mode

After the offline mode is turned on, it will not detect whether there is an update to the dependency at the beginning, nor will it download the updated dependency. The first build cannot be enabled, otherwise the build cannot be completed. Subsequent builds can be enabled, and in some cases it will be greatly improved. Improve compilation speed, strongly recommended for development phase. Click the button of the icon in the picture below to start the offline mode, some versions are displayed as an icon similar to wifi, click again to cancel the offline mode:

insert image description here

Change AS memory size

Click on the menu item of AS Helpand select Change Memory Settingsthe option. As shown in the picture:

insert image description here

The pop-up box as shown in the figure below pops up, Maxinum Heap Sizemodify to an appropriate value, and the specific modification value is selected according to the memory configuration of your own computer

insert image description here

Data comparison:

rebuild Modification method modify xml
Before the AS configuration is modified 2m39s 42s 20s
After the AS configuration is modified 2m16s 37s 16s
income 14% reduction 11% reduction 20% reduction

Update the latest Gradle version

Since gradle generally further optimizes the build speed in new versions, keeping the latest gradle version can obtain the best build experience. The update method is as follows:

  • First gradle-wrapper.propertiesconfigure the gradle version in:

    distributionUrl=https\:``//services.gradle.org/distributions/gradle-6.7.1-all.zip
    
  • build.gradleThen update the gradle plugin version in the root directory :

    classpath 'com.android.tools.build:gradle:4.1.1'
    

Possible problems and solutions after updating to 6.x or higher:

  1. Exception reported:

    FAILURE: Build failed with an exception.
    * What went wrong:
    A problem occurred configuring project ':live'.
    > Failed to notify project evaluation listener.
       > org.gradle.api.tasks.TaskInputs.property(Ljava/lang/String;Ljava/lang/Object;)Lorg/gradle/api/tasks/TaskInputs;
       > Could not get unknown property 'additionalParameters' for task ':live:dexBuilderDebug' of type com.android.build.gradle.internal.tasks.DexArchiveBuilderTask.
    * Try:
    Run with --info or --debug option to get more log output. Run with --scan to get full insights.
    * Exception is:
    org.gradle.api.ProjectConfigurationException: A problem occurred configuring project ':live'.
        at org.gradle.configuration.project.LifecycleProjectEvaluator.wrapException(LifecycleProjectEvaluator.java:75)
        ......
        at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
    Cause 2: groovy.lang.MissingPropertyException: Could not get unknown property 'additionalParameters' for task ':live:dexBuilderDebug' of type com.android.build.gradle.internal.tasks.DexArchiveBuilderTask.
        at org.gradle.internal.metaobject.AbstractDynamicObject.getMissingProperty(AbstractDynamicObject.java:85)
        ......
        at java.lang.Thread.run(Thread.java:748)
     
    * Get more help at https://help.gradle.org
    

    This is caused by the current greenDao version being too low. Just update the greenDao version and modify the version under the root directory build.gradle:

    classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0'
    
  2. Exception reported:

    FAILURE: Build failed with an exception.
    * Where:
    Build file '/Users/uxin/AndroidStudioProjects/Pika/UXLiveOverseas/live/build.gradle' line: 253
    * What went wrong:
    A problem occurred configuring project ':live'.
    > Could not get unknown property 'additionalParameters' for task ':live:dexBuilderDebug' of type com.android.build.gradle.internal.tasks.DexArchiveBuilderTask.
    * Try:
    Run with --info or --debug option to get more log output. Run with --scan to get full insights.
    * Exception is:
    org.gradle.api.ProjectConfigurationException: A problem occurred configuring project ':live'.
        at org.gradle.configuration.project.LifecycleProjectEvaluator.wrapException(LifecycleProjectEvaluator.java:75)
        ......
        at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
    Caused by: groovy.lang.MissingPropertyException: Could not get unknown property 'additionalParameters' for task ':live:dexBuilderDebug' of type com.android.build.gradle.internal.tasks.DexArchiveBuilderTask.
        at org.gradle.internal.metaobject.AbstractDynamicObject.getMissingProperty(AbstractDynamicObject.java:85)
        ......
        at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
     
    * Get more help at https://help.gradle.org
    

    In sdk 21the past, third-party dependencies were generally used to multidexenable dex blocks, and sdk 21later, the official comes with it multidex, so the third-party needs to be removed multidex:

    • In the main project module build.gradle, delete the multidex dependencies and custom tasks:

      //implementation 'androidx.multidex:multidex:2.0.0'
      
      //    afterEvaluate {
              
              
      //        tasks.matching {
              
              
      //            it.name.startsWith('dex')
      //        }.each { dx ->
      //            if (dx.additionalParameters == null) {
              
              
      //                dx.additionalParameters = ['--multi-dex']
      //            } else {
              
              
      //                dx.additionalParameters += '--multi-dex'
      //            }
      //        }
      //    }
      
    • multidexInitialization deleted in custom Application class :

      //import androidx.multidex.MultiDex;
      
      //MultiDex.install(this);
      
  3. Exception reported:

    FAILURE: Build failed with an exception.
    * Where:
    Build file '/Users/xxx/Projects/xxx/xxx/xxx/build.gradle' line: 1
    * What went wrong:
    A problem occurred evaluating project ':live'.
    > Failed to apply plugin 'com.android.internal.application'.
       > The option 'android.enableSeparateAnnotationProcessing' is deprecated.
         The current default is 'false'.
         It was removed in version 4.0 of the Android Gradle plugin.
         This feature was removed in AGP 4.0
    * Try:
    Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
    * Get more help at https://help.gradle.org
    

    This is the newly added annotation incremental compilation field has been removed in the new version of gradle, so it should be removed and gradle.propertiesdeleted in:

    #android.enableSeparateAnnotationProcessing=true
    
  4. Exception reported:

    private static final String LIBRARY_VERSION = ". Version: " + BuildConfig.VERSION_NAME;
                                                                               ^
    符号: 变量 VERSION_NAME
    位置:BuildConfig
    

    Since there is not much difference between versionNameand versionCode, in order to prevent concept confusion, the official removed it VERSION_NAME, so if we still need to use it in our project, it can be configured with the custom buildConfigone VERSION_NAMEin the module that reported the error :build.gradle

    defaultConfig {
          
          
        minSdkVersion MIN_SDK_VERSION as int
        targetSdkVersion TARGET_SDK_VERSION as int
        versionCode 2
        versionName "1.0.1"
        buildConfigField 'String', 'VERSION_NAME', "\"" + versionName + "\""
    }
    
  5. Exception reported:

    
    /Users/xxx/Projects/xxx/xxx/xxx/xxx/src/main/java/com/xxx/base/utils/Utils.java:86: 错误: 找不到符号
          intent.putExtra(PAKAGENAME, BuildConfig.APPLICATION_ID);
                                                               ^
      符号: 变量 APPLICATION_ID
      位置:BuildConfig
    

    In the higher version of Gradle, in order to prevent confusion between the id and the id of the appication itself when used in the library BuildConfig.APPLICATION_ID, it needs to be changed to a new field:

    BuildConfig.LIBRARY_PACKAGE_NAME
    
  6. Exception reported:

    
    /Users/xxx/Projects/xxx/xxx/xxx/xxx/src/main/java/com/xxx/base/view/ShareScreenShotDialog.java:205: 错误: 找不到符号
                                            shareInfo.setWeiboCopyWriter(String.format(mContext.getString(R.string.novel_share_intro_wb_empty),
                                                                                                                  ^
      符号: 变量 novel_share_intro_wb_empty
      位置: 类 string
    

    The higher version of Gradle does not allow the default language configuration in the language configuration to be empty, so you need to default stringadd the part of the string that reported the error in

  7. Exception reported:

    Execution failed for task ':live:transformClassesWithAjxForRelease'.
    > Cannot cast object 'com.android.build.gradle.internal.pipeline.TransformTask$2$1@6fe77eee' with class 'com.android.build.gradle.internal.pipeline.TransformTask$2$1' to class 'com.android.build.gradle.internal.pipeline.TransformTask'
    

    This is because aspectjxthe version is too low, just update the version and modify it in the root directory build.gradle:

    classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.10'
    

Data comparison:

rebuild Modification method modify xml
Before Gradle update 2m16s 37s 16s
After Gradle update 1m15s 33s 12s
income 44% reduction 10% reduction 25% reduction

Convert Module source code to aar

As the business volume increases, the introduction of modules will also increase, and each module will take a certain amount of time to compile. Even if the new version of gradle does not compile unmodified and cached modules, it still needs to be cached. certain time. After converting the module into aar, it is no longer necessary to compile or cache every time, which can reduce part of the time

Module to aar optimization steps are as follows:

  1. Build->Make module xxxGenerate aar for each module build/output/aarin

  2. Create a new module, name it randomly, copy the aar generated by other modules to the libs of the new module, and copy the aar packages under the libs of other modules to the libs of the new module (because the aar package will not contain its own dependencies Other aar packages), you can also not create a new module, directly put it under the libs of the main project

  3. Delete other files and folders under the src of the new module, leaving only the main folder and AndroidManifest.xmlfiles

  4. Delete AndroidManifest.xmthe application in

  5. In the new module, build.gradlecopy all the aar dependencies referenced by other modules, and rely on the aar made by several other modules at the same time

  6. Modify in the root directory build.gradle:

    flatDir {
          
          
            dirs 'libs',project(':aar的module').file('libs')
    }
    

The benefits of using a new module to store aar:

  • Separated from the main project module, there is no need to copy aar to the libs of the main project, and there is no need to write dependencies in the dependencies of the main project module
  • You can setting.gradledirectly judge whether to choose aar compilation or source code compilation in
  • When a component is updated, you can compile it and directly replace the aar file, and update it immediately

extension:

You can make a global variable to control the use of component source code or aar, the operation steps are as follows:

  1. setting.gradleAdded global switch in

    #是否修改组件代码
    isModifyInComponent=false
    
  2. Modify in build.gradle of the main project module:

    def modifyInComponent = isModifyInComponent.toBoolean()
    flatDir {
          
          
        if (modifyInComponent) {
          
          
            dirs 'libs',
                    project(':源码的module').file('libs'),
        }else {
          
          
            dirs 'libs',project(':aar的module').file('libs')
        }
    }
     
    if (modifyInComponent) {
          
          
        implementation project('源码的module')
    } else {
          
          
        implementation project('aar的module')
    }
    
  3. Modify in setting.gradle:

    include ':app
    if (isModifyInComponent.toBoolean()) {
          
          
        include ':源码的module'
    } else {
          
          
        include ':aar的module'
    }
    

The best way is to build a private server maven warehouse to store aar, and the direct dependency is completed, which is more convenient and easy to manage. I will write the corresponding article later here

Data comparison:

rebuild Modification method modify xml
Component source code 1m15s 33s 12s
Component aar 50s 28 p 11s
income 33% reduction 15% reduction 8% reduction

Customized tasks

During the build process, some tasks are performed to optimize the app. These tasks do not need to be executed during the development process, they only need to be executed during official packaging. These tasks can therefore be temporarily turned off to reduce execution time. You can introduce a global variable as a switch, and then make judgments where these task plugins are introduced, and execute them as needed.

The operation steps are as follows:

  1. gradle.propertiesDefine a switch in

    #开启快速编译模式,快速编译舍弃了一些配置,可以较快编译执行app,适合开发调试阶段
    isFastBuildMode=false
    
  2. Make judgments in the main project module build.gradle, such as the following:

    def fastBuildMode = isFastBuildMode.toBoolean()
    if (fastBuildMode) {
          
          
        repositories {
          
          
            flatDir {
          
          
                    dirs 'libs',
                            project(':aar的module').file('libs')
            }
        }
    } else {
          
          
        apply plugin: 'org.greenrobot.greendao'
        apply plugin: 'walle'
        apply plugin: 'com.didiglobal.booster'
        apply plugin: 'android-aspectjx'
        repositories {
          
          
            flatDir {
          
          
                    dirs 'libs',
                            project(':源码的module').file('libs')
            }
        }
        greendao {
          
          
            schemaVersion 2
            targetGenDir 'src/main/java'
        }
        walle {
          
          
           ...
        }
     
        aspectjx {
          
          
            exclude 'com.alipay', 'com.tencent', 'com.squareup.leakcanary'
        }
    }
     
    if (fastBuildMode) {
          
          
        ndk {
          
          
            abiFilters 'armeabi-v7a'
        }
        resConfigs "cn", "xhdpi"
     
    } else {
          
          
        ndk {
          
          
            abiFilters 'armeabi-v7a', 'arm64-v8a'
        }
    }
    

Note: Turning on the fast compilation switch will close some tasks, so it may cause some unpredictable problems. If an exception occurs during the debugging process, you can turn off the fast compilation switch and try again. Remember to turn off the fast compilation switch when packaging the apk for release.

Data comparison:

rebuild Modification method modify xml
turn off fast compilation 50s 28 p 11s
Enable fast compilation 38s 13s 11s
income 24% reduction 53% reduction flat

Maven proxy

As mentioned earlier, you can create a private server maven to store the generated aar. In fact, you can also use a private server maven to proxy the dependencies that need to be downloaded, put them in the internal network warehouse, and read them directly from the internal network when needed, without going to the remote maven. Warehouse reading, which is very helpful for some projects that use maven warehouses on the external network. If you don’t want to build them yourself, you can also use Alibaba Cloud’s mirrored maven warehouse. There are some commonly used warehouse mirrors in it. The construction method will be updated later article to illustrate

Use a remote shared build cache

The way to enable the cache was mentioned earlier, but that is only for the local cache. When compiling for the first time, the cache is empty, and it still takes a lot of time to compile. But in the company's development process, there are usually colleagues' machines or CI builds that have been compiled and built. Can we use their cache in some way? This solves the problem of too long compilation time for the first time. There are more solutions than difficulties. If you are interested in sharing the build cache, you can check this article: Gradle uses remote build cache

Summarize

Relatively safe and simple optimization scheme:

  • Enable parallel compilation, on-demand build, build cache
  • Enable annotation incremental compilation (below Gradle plugin 4.0)
  • Turn on offline mode
  • Use apply changes
  • Modify JVM size

The optimization scheme that needs to be adapted:

  • Upgrade gradle and its plugins
  • Use the official muitidex

An extremely fast solution that is only suitable for debugging and developing:

  • Module
  • Customize execution tasks on demand

Further optimization:

  • Customize and write optimization plug-ins to improve cache hit rate, etc.

  • Private server maven mirror proxy

  • CI shared build cache

Total revenue for Mac systems:

rebuild Modification method modify xml
before optimization 4m46s 46s 22s
Optimized 38s 13s 11s
income 86% reduction 46% reduction 50% reduction

Windows system verification

Configuration:

  • Processor: i7-10510U 2.3GHz
  • Memory: 8GB
  • SSD

Total revenue:

rebuild Modification method modify xml
before optimization 10m30s 2m33s 12s
Optimized 1m9s 12s 9s
income 89% reduction 92% reduction 25% reduction

The data may not be completely accurate, but the compilation speed is visible to the naked eye. After optimization, the compilation time can be greatly reduced. It’s not good to drink coffee at this time. If the funds are sufficient, update the computer configuration again, and the speed will take off directly. Less overtime depends on this optimization!

Guess you like

Origin blog.csdn.net/Nbin_Newby/article/details/120439965