乐趣区

关于an-d-ro-id:Android高手笔记D8-R8编译优化

D8

  • D8 是一款用于取代 DX、更快的 Dex 编译器,能够生成更小的 APK;
开启 D8 的益处
  1. 编译更快、工夫更短
  2. 编译时占用内存更小
  3. .dex 文件更小
  4. .dex 文件领有更好的运行时性能
  5. 反对在代码中应用 Java 8 语言
开启与敞开
  • Android Studio 3.0 须要被动在 gradle.properties 文件中新增:android.enableD8=true
  • D8 作为 DX 的一个代替计划,Android Studio 3.1 版本开始,将 D8 作为默认的 Dex 编译器。
  • 想敞开 D8,能够在 gradle.properties 里增加如下配置:
android.enableD8=false // 敞开 D8 复原到 DX-
android.enableD8.desugaring=false // 复原到以前的行为,让脱糖产生在 Java 编译之后,.class 字节码仍遵循 Java 7 格局
执行增量构建
  • 为了在开发过程中进步构建速度(例如进步继续集成 build 的速度),能够批示 d8 仅编译我的项目的局部 Java 字节码;
  • 例如,如果启用了按类 dexing 解决,则只需从新编译自上次构建以来批改过的类(d8 无奈自动检测哪些字节码文件已被批改过,因而您须要手动指定类列表):
// 执行几个类的增量构建,并启用按类 dexing 解决, 并为增量构建指定输入目录
d8 MainActivity.class R.class --intermediate --file-per-class --output ~/build/intermediate/dex
  • 能够应用 –main-dex-list 指定想让 d8 编译到主 DEX 文件中的类
d8 ~/build/intermediate/dex --release --main-dex-list ~/build/classes.txt --output ~/build/release/dex
反对 Java8
  • 通过一个叫做“脱糖”的编译过程,将这些实用的语言性能转换为能够在 Android 平台上运行的字节码,D8 脱糖就不会在 transforms 目录下生成 desugar 目录。
  • Android Studio 和 Android Gradle 插件蕴含了 d8 启用脱糖所需的类门路资源。
  • 从命令行应用 d8 时,须要手动增加一些资源:
  1. –lib:标记指标 Android SDK 中的 android.jar 门路
  2. –classpath:标记我的项目的局部已编译的 Java 字节码,目前不打算将这部分字节码编译为 DEX 字节码,但在将其余类编译为 DEX 字节码时须要用到这些字节码。例如,如果代码应用默认和动态接口办法(一种 Java 8 语言性能),则须要应用此标记来指定您我的项目的所有 Java 字节码的门路,即便您不打算将所有 Java 字节码都编译为 DEX 字节码也是如此。这是因为 d8 须要依据这些信息来了解您我的项目的代码并解析对接口办法的调用
  • 示例对一个拜访默认接口办法的类执行增量构建:
d8 MainActivity.class --intermediate --file-per-class --output ~/build/intermediate/dex
--lib android_sdk/platforms/api-level/android.jar
--classpath ~/build/javac/debug
Java8 新个性:接口默认办法和静态方法
  • JDK1.8 以前,接口 (interface) 没有提供任何具体的实现;
  • JDK1.8 开始,接口容许定义默认办法和静态方法

R8

  • R8 之前采纳 D8+ProGuard 的模式构建,R8 则将 ProGuard 和 D8 工具进行整合,目标是减速构建工夫和缩小输入 apk 的大小;
开启 R8 的益处
  1. 代码缩减(摇树优化):应用动态代码剖析来查找和删除无法访问的代码和未实例化的类型,对躲避 64k 援用限度十分有用;
  2. 资源缩减:移除不应用的资源,包含利用库依赖项中不应用的资源。
  3. 混同代码:缩短类和成员的名称,从而减小 DEX 文件的大小
  4. 优化代码:查看并重写代码,选择性内联,移除未应用的参数和类合并来优化代码大小
  5. 缩小调试信息 : 规范化调试信息并压缩行号信息。
  • R8 会主动执行上述编译时工作,也能够停用某些工作或通过 ProGuard 规定文件自定义 R8 的行为。
  • 应用某个第三方库时,通常只应用其中很小一部分。若不压缩,所有库代码都会保留在利用中。简短的代码有时能够进步可读性和可维护性: 例如,应用有意义的变量名和建造者模式 来帮忙其他人更容易检查和了解代码;然而这些模式会加大代码量, 通常咱们本人编写的代码有很大的压缩空间。
