如需启用应用优化,您必须使用与 Android 优化兼容的库。如果某个库未针对 Android 优化进行配置(例如,如果它使用 反射 而不捆绑关联的保留规则),则可能不适合 Android 应用。本页介绍了为何某些库更适合应用优化,并提供了一些一般提示来帮助您进行选择。
选择库时的一般提示
使用这些提示有助于确保您的库与应用优化兼容。
首选代码生成而非反射
选择使用 代码生成 (codegen) 而非反射的库。借助代码生成,优化器可以确定在运行时实际使用的代码以及可以移除的代码。很难判断库是使用代码生成还是反射,但有一些迹象可以帮助您判断,请参阅相关提示。
如需详细了解代码生成与反射,请参阅面向库作者的 优化。
检查是否使用了反射(高级)
您可以通过检查库的代码来判断它是否使用了反射。 如果库使用了反射,请检查它是否提供了关联的保留规则。如果库执行以下操作,则很可能使用了反射:
- 使用
kotlin.reflect或java.lang.reflect软件包中的类或方法。 - 使用函数
Class.forName或classLoader.getClass。 - 在运行时读取注解,例如,如果它使用注解值
使用
val value = myClass.getAnnotation()或val value = myMethod.getAnnotation(),然后对value执行某些操作。 使用方法名称作为字符串来调用方法,如以下示例所示:
// Calls the private `processData` API with reflection myObject.javaClass.getMethod("processData", DataType::class.java) ?.invoke(myObject, data)
检查是否存在优化问题
在考虑使用新库时,请查看该库的问题跟踪器和在线讨论,以检查是否存在与缩减或配置应用优化相关的问题。如果存在此类问题,您应尝试寻找该库的替代方案。请注意以下几点:
- AndroidX 库和 Hilt 等库与 应用优化配合使用效果良好,因为它们主要使用代码生成而非反射。当它们确实使用反射时,它们会提供最少的保留规则,以仅保留所需的代码。
- 序列化库经常使用反射来避免在实例化或序列化对象时使用样板代码。请寻找使用代码生成而非基于反射的方法(例如用于 JSON 的 Gson)的库,以避免这些问题,例如使用 Kotlin 序列化 {:.external} 或 使用代码生成的 Moshi。
- 如果可能,请避免使用包含软件包级保留规则的库。 软件包级保留规则有助于解决错误,但应最终优化广泛的保留规则,以仅保留所需的代码。如需了解更多 信息,请参阅逐步采用优化。
- 在发布使用第三方库的应用之前,请使用 R8 配置分析器 审核其提供的保留规则。通过查看报告,您可以验证库的保留规则是否过于宽泛,从而阻止 R8 对您的代码库执行关键优化。此检查可确保您选择的库与应用性能目标保持一致,并且不会引入不必要的配置膨胀。
- 库不应要求您将保留规则从文档复制并粘贴到项目中的文件中,尤其是软件包级保留规则。从长远来看,这些规则会给应用开发者带来维护负担,并且难以随着时间的推移进行优化和更改。
添加新库后启用优化
添加新库后,请启用优化并检查是否存在错误。如果存在错误,请寻找该库的替代方案或编写保留规则。如果库与优化不兼容,请针对该库提交 bug。
过滤掉不良的保留规则(高级)
保留规则是累加的。这意味着,库依赖项包含的某些规则无法移除,并且可能会影响对应用其他部分的编译。例如,如果某个库包含停用代码优化功能的规则,该规则会针对整个项目停用优化功能。
您应避免使用保留规则来保留应移除的代码的库。但如果您必须使用它们,则可以按照以下代码所示过滤掉这些规则:
// If you're using AGP 8.4 and higher
buildTypes {
release {
optimization.keepRules {
it.ignoreFrom("com.somelibrary:somelibrary")
}
}
}
// If you're using AGP 7.3-8.3
buildTypes {
release {
optimization.keepRules {
it.ignoreExternalDependencies("com.somelibrary:somelibrary")
}
}
}
案例研究:为何 Gson 会因优化而中断
Gson 是一个序列化库,由于大量使用反射,因此经常会导致应用优化出现问题。以下代码段展示了 Gson 的典型使用方式,这可能会导致运行时崩溃。请注意,当您使用 Gson 获取 User 对象列表时,您不会调用构造函数或将工厂传递给 fromJson() 函数。如果构建或使用应用定义的类时没有以下任何一项,则表明库可能使用了开放式反射:
- 实现库或标准接口或类的应用类
- KSP 等代码生成插件
class User(val name: String)
class UserList(val users: List<User>)
// This code runs in debug mode, but crashes when optimizations are enabled
Gson().fromJson("""[{"name":"myname"}]""", User::class.java).toString()
如需了解 R8 如何处理 Gson,请参阅 Gson 使用者规则。当 R8
分析此代码并且在任何位置都看不到 UserList 或 User 实例化时,它可以重命名字段或移除似乎未使用的构造函数,从而导致应用崩溃。如果您以类似方式使用任何其他库,则应检查它们是否会干扰应用优化,如果会,则应避免使用它们。
如需以与 Gson 的使用者规则兼容的方式定义类,请使用以下代码段作为参考:
class User(@com.google.gson.annotations.SerializedName("name") val name: String)
class UserList(@com.google.gson.annotations.SerializedName("users") val users: List<User>)
请注意,Room、Hilt 和使用代码生成的 Moshi 会构建 应用定义的类型,但会使用代码生成来避免使用反射。