如需启用应用优化,您必须使用与 Android 优化兼容的库。如果某个库未针对 Android 优化进行配置(例如,如果它使用反射,但未捆绑关联的 keep 规则),则可能不太适合 Android 应用。此页面将说明为何某些库更适合应用优化,并提供一些有助于您选择库的一般提示。
首选代码生成而非反射
一般来说,您应该选择使用代码生成 (codegen) 而不是反射的库。借助代码生成,优化器可以更轻松地确定在运行时实际使用的代码以及可以移除的代码。很难判断库是使用代码生成还是反射,但有一些迹象可以帮助您做出判断,请参阅提示。
如需详细了解代码生成与反射,请参阅面向库作者的优化。
选择库时的一般提示
请参考以下提示,确保您的库与应用优化功能兼容。
检查是否存在优化问题
考虑使用新库时,请查看该库的问题跟踪器和在线讨论,以检查是否存在与缩小或配置应用优化相关的问题。如果有,您应该尝试寻找该库的替代方案。请注意以下几点:
- AndroidX 库和 Hilt 等库使用代码生成而非反射,因此非常适合应用优化。如果使用反射,它们会提供最少的保留规则,以仅保留所需的代码。
- 序列化库在实例化或序列化对象时经常使用反射来避免样板代码。请寻找使用代码生成的库(例如 Kotlin 序列化),而不是基于反射的方法(例如用于 JSON 的 Gson),以避免这些问题。
- 应尽可能避免使用包含软件包级保留规则的库。软件包范围的保留规则有助于解决错误,但最终应细化广泛的保留规则,仅保留所需的代码。如需了解详情,请参阅逐步采用优化。
- 库不应要求您将保留规则从文档复制并粘贴到项目中的文件中,尤其是软件包范围的保留规则。从长远来看,这些规则会给应用开发者带来维护负担,并且难以随着时间的推移进行优化和更改。
添加新库后启用优化
添加新库后,启用优化功能并检查是否存在错误。如果存在错误,请寻找该库的替代方案或编写保留规则。如果某个库与优化不兼容,请针对该库提交 bug。
规则可累加
请注意,保留规则可累加。这意味着,库依赖项包含的某些规则无法移除,并且可能会影响应用其他部分的编译。例如,如果某个库包含停用代码优化的规则,该规则将针对整个项目停用优化。
检查是否使用了反射(高级)
您或许可以通过检查库的代码来判断该库是否使用了反射。 如果库使用反射,请检查它是否提供关联的保留规则。如果库执行以下操作,则可能正在使用反射:
- 使用
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)
过滤掉不良的保留规则(高级)
您应避免使用具有保留规则的库,这些规则会保留本应移除的代码。但如果您必须使用这些规则,可以按以下代码所示过滤掉这些规则:
// 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 分析此代码时,如果发现任何地方都没有实例化 UserList 或 User,它可能会重命名字段或移除似乎未使用的构造函数,从而导致应用崩溃。如果您以类似方式使用任何其他库,则应检查这些库是否会干扰应用优化,如果会,请避免使用这些库。
请注意,Room 和 Hilt 都会构建应用定义的类型,但会使用代码生成来避免需要反射。