开启与敞开
  • Android Studio 3.3 需在我的项目的 gradle.properties 里加上:android.enableR8=true
  • Android Studio 3.4 或 Android Gradle 插件 3.4.0 及更高版本时,R8 是默认编译器(不再应用 ProGuard 执行编译时代码优化),用于将我的项目的 Java 字节码转换为在 Android 平台上运行的 DEX 格局。
  • 不过创立新我的项目时,缩减、混同解决和代码优化性能默认处于停用状态。因为这些编译时优化性能会减少我的项目的构建工夫,而且如果没有充沛自定义要保留的代码,还可能会引入谬误。
  1. 开启代码缩减,须要在利用的主 build.gradle 文件中将 minifyEnable 属性设置为 true
  2. 开启资源缩减:须要在利用的主 build.gradle 文件中将 shrinkResources 属性设置为 true
  • 资源缩减只有在与代码缩减配合应用时能力发挥作用。在代码缩减器移除所有不应用的代码后,资源缩减器便可确定利用仍要应用的资源,当增加蕴含资源的代码库时尤其如此。必须移除不应用的库代码,使库资源变为未援用资源,因此可由资源缩减器移除。
  1. 创立新我的项目或模块时,IDE 会创立一个 /proguard-rules.pro 文件,以便您增加本人的规定。
android {
    ...
    buildTypes {
        release {
            shrinkResources true // 启用 R8 的资源缩减性能
            minifyEnabled true // 启用 R8 的代码缩减性能
             proguardFiles
                //1. Android Gradle 插件会生成 proguard-android-optimize.txt(其中蕴含了对大多数 Android 我的项目都有用的规定),并启用 @Keep* 注解。getDefaultProguardFile('proguard-android-optimize.txt'),
                //2. 应用 Android Studio 创立新模块时,Android Studio 会在该模块的根目录中创立 proguard-rules.pro 文件
                'proguard-rules.pro'

                //3. AAR 库:<library-dir>/proguard.txt, JAR 库:<library-dir>/META-INF/proguard/
                // 因为 ProGuard 规定是累加的,因而 AAR 库依赖项蕴含的某些规定无奈移除,并且可能会影响对利用其余局部的编译。// 例如,如果某个库蕴含停用代码优化性能的规定,该规定会针对整个我的项目停用优化性能。//4. Android 资源打包工具 2 (AAPT2):
                // 应用 minifyEnabled true 构建我的项目后,AAPT2 会依据对利用清单中的类、布局及其他利用资源的援用,生成保留规定。// 文件门路为:<module-dir>/build/intermediates/proguard-rules/debug/aapt_rules.txt

                //5. 自定义配置文件:详见上面的增加其余配置
        }
    }
}
增加其余配置
  • 能够通过在相应的 productFlavor 代码块中再增加一个 proguardFiles 属性来增加每个构建变体专用的规定
android {
    ...
    buildTypes {
        release {...}
    }
    flavorDimensions "version"
    productFlavors {
        flavor1 {...}
        flavor2 {proguardFile 'flavor2-rules.pro'}
    }
}
  • flavor2 应用全副三个 ProGuard 规定,因为还利用了来自 release 代码块的规定。
敞开 R8
  • 能够在 gradle.properties 里增加如下配置:
android.enableR8=false
开启 R8 齐全模式
  • R8 一般模式是兼容 ProGuard 的,若原我的项目里已应用了 ProGuard,间接启用 R8 即可。同时,R8 也有齐全模式,与 ProGuard 不间接兼容。

能够在 gradle.properties 文件中另外设置以下内容:

android.enableR8.fullMode=true
  • 额定的优化性能会使 R8 的行为与 ProGuard 不同,因而可能会须要您增加额定的 ProGuard 规定,以防止运行时问题。
自定义要保留的代码
  • 在某些状况下,R8 很难做出正确判断,因此可能会移除利用实际上须要的代码:
1. 当利用通过 Java 原生接口 (JNI) 调用办法时
2. 当您的利用在运行时查问代码时(如应用反射)- 反射 (Reflection) 会导致 R8 在跟踪代码时无奈辨认到代码的入口点
  • 如需修复谬误并强制 R8 保留某些代码,在 ProGuard 规定文件中增加 -keep 代码行, 如
-keep public class MyClass
  • 或者为要保留的代码增加 @Keep 注解
