Long build time will slow down the development progress of the project, especially for large projects. The build time of the app can be as long as ten minutes or as short as a few minutes. The long build time has become a development bottleneck. This article provides some optimization suggestions for improving the app build speed based on Google official documents and my own understanding. 1. Create a variant for the development environment There are many configurations that you need when preparing the release version of your app, but they are not needed when you are developing your app. Starting unnecessary build processes will make your incremental builds or clean builds very slow, so you need to build a variant that only retains the configuration needed for development. The following example creates a dev and prod variant (prod is the configuration of the release version).
2. Avoid compiling unnecessary resources To avoid compiling and including resources that you haven't tested (such as adding a native language and screen density resource), you can just specify a language and a screen density in your 'dev' flavor, like this:
The above configuration will restrict the dev variant to only use english string resources and xxhdpi screen density resources. 3. Configure debug build of Crushlytics to be unavailable In debug build state, if you do not need to run crash reporting, you can set this plugin to be unavailable to improve your build speed, as follows:
The above is just an example. Crushlytics is a crash reporting analysis tool. We may not need it during development, so there is no need to open it. In our actual development, crash reporting SDKs, data statistics SDKs (such as Umeng Statistics, GrowingIO, Baidu Statistics) are set to unavailable during the development phase to improve the build speed. 4. Build your Debug version using static build configuration values Typically, in your debug builds, use static/hard-coded values for your manifest or resource configuration files. If your manifest or resource values need to be dynamically updated for each build, then Instant Run cannot perform a code swap - 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 will have to build the entire APK every time you want to perform a change, even if the actual change may only require a hot swap. If these build configurations need to be dynamically configured, separate them from your release build variants and keep their static values in your debug builds. As shown in the following build.gradle file:
5. Use static version dependencies When you declare dependencies in your build.gradle file, you should avoid using a + sign at the end of the version number, such as com.android.tools.build:gradle:2.+ Because Gradle checks for updates, using dynamic version numbers can lead to unknown version updates, making it difficult to resolve version differences and slower builds. You should use static or hard-coded version numbers instead. For example: com.android.tools.build:gradle:2.2.2 . 6. Set the on demand configuration to enable state In order for Gradle to know exactly how to build your app, the build system configures all modules and other dependencies of the project before each build (even if you only want to build or test a module). This makes the build speed of large multi-module projects very slow. To tell Gradle to configure only the modules you want to build, use the following steps to enable the on demand configuration: (1) Select File -> Settings on the menu bar (if you are on a Mac, select Android Studio -> Preferences) (2) Navigate to Build, Execution, Deployment -> Compiler (3) Check the Configure on demand checkbox (4) Click OK As shown in the figure: on_demand.png 7. Create library module Check the code in your app and extract the modularized code into 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 the build results for subsequent builds. Similarly, configuring on demand and parallel project execution will be more efficient (when you turn on these features). 8. Create Tasks for custom build logic After you create a build profile (build profiles are discussed later), if it appears that a relatively large portion of the build time is spent in the "configure project" phase, then review your build.gradle script and look for code that can be included in a custom Gradle Task. By moving some of the build logic into a task, it is run only when needed, the results can be cached for subsequent builds, and this build logic can be executed in parallel (if you have enabled parallel execution of the project). For more details, please read the official Gradle documentation. Tips: If your build contains a lot of custom tasks, you may want to clean up your build.gradle file and define custom task classes (that is, custom Gradle plugins). Add your classes to the project-root/buildSrc/src/main/groovy/ directory and Gradle will automatically include them in the class path for all build.gradle files in the project. 9. Configure dexOptions and enable library pre-dexing (dex preprocessing) Let me add a little knowledge point: Dex-in-process: The newly released Android Studio 2.1 adds a new feature: Dex In Process, which can greatly speed up the recompilation speed and also improve the performance of Instant Run. (The 10th optimization suggestion will mention it) For more details, see Faster Android Studio Builds with Dex In Process The Android plugin provides a dexOptions script block so you can configure DEX build features that can improve build speed: (1)preDexLibraaies: declares whether to pre-dex dependent libraries to make your incremental builds faster. Because this feature may slow down your clean builds, you may want to turn this feature off on your continuous integration server. (2) maxProcessCount : Sets the maximum number of threads to use when running dex-in-process. The default value is 4. (3)javaMaxHeapSize: Sets 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:
You should test these settings by increasing their values and then profile them to see the effect; you may get a negative impact when you allocate too many resources to the process. 10. Increase Gradle's heap size and enable dex-in-process Dex-in-process allows multiple DEX processes to run in a single VM, which makes incremental builds and clean builds faster. By default, new projects created with Android Studio 2.1 or higher allocate enough memory to enable this feature. If you do not use Android Studio 2.1 or higher to create a project, you need to set the heap size memory for the Gradle daemon to at least 1536MB. The default is as follows: gradle_heap.png The following example sets the Gradle heap memory size to 2048MB in gradle.properties:
On larger projects, it may be beneficial to allocate more memory to the Gradle heap. However, if you are on a machine with less memory, you may need to configure the IDE to use less memory. For information on how changing the amount of memory allocated to the IDE and Gradle can affect build performance, see profiling your build. If you define a value for android.dexOptions.javaMaxHeapSize in your Module build.gradle file, you need to set the Gradle heap size to 512MB more than javaMaxHeapSize and 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). Of course, it is better to set it larger. build.gradle:
gradle.properties:
11. Convert images to WebP format WebP is an image file format that provides lossy compression like JPEG and transparent support like PNG, but at the same time its compression quality is better than either JPEG or PNG, reducing the size of the image file without compressing it at build time, so it can improve build speed, especially if your app uses a lot of image resources. But one thing is that when decompressing WebP format images, your device's CPU usage will increase slightly. You can easily convert to WebP format using Android Studio, for details, see convert your images to WebP. Tips: In addition, converting the images in the project to webP format is also a way to optimize the size of the APK. WebP is supported by Android native 4.0. It can provide images of the same quality as JPEG and PNG but with a smaller size. There is no adaptation problem. 12. PNG crunching is prohibited If you can’t (or don’t want to) convert your PNG images to WebP, you can still improve build speed by disabling automatic image compression every time you build your app. To disable this optimization, add the following code to your build.gradle:
13. Use Instant Run Instant Run significantly reduces the time it takes to update your 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) after making code changes. It is turned on by default when you do the following:
14. Using the build cache The build cache stores certain artifacts generated by the Android Gradle plugin when building your project (such as AAR packages and pre-dexed versions of remote dependencies). When you use the cache, your clean builds are faster because the build system can simply reuse their caches for subsequent builds instead of recreating them. New projects using the Android Gradle plugin 2.3.0 or higher have the build cache enabled by default (unless you disable it). For more information, read Accelerate clean builds with build cache. 15. Do not use annotation processors Gradle 2.1 and later can build Java incrementally. Incremental builds are not available when using annotation processors. If possible, avoid using annotation processors so that you benefit from building only changed classes. (Improve compilation time) 16. Profile your build In large projects (or projects that implement a lot of custom build logic), you may need to have a deeper understanding of the build process to find bottlenecks. You can analyze how long each gradle task takes to execute at each stage of the build lifecycle. For example, if your build data shows that Gradle spends a lot of time configuring your project, this suggests that you need to put your custom build logic outside the configuration phase. In addition, if the mergeDevDebugResources task consumes a lot of build time, this indicates that you need to convert images to WebP format or disable PNG Crunching (Optimization Tips 11 and 12) Improving your build speed with build profiling usually involves running your build with profiling turned on, changing the build configuration several times, profiling, and watching the results change. To generate and view a build profile, perform the following steps: 1) Open the project in Android Studio and select View -> Tool Windows -> Terminal to open the command line. 2) Perform a clean build Enter the following command. When you analyze your build, you need to perform a clean build between each build because Gradle will skip tasks whose inputs have not changed. Therefore, the second build with unchanged inputs will usually run faster because tasks are not re-run. Therefore, running a cleanask between builds ensures that you analyze the entire build process.
3) Select one of the product flavors to perform a debug build, for example, dev flavor, as follows:
Command analysis: --profile: Enable profiling --recompile-scripts: Force script recompilation to skip cache --offline: Disables Gradle from fetching offline dependencies. This ensures that any delays are caused by Gradle trying to update dependencies and do not mislead your analysis data. You should prepare a build to ensure that Gradle has downloaded and cached dependencies. --rerun-tasks: Forces Gradle to rerun all tasks and ignore any task optimizations. 4) After the build is completed, in the project-root/build/reports/profile/ directory:
profile_build.png 5) Right-click profile_timestamp.html and choose Open in browser. You will see the following picture. 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 displays the execution time of each task, as shown below: 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 and does not re-execute tasks whose inputs have not been modified (they are marked as UP-TO-DATE under the Task Execution tab, as shown below:), you can identify which tasks have not been executed. For example, if :app:processDevUniversalDebugManifest is not marked as UP-TO-DATE, it means that your build configuration is to dynamically update the manifest file for each build. However, some tasks still need to be executed every time, such as: :app:checkDevDebugManifest profile_up_to_date.png Now that you have a build analysis report, you can start looking for optimization opportunities by observing each tab of the build report. Some build configurations require experimentation because they benefit differently in different projects or workspaces. For example, large projects with a lot of code may benefit from using obfuscation, removing unused code, and reducing the size of the APK. However, small projects may benefit from turning off obfuscation (which is still time-consuming). In addition, increasing or decreasing the Gradle heap size on a machine with little memory may also have the opposite effect. 17. Project componentization For large projects, the above optimization suggestions may have some effect, but the build speed is still a bit slow, then you can consider componentization, split the project into separate components, each module in the development environment is an APK, and when released, each module is a lib for the main project to use. Due to space constraints, I will not introduce componentization in detail here. Componentization is a trend now. If you have the energy or strength, componentization is a very good choice. at last The above are some optimization suggestions for solving the slow app build speed. If you think your project build speed is slow, you can try these optimization items. If you have any questions, please leave a message in the comment area. If you have any better optimization suggestions, you can also leave a message below and I will add it to the end of the article. refer to
|
<<: Android compatibility | NDK toolset update notes
>>: Deeply debug network requests using WireShark
April 14 is World Quantum Day. Why was this date ...
When it comes to eating meat, some female friends...
This article was reviewed by Pa Li Ze, chief phys...
"Nezha 2" has become popular, and the s...
1. Project Background A domestic financial APP pr...
The promotion process of traditional form delivery...
Review expert: Gan Qiang, lecturer at Beijing Ins...
Hello, everyone. This is the villager's life....
The past few years may be the time when immunolog...
Copywriting is not about creating words and sente...
Promotion goals Compared with the complete Intern...
Audit expert: Zheng Yuanpan, professor at Zhengzh...
Recently, foreign media revealed that Great Wall ...
The author of this article will interpret group f...