Android优化APP构建速度的17条建议

较长的构建时间将会减缓项目的开发进度,特别是对于大型的项目,app的构建时间长则十几分钟,短则几分钟,长的构建时间已经成了开发瓶颈,本篇文章根据Google官方文档,加上自己的一些理解提供一些提升app构建速度的优化建议。

1. 为开发环境创建一个变体

有许多配置是你在准备app的release 版本的时候需要,但是当你开发app的时候是不需要的,开启不必要的构建进程会使你的增量构建或者clean构建变得很慢,因此需要构建一个只保留开发时需要配置的变体,如下例子创建了一个dev和prod变体(prod 为release 版本的配置)。


  1. android { 
  2.   ... 
  3.   defaultConfig {...} 
  4.   buildTypes {...} 
  5.   productFlavors { 
  6.     // When building a variant that uses this flavor, the following configurations 
  7.     // override those in the defaultConfig block. 
  8.     dev { 
  9.       // To avoid using legacy multidex, set minSdkVersion to 21 or higher. 
  10.       minSdkVersion 21 
  11.       versionNameSuffix "-dev" 
  12.       applicationIdSuffix '.dev' 
  13.     } 
  14.  
  15.     prod { 
  16.       // If you've configured the defaultConfig block for the release version of 
  17.       // your app, you can leave this block empty and Gradle uses configurations in 
  18.       // the defaultConfig block instead. You still need to create this flavor. 
  19.       // Otherwise, all variants use the "dev" flavor configurations. 
  20.     } 
  21.   } 
  22. }  

2 . 避免编译不必要的资源

避免编译和包含你没有测试的资源(比如添加的一个本地的语言和屏幕密度资源),你可以只在你的’dev’ flavor下指定一种语言和一个屏幕密度,如下:


  1. android { 
  2.   ... 
  3.   productFlavors { 
  4.     dev { 
  5.       ... 
  6.       // The following configuration limits the "dev" flavor to using 
  7.       // English stringresources and xxhdpi screen-density resources. 
  8.       resConfigs "en", "xxhdpi" 
  9.     } 
  10.     ... 
  11.   } 
  12. }  

上面的配置将会限制dev 变体只使用 english string 资源和 xxhdpi 屏幕密度资源。

3 . 配置debug 构建的Crushlytics为不可用状态