1. 在类上增加 @Keep 可按原样保留整个类
2. 在办法或字段上增加该正文,将使该办法 / 字段(及其名称)以及类名称放弃不变。3. 只有在应用 AndroidX 注解库且您增加 Android Gradle 插件随附的 ProGuard 规定文件时,此注解才可用。
  • 如需输入 R8 在构建我的项目时利用的所有规定的残缺报告,请将以下代码增加到模块的 proguard-rules.pro 文件中:
// You can specify any path and filename.
-printconfiguration ~/tmp/full-r8-config.txt
自定义要保留的资源
  • 如果您有想要保留或舍弃的特定资源,请在我的项目中创立一个蕴含 标记的 XML 文件,并在 tools:keep 属性中指定每个要保留的资源,在 tools:discard 属性中指定每个要舍弃的资源。这两个属性都承受以逗号分隔的资源名称列表。您能够将星号字符用作通配符。
  • 将该文件保留在我的项目资源中,例如,保留在 res/raw/keep.xml 中。构建零碎不会将此文件打包到利用中。
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"
    tools:discard="@layout/unused2" />
严格援用查看
  • 通常资源缩减器能够精确地判断是否应用了某个资源。不过如果代码中调用了 Resources.getIdentifier()(或者援用的任何库会执行此调用,例如 AppCompat 库便会执行此调用),这意味着代码将依据动静生成的字符串查问资源名称。资源缩减器在默认状况下(平安缩减模式)会采取爱护行为,将所有具备匹配名称格局的资源标记为可能已应用,无奈移除。资源缩减器还会查看代码中的所有字符串常量以及各种 res/raw/ 资源,以查找格局相似于 file:///android\_res/drawable//ic\_plus\_anim\_016.png 的资源网址。如果它找到与此类似的字符串,或找到其余看似可用来构建与此类似的网址的字符串,则不会将它们移除。
  • 例如,以下代码会将所有带 img\_ 前缀的资源标记为已应用:
val name = String.format("img_%1d", angle + 1)
val res = resources.getIdentifier(name, "drawable", packageName)
  • 启用严格援用查看: 将 keep.xml 文件中的 shrinkMode 设为 strict, 此时如果通过动静生成的字符串援用资源,必须应用 tools:keep 属性手动保留这些资源。
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:shrinkMode="strict" />
移除未应用的备用资源
  • Gradle 资源缩减器只会移除未由利用代码援用的资源,这意味着,它不会移除用于不同设施配置的备用资源;
  • 例如应用的是蕴含语言资源的库(如 AppCompat 或 Google Play 服务),那么利用中将蕴含这些库中音讯的所有已翻译语言的字符串,能够应用 resConfigs 属性移除利用不须要的备用资源文件,如设置只保留英语和法语的语言资源
android {
    defaultConfig {
        ...
        resConfigs "en", "fr"
    }
}
合并反复资源
  • 默认状况下,Gradle 还会合并同名的资源(多个文件具备完全相同的资源名称、类型和限定符时)。这一行为不受 shrinkResources 属性管制,也无奈停用,因为当多个资源与代码查问的名称匹配时,有必要利用这一行为防止谬误。
  • Gradle 会在反复项中抉择它认为最合适的文件(依据下述优先程序),并且只将这一个资源传递给 AAPT,以便在最终工件中散发
  • Gradle 会按以下级联优先程序合并反复资源:库我的项目依赖项 → 主资源 → 构建变种 → 构建类型, 如某个反复资源同时呈现在主资源和构建变种中,Gradle 会抉择构建变种中的资源。
  • 如果完全相同的资源呈现在同一源代码集中,Gradle 无奈合并它们,并且会收回资源合并谬误, 或者在 build.gradle 文件的 sourceSet 属性中定义了多个源代码集,src/main/res/ 和 src/main/res2/ 蕴含完全相同的资源也会报错。

因为文章篇幅无限,文档资料内容较多,须要《2022 最新 Android 面试真题 + 解析》、数据结构与算法面试题、Java 面试题、Android 四大组件、Android 面试题、UI 控件篇、网络通信篇、架构设计篇、性能优化篇、源码流程篇、Kotlin 方面、第三方框架、大厂面经,能够【点击这里收费获取】,心愿可能共同进步,独特学习,共勉!

本文转自 https://juejin.cn/post/7052636587288428557,如有侵权,请分割删除。

退出移动版