针对库作者的优化

作为库作者,您应确保应用开发者可以轻松地将您的库集成到其应用中,同时保持高质量的最终用户体验。您应确保您的库与 Android 优化兼容,无需额外设置——或者记录该库可能不适合在 Android 上使用。

本文档面向已发布的库的开发者,但对于大型模块化应用中的内部库模块开发者也可能有用。

如果您是应用开发者并希望了解如何优化您的 Android 应用,请参阅启用应用优化。要了解哪些库适合使用,请参阅明智地选择库

优先使用代码生成而非反射

在可能的情况下,优先使用代码生成 (codegen) 而非反射。代码生成和反射都是编程中避免样板代码的常用方法,但代码生成与 R8 等应用优化器更兼容。

  • 使用代码生成,代码在构建过程中被分析和修改。由于编译后没有重大修改,优化器知道最终需要哪些代码以及哪些可以安全移除。
  • 使用反射,代码在运行时被分析和操作。由于代码在执行前并未真正定稿,优化器不知道哪些代码可以安全移除。它可能会移除在运行时通过反射动态使用的代码,这会导致用户应用崩溃。

许多现代库都使用代码生成而不是反射。有关常见入口点,请参阅 KSP,它被 RoomDagger2 和许多其他库使用。

何时可以使用反射

如果您必须使用反射,则应仅反射以下类型:

  • 特定目标类型(特定的接口实现者或子类)
  • 使用特定运行时注解的代码

以这种方式使用反射可以限制运行时开销,并能够编写有针对性的消费者保留规则

这种特定且有针对性的反射形式是您在 Android 框架(例如,膨胀 Activity、视图和 Drawable 时)和 AndroidX 库(例如,构建 WorkManager ListenableWorker 或 RoomDatabase 时)中都能看到的一种模式。相比之下,Gson 的开放式反射不适合在 Android 应用中使用

编写消费者保留规则

库应打包“消费者”保留规则,这些规则与应用保留规则使用相同的格式。这些规则捆绑到库工件(AAR 或 JAR)中,并在使用库时在 Android 应用优化期间自动使用。

AAR 库

要为 AAR 库添加消费者规则,请在 Android 库模块的构建脚本中使用 consumerProguardFiles 选项。有关更多信息,请参阅我们关于创建库模块的指南

Kotlin

android {
    defaultConfig {
        consumerProguardFiles("consumer-proguard-rules.pro")
    }
    ...
}

Groovy

android {
    defaultConfig {
        consumerProguardFiles 'consumer-proguard-rules.pro'
    }
    ...
}

JAR 库

要将规则与作为 JAR 发布的 Kotlin/Java 库捆绑在一起,请将您的规则文件放入最终 JAR 的 META-INF/proguard/ 目录中,可以使用任何文件名。例如,如果您的代码在 <libraryroot>/src/main/kotlin 中,请将消费者规则文件放在 <libraryroot>/src/main/resources/META-INF/proguard/consumer-proguard-rules.pro,规则将捆绑到输出 JAR 中的正确位置。

通过检查规则是否在 META-INF/proguard 目录中来验证最终 JAR 是否正确捆绑了规则。

优化 AAR 库构建(高级)

一般来说,您不应直接优化库构建,因为在库构建时可进行的优化非常有限。只有在应用构建期间,当库作为应用的一部分包含时,R8 才能知道库的所有方法是如何使用的,以及传递了哪些参数。作为库开发者,在优化库之前,您需要考虑库和应用构建时的多个优化阶段和保留行为。

如果您仍想在构建时优化您的库,Android Gradle 插件支持此功能。

Kotlin

android {
    buildTypes {
        release {
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
        configureEach {
            consumerProguardFiles("consumer-rules.pro")
        }
    }
}

Groovy

android {
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles
                getDefaultProguardFile('proguard-android-optimize.txt'),
                'proguard-rules.pro'
        }
        configureEach {
            consumerProguardFiles "consumer-rules.pro"
        }
    }
}

请注意,proguardFiles 的行为与 consumerProguardFiles 截然不同。

  • proguardFiles 在构建时使用,通常与 getDefaultProguardFile("proguard-android-optimize.txt") 一起使用,以定义在库构建期间应保留库的哪些部分。至少,这是您的公共 API。
  • 相比之下,consumerProguardFiles 被打包到库中,以影响稍后在消耗您库的应用构建期间发生的优化。

例如,如果您的库使用反射来构造内部类,您可能需要在 proguardFilesconsumerProguardFiles 中都定义保留规则。

如果您在库的构建中使用 -repackageclasses,请将类重新打包到库包内部的子包中。例如,使用 -repackageclasses 'com.example.mylibrary.internal' 而不是 -repackageclasses 'internal'

支持不同的代码压缩器(高级)

您可以根据特定代码压缩器(R8 或 ProGuard)以及特定代码压缩器版本调整规则。这使得您的库在使用新代码压缩器版本的项目中能够最佳运行,同时允许现有规则在较旧代码压缩器版本的项目中继续使用。

要指定有针对性的代码压缩规则,您需要将它们包含在 AAR 或 JAR 库内的特定位置,如下所述。

In an AAR library:
    consumer-proguard-rules.pro (legacy location)
    classes.jar
    └── META-INF
        └── com.android.tools (targeted shrink rules location)
            ├── r8-from-<X>-upto-<Y>/<R8-rules-file>
            └── proguard-from-<X>-upto-<Y>/<ProGuard-rules-file>

In a JAR library:
    META-INF
    ├── proguard/<ProGuard-rules-file> (legacy location)
    └── com.android.tools (targeted shrink rules location)
        ├── r8-from-<X>-upto-<Y>/<R8-rules-file>
        └── proguard-from-<X>-upto-<Y>/<ProGuard-rules-file>

这意味着有针对性的代码压缩规则存储在 JAR 的 META-INF/com.android.tools 目录中,或存储在 AAR 的 classes.jar 内部的 META-INF/com.android.tools 目录中。

在该目录下,可以有多个目录,其名称形式为 r8-from-<X>-upto-<Y>proguard-from-<X>-upto-<Y>,用于指示目录内的规则是为哪个代码压缩器的哪些版本编写的。请注意,-from-<X> 和 -upto-<Y> 部分是可选的,<Y> 版本是排他性的,并且版本范围必须是连续的。

例如,r8-upto-8.0.0r8-from-8.0.0-upto-8.2.0r8-from-8.2.0 构成了一组有效的有针对性的代码压缩规则。在 r8-from-8.0.0-upto-8.2.0 目录下的规则将被 R8 从 8.0.0 版本到(但不包括)8.2.0 版本使用。

鉴于此信息,Android Gradle 插件将从匹配的 R8 目录中选择规则。如果库未指定有针对性的代码压缩规则,Android Gradle 插件将从旧版位置(AAR 为 proguard.txt,JAR 为 META-INF/proguard/<ProGuard-rules-file>)选择规则。