在debug 构建状态下,如果你不需要运行崩溃上报,你可以将这个插件设置为不可用状态来提升你的构建速度,如下:


  1. android { 
  2.   ... 
  3.   buildTypes { 
  4.     debug { 
  5.       ext.enableCrashlytics = false 
  6.     } 
  7. }  

上面只是举个例子,Crushlytics 为崩溃上报分析工具,在开发的时候我们可能不需要,因此不需要打开,在我们实际开发中,像崩溃上报SDK,数据统计SDK等(如 友盟统计、GrowingIO、百度统计)在开发阶段都设置为不可用,来提升构建速度。

4 . 用静态的构建配置值来构建你的Debug版

一般地,在你的debug 构建时,为manifest文件或者资源文件配置使用静态/硬编码的值。如果你的manifest或者资源文件的值每次构建都需要动态更新,那么Instant Run 无法执行代码交换-它必须重新构建和安装新的APK。

例如,使用动态的version codes ,version names ,resources或者其他更改manifest文件的构建逻辑,每次你想执行一个修改都会构建全部APK,即使实际的修改可能仅仅只需要热交换。如果这些构建配置是需要动态配置的,那么将它们从你的release 构建变体中分离出来,并且在你的debug 构建中保留它们的静态值。像下面build.gradle 文件显示的这样:


  1. int MILLIS_IN_MINUTE = 1000 * 60 
  2. int minutesSinceEpoch = System.currentTimeMillis() / MILLIS_IN_MINUTE 
  3.  
  4. android { 
  5.     ... 
  6.     defaultConfig { 
  7.         // Making either of these two values dynamic in the defaultConfig will 
  8.         // require a full APK build and reinstallation because the AndroidManifest.xml 
  9.         // must be updated (which is not supported by Instant Run). 
  10.         versionCode 1 
  11.         versionName "1.0" 
  12.         ... 
  13.     } 
  14.  
  15.     // The defaultConfig values above are fixed, so your incremental builds don't 
  16.     // need to rebuild the manifest (and therefore the whole APK, slowing build times). 
  17.     // But for release builds, it's okay. So the following script iterates through 
  18.     // all the known variants, finds those that are "release" build types, and 
  19.     // changes those properties to something dynamic. 
  20.     applicationVariants.all { variant -> 
  21.         if (variant.buildType.name == "release") { 
  22.             variant.mergedFlavor.versionCode = minutesSinceEpoch; 
  23.             variant.mergedFlavor.versionName = minutesSinceEpoch + "-" + variant.flavorName; 
  24.         } 
  25.     } 
  26. }  

5 . 用静态的版本依赖

当你在build.gradle文件中声明依赖的时候,你应该避免在版本号结束的地方使用+号,比如:com.android.tools.build:gradle:2.+ 因为Gradle的检查更新,用动态的版本号会导致未知的版本更新、使解决版本的差异变得困难和更慢的构建。你应该使用静态或者硬编码版本号来代替。如:com.android.tools.build:gradle:2.2.2 。

6 . 使 on demand 配置为enable 状态

为了让Gradle能够确切的知道该如何构建你的APP,在每次构建之前,构建系统配置工程的所有modules和其他依赖(即使你只想构建或者测试一个modules),这使得大型的多module 工程的构建速度变得很慢。告诉Gradle仅仅配置你想要构建的Modules,用如下步骤使 on demand 配置可用

(1) 在菜单栏上选择 File -> Settings(如果是Mac上 ,选择 Android Studio ->Preferences)

(2) 导航到 Build,Execution,Deployment -> Compiler

(3) check Configure on demand 复选框

(4) 点击 OK

如图: 

on_demand.png

7 . 创建 library 模块

检查你app中的代码,将可模块化的代码抽取一个Android Library module,通过这种方式模块化你的代码将允许构建系统仅仅只编译那些有改动的模块,并将其构建结果缓存下来以被后面的构建使用。同样的配置了 on demand 和 parallel project execution (project 并行执行) 将更加高效(当你打开这些特性时)。

8 . 为自定义构建逻辑创建Tasks

在你创建了 build profile (build profile 后文会讲)之后,如果显示构建时间相对长的一部分时间花在“configure project(配置工程)阶段,那么请 review 你的build.gradle 脚本,并且查找可包含到自定义Gradle Task中的代码,通过将一些构建逻辑移动到一个task 中,当需要的时候才运行,结果能被缓存用于后续的构建,并且这个构建逻辑可以并行执行(如果你开启了 并行执行project),更多详细信息请阅读Gradle官方文档。 official Gradle documentation

小提示:

如果你的构建包含了大量的自定义任务tasks,你可能想清理你的build.gradle文件,通过自定义task classes (也就是自定义Gradle 插件啦),将你的classes 添加到 project-root/buildSrc/src/main/groovy/目录下,Gradle将自动包含它们到class path ,为项目的所有build.gradle文件。

9 . 配置 dexOptions 和 开启 library pre-dexing(dex预处理)

先补充一个知识点:Dex-in-process:新发布的Android Studio 2.1增加了一个新的特性:Dex In Process,可以极大的加快重新编译的速度,同样也能提高Instant Run的性能。(第10条优化建议会说到)

详情请看Faster Android Studio Builds with Dex In Process

Android 插件提供了 dexOptions script block ,因此你可以配置相应的 DEX 构建特性,它们可以提高构建速度:

(1)preDexLibraaies : 声明是否对依赖的库进行dex 预处理来使你的增量构建更快速,因为这个特性可能会使你的clean 构建变慢,因此在你的持续集成服务器上你可能想关闭这个特性。

(2) maxProcessCount : 设置最大的线程数量使用当运行 dex-in-process时,默认值是4。

(3)javaMaxHeapSize: 为DEX 编译器 设置最大的堆大小,相对于设置这个属性,你应该增加 Gradle的 堆大小(这个堆大小dex-in-process可用的时候对DEX 编译器有效)

例子:


  1. android { 
  2.   ... 
  3.   dexOptions { 
  4.     preDexLibraries true 
  5.     maxProcessCount 8 
  6.     // Instead of setting the heap size for the DEX process, increase Gradle's 
  7.     // heap size to enable dex-in-process. To learm more, read the next section. 
  8.     // javaMaxHeapSize "2048m" 
  9.   } 
  10. }  

你应该增加它们的值来测试一下这些设置,然后通过profile观察效果,当你为这个进程分配太多资源的时候,可能会得到一个负面的影响。

10 . 增加Gradle的堆大小 和开启 dex-in-process

Dex-in-process 允许多个DEX 进程运行在一个单独的VM 中,这使得增量构建和清理构建变得更快。默认情况下,通过Android Studio2.1 或者更高版本创建的新项目分配了足够的内存来开启这个特性,如果你没有使用Android Studio 2.1 或者更高的版本创建项目,你需要给Gradle后台驻扎程序设置至少1536MB 的堆大小内存。默认如下图:  

gradle_heap.png

下面的例子在gradle.properties中将Gradle 堆内存大小设置为 2048MB:


  1. org.gradle.jvmargs = -Xmx2048m //设置Gradle 堆大小 2G 

在一些大型的项目上,为Gradle堆分配更多的内存当然更有利,然而,如果你用的是一个小内存的机器,你可能需要给IDE配置更少的内存,想知道如何改变分配给IDE资源的数量和Gradle 对构建表现的影响,请看profiling your build这一条。

如果在你的Module build.gradle 文件中为android.dexOptions.javaMaxHeapSize定义了一个值,那么你需要给Gradle的堆大小设置 的值为比javaMaxHeapSize多512MB,并且满足至少为1536MB。举个例子:在build.gradle中设置javaMaxHeapSize `为1280MB,那么你就要给Gradle堆大小设置 至少1792MB(1280 + 512),当然了,设置大一点更佳。

build.gradle:


  1. dexOptions { 
  2.        javaMaxHeapSize "1280m" 
  3.    }  

gradle.properties:


  1. org.gradle.jvmargs = -Xmx1792m 

11 . 将图片转为 WebP格式

WebP是一种图片文件格式,它提供了像JPEG一样的有损压缩和像PNG一样的透明支持,但是同时它的压缩质量比JPEG或者PNG任何一个都更好,减小Image文件的大小,而不用在构建时做压缩,因此它能提高构建速度,尤其是你的APP使用了大量的图片资源。但是有一点,在解压WebP格式的图片的时候,你的设备的CPU使用将小幅度增加。 用Android Studio 可以很方便的转WebP格式,详情请看convert your images to WebP.

小提示:此外,将工程里面的图片转为webP格式也是优化APK体积的一个方向,webp是Android 原生4.0就开始支持的,它能提供和JPEG和PNG相同质量的图片但是size 更小。没有任何适配问题。

12 . 禁止使用 PNG crunching

如果你不能(或者不想)转换你的PNG格式图片为WebP,你仍然可以通过禁止每次构建app都自动压缩图片来提升构建速度,要禁止这项优化,在build.gradle 的添加如下代码:


  1. android { 
  2.   ... 
  3.   aaptOptions { 
  4.     cruncherEnabled false 
  5.   } 
  6. }  

13 . 使用 Instant Run

Instant Run显著的减少了更新app的时间,它通过推送确定的代码、资源变更而不用构建一个新的app ,并且在一些情况下,甚至不用重启当前的activity,在代码变更后,使用Instant Run 通过点击Apply Changes(黄色图标)。当你做了如下几步,它会默认打开:

  • 用debug 构建变体来构建你的app
  • Gradle插件的版本 2.3.0或者更高
  • 在module层级的build.gradle中设置minSdkVersion为15或者更高
  • 发布你的app 在Android 5.0(API level 21) 或者更高 点击 Run

14 . 使用构建缓存

在构建你的工程的时候,构建缓存存储了Android Gradle插件生成的确定的产物(如 AAR包和远程依赖的 pre-dexed)。当你使用缓存的时候,你的清理构建更快是因为构建系统后续构建能够简单地重用它们的缓存而不用重新创建。

新的工程使用Android Gradle 插件2.3.0或者更高版本默认就开启了构建缓存(除非你手动关闭了),了解更多请阅读Accelerate clean builds with build cache.

15 . 禁止使用注解处理器

Gradle 2.1后可以增量构建Java,当使用注解处理器时增量构建将不可用,如果可以,避免使用注解处理器,让你从只构建更改的类来获益。(提升编译时间)

16 . 分析你的构建(Profile your build)

在大型的项目中(或者实现了大量自定义构建逻辑),可能需要更加深入的了解构建进程来寻找瓶颈,你可以通过分析构建生命周期的各个阶段 每个gradle task 执行了多长时间。例如:如果你的构建资料显示Gradle 花了大量的时间来配置你的工程,这建议你需要将自定义构建逻辑放在配置阶段之外。另外,如果mergeDevDebugResources 任务 消费了大量的的构建时间,这表明你需要将图片转换为WebP格式或者禁止PNG Crunching(第11,12 条优化建议)

通过构建分析来提升你的构建速度通常需要在分析打开的情况下运行你的构建,多次修改构建配置,分析和观察结果的变化。

生成和查看 build profile ,执行下面步骤:

1),用Android Studio打开项目,选择 View -> Tool Windows -> Terminal 打开命令行

2),执行clean build 输入下面的命令,当你分析你的构建时,每次构建之间需要执行一个 clean build 操作,因为Gradle会跳过输入没有 改变的tasks,因此,第二个没有改变输入的构建通常会运行得更快因为tasks 没有重新运行,因此在构建之间运行一个cleantask 保证你分析了全部的构建进程。


  1. //如果在Mac 或者 Linux上 用 ./gradlew 
  2. gradlew clean  

3),选择其中一个产品风味(product flavor) 执行debug 构建,比如:dev flavor,如下:


  1. gradlew --profile --recompile-scripts --offline --rerun-tasks assembleFlavorDebug 

命令分析:

--profile: 开启profiling

--recompile-scripts: 强制脚本重新编译跳过cache

--offline:禁止 Gradle获取离线依赖,这是确保任何的延迟都是Gradle试图更新依赖而导致,不会误导你的分析数据。你应该先准备好构建一次工程确保Gradle 已经下载好并且缓存依赖。

--rerun-tasks:强制Gradle返回所有task 并且忽略任何task 优化。

4),构建结束后,project-root/build/reports/profile/ 目录下:

profile_build.png

5),右键点击profile_timestamp.html,选择在浏览器中打开,你就会看到下面这张图,你可以观察报告中的每一个tab来了解你的构建,比如Tasks Execution 显示了每一个task执行的时间。

profile_in_brower.png

task Execution 显示每个task的执行时长,如下图:

task_execution.png

6),可选项:在Project 或者构建配置做出任何修改之前,重复几次步骤3,但是去掉--rerun-tasks标志,由于Gradle 试图节省时间而不会重新执行那些输入没有任何修改的task(它们被标志为UP-TO-DATE 在Task Execution tab下,如下图:),你可以识别哪些任务没有被执行,例如,如果:app:processDevUniversalDebugManifest没有被标记成UP-TO-DATE,那么它表示你的构建配置是每一次构建都动态更新Manifest文件的。然而,有一些task还是需要每次都执行的,例如::app:checkDevDebugManifest

 

profile_up_to_date.png

现在,你已经有了一个构建分析报告,你可以开始通过观察构建报告的每一个tab来寻找优化时机,一些构建配置是需要试验的,因为在不同的项目或者工作空间中它们的获益不一样。比如,基于大量代码的大型工程,它们可能获益于使用混淆、清除无用代码和缩减APK体积。然而,小型工程可能获益于关闭混淆(混淆还是挺耗时的)。此外,在第内存的机器上增减Gradle 的堆大小,也有可能起到反面作用。

17 . 项目组件化

对于大型的项目,可能上面这些优化建议有一定的效果,但是构建速度还是有些慢,那么就可以考虑组建化了,将项目拆分成一个个单独的组件,开发环境每个module 都是一个APK,发布的时候,每个module都是一个lib 给主工程使用。篇幅有效,这里就不再详细介绍组件化,现在组件化是一个趋势,如果有精力或者有实力,组件化是一个很不错的选择。

最后

以上就是一些解决app构建速度慢的优化建议,如果你觉得你的工程构建速度慢,你可以试一下这些优化项。如有问题,欢迎评论区留言。如果你还有什么更好优化建议,也可以在下面留言,我会追加文章后面。

本文作者:佚名

来源:51CTO

时间: 2024-09-08 05:45:05

Android优化APP构建速度的17条建议的相关文章

Docker安全部署的17条建议

本文讲的是Docker安全部署的17条建议,[编者的话]本文作者从Docker镜像.网络命名空间.日志和审核.守护进程特权.SELinux.二进制SUID/GUID.设备控制组.服务和应用. Linux内核.用户命名空间.libseccomp等方面给出了自己的建议,可以一读. 当前持续增长的云计算市场对虚拟化技术有着强烈的需求.遗憾的是,大多数的虚拟化解决方案不够灵活,无法满足研发需求,且使用全虚拟化解决方案的潜在开销变成了制约基础设施扩展性的负担. Docker让开发和运维人员能无缝地部署容器

android优化-android APP 内存与速度的优化问题。。。

问题描述 android APP 内存与速度的优化问题... android APP 内存与速度的优化问题... 我最近写了一个APP 一开就用了很多资源...请问怎么来优化内存,速度... 大家可以谈谈从代码,或者风格,框架方面谈谈... 解决方案 性能优化 Android应用程序运行的移动设备受限于其运算能力,存储空间,及电池续航.由此,它必须是高效的.电池续航可能是一个促使你优化程序的原因,即使他看起来已经运行的足够快了.由于续航对用户的重要性,当电量耗损陡增时,意味这用户迟早会发现是由于

Android WebView加载含有大量网络图片的网页,如何优化加载速度,以及效果

问题描述 Android WebView加载含有大量网络图片的网页,如何优化加载速度,以及效果 目前碰到一个问题,有一个含有大量网络图片的网页,我用WebView展示的时候,加载速度很慢,而且可能会出现图片加载不出来的可能性,除此之外,图片加载的过程也会在客户端呈现导致用户体验不好.看别人的APP在加载含有大量网络图片的网页的时候,速度比较快,而且别人的做法是等所有的图片全部加载完之后再显示,所以体验比较好,关键是人家的全部加载完所需要的时间不长啊,是不是有什么方法可以让WebView对于图片的

jQuery性能优化28条建议你值得借鉴_jquery

jQuery性能优化28条建议 一直在寻找有关jQuery性能优化方面的小窍门,能让我那臃肿的动态网页应用变得轻便些.找了很多文章后,我决定将最好最常用的一些优化性能的建议列出来.我也做了一个jQuery性能优化的简明样式表,你可以打印出来或者设为桌面背景. 一.选择器性能优化建议 1. 总是从#id选择器来继承 这是jQuery选择器的一条黄金法则.jQuery选择一个元素最快的方法就是用ID来选择了. 复制代码 代码如下: $('#content').hide(); 或者从ID选择器继承来选

Git的17条基本用法

在开发过程中,经常会遇到一个项目由多人合力完成这种情况,每个人负责其中一个模块.项目开发过程中为了确保代码的可追溯,我们引入了版本控制概念,每个人修改了什么代码或提交了什么代码都能够跟踪记录.现在流行的版本控制主要有:集中式版本控制(SVN)和分布式版本控制(GIT).本文将介绍Git的17条基本用法. 1.初始化Git仓库 Git仓库分为两种类型:一种是存放在服务器上面的裸仓库,里面没有保存文件,只是存放.git的内容:一种是标准仓库,会在项目根目录下创建一个.git目录. $ git ini

Android实现为Notification加上一个进度条的方法_Android

本文实例讲述了Android实现为Notification加上一个进度条的方法.分享给大家供大家参考,具体如下: package com.notification; import android.app.Activity; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Intent;

Android仿水波纹流量球进度条控制器_Android

仿水波纹流球进度条控制器,Android实现高端大气的主流特效,供大家参考,具体内容如下 效果图: CircleView 这里主要是实现中心圆以及水波特效 package com.lgl.circleview; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.gra

史上最详细的Android原生APP中添加ReactNative进行混合开发教程

背景 React Native出来已经一段时间了,相对来说也算稳定了,在很多的企业中都实际使用他们,混合开发已经是未来的一种趋势,混合开发中使用的技术很多,不外乎Html5.JS框架通过一定的技术和原始交互,目前主流混合开发React Native.Cordova.APICloud.MUI.AppCan.Sencha Touch.jQuery Mobile等等(其他的小伙伴们自己收集),目前网上写教程的人很多,但是React Native更新速度很快,根据他们的教程,中间会遇到各种问题,今天我和

服务器-iOS 数据 设置模式 优化 数据返回速度

问题描述 iOS 数据 设置模式 优化 数据返回速度 现在APP的问题 ,后台.很小压力很大,逻辑判断复杂,返回数据速度太慢. 现在的流程:我要所搜附近的小时工,手机定位把自己的坐标发给付服务器,服务器跳去所有的员工,首先判断服务类型是否为小时工 然后判断是否空闲 剩下若干个空闲的小时工,然后根据系统工具和小时工的坐标计算剩下每个小时工到客户的距离,根据距离排序,取里的最近的几个人返回.经过这一系列操作所耗费的时间太长,现在想优化,减少时间. 解决方案1. 把客户端和员工端增加固定地址的设置,客