安卓性能优化之构建优化

安卓性能优化之构建优化建议整理

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

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

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.
    }
  }
}

作者:依然范特稀西
链接:https://juejin.im/post/58caaf3a61ff4b00601ad991
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

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

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

作者:依然范特稀西
链接:https://juejin.im/post/58caaf3a61ff4b00601ad991
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

3. 禁用不必要的插件

android {
  ...
  buildTypes {
    debug {
      ext.enableCrashlytics = false
    }
}

4.使用静态的构建配置值来构建 debug 版本

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;
        }
    }
}

作者:依然范特稀西
链接:https://juejin.im/post/58caaf3a61ff4b00601ad991
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

5.用静态的版本依赖

谨慎使用+号的形式做声明依赖,每次构建会引入版本检查

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

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 编译器有效)

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"
  }
}

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

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

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

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),当然了,设置大一点更佳。

dexOptions {
        javaMaxHeapSize "1280m"
}
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 的添加如下代码:

android {
  ...
  aaptOptions {
    cruncherEnabled false
  }
}

13.使用 Instant Run

Emmm,不建议。。

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 保证你分析了全部的构建进程。

gradlew clean

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

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_timestamp.html,选择在浏览器中打开,你就会看到下面这张图,你可以观察报告中的每一个 tab 来了解你的构建,比如 Tasks Execution 显示了每一个 task 执行的时间。

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

17.组件化

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

发布了31 篇原创文章 · 获赞 13 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/weixin_42881744/article/details/104051760