搞定 Class 常量池中的大坑,R 的 AGP 之旅,这一次真的让编译器停止了?
2023-09-26 19:30:39
昨天发了一篇关于 AGP 惹的祸的文章(记录一次 AGP 调研过程中的思考,我从一个事故搞出了一个故事!),主要分析了当 AGP 和 R 的 class 在 AGP 编译阶段会造成 class 常量池异常的问题。文章发布后,有小伙伴说自己也遇到了类似的问题,不过并不是在 AGP 编译阶段,而是在 javac 阶段。巧了,我也遇到过类似问题,而且也是在 javac 阶段。因此,今天就来和大家聊聊 R 的 class 在 javac 阶段引发的另一个问题。
我们先来看一下我同事遇到的问题,当时他给我的 log 是这样的:
java.lang.NoSuchMethodError: 'int java.util.Collections.binarySearch(java.util.List, java.lang.Comparable, java.util.Comparator)'
从这个错误信息可以看出,问题出在 `java.util.Collections.binarySearch` 方法上。而这个方法在 R 中其实是有实现的,并且 R 也将这个方法贡献给了 AndroidX,因此在 Android 项目中,`java.util.Collections.binarySearch` 方法应该是由 R 实现的。但是,从我同事给我的 log 中可以看出,这个方法并不是由 R 实现的,而是由 JDK 实现的。这说明 R 的 class 在编译阶段被排除了。
为了验证这个猜测,我让同事将 R 的 aar 包添加到他的项目中,并重新编译。果然,编译成功了。这说明 R 的 class 在编译阶段确实被排除了。那么,问题来了,为什么 R 的 class 会被排除呢?
经过一番排查,我发现问题出在 R 的 aar 包的清单文件中。在清单文件中,有一个名为 `proguard-rules.pro` 的文件。这个文件的作用是指定 ProGuard 在混淆 R 的 class 时需要遵循的规则。而在这个文件中,有一条规则是:
-keep class com.google.protobuf.FieldPresenceTest*
这条规则的意思是,保留 `com.google.protobuf.FieldPresenceTest*` 开头的所有 class。而 R 中正好有一个 class 名为 `com.google.protobuf.FieldPresenceTest`,因此这条规则将这个 class 排除了。也就是说,R 的 class 在编译阶段被排除,是因为 ProGuard 根据清单文件中的规则将其排除了。
知道了问题的原因,解决方法就简单了。只需要将清单文件中的这条规则删除即可。但是,需要注意的是,这条规则并不是 R 无缘无故添加的。它主要是为了解决一个 bug。因此,在删除这条规则之前,需要确保这个 bug 已经解决了。如果 bug 没有解决,删除这条规则可能会导致新的问题。
经过一番折腾,我同事终于解决了这个问题。他将清单文件中的那条规则删除,重新编译,项目顺利通过编译。至此,R 的 class 在 javac 阶段被排除的问题终于解决了。
通过这个案例,我们可以学到以下几点:
- R 的 class 在编译阶段被排除,可能是因为 ProGuard 根据清单文件中的规则将其排除了。
- 在删除 ProGuard 规则之前,需要确保该规则不是为了解决某个 bug 而添加的。
- 在解决问题时,需要仔细排查问题的原因,不能盲目地尝试各种方法。
好了,关于 R 的 class 在 javac 阶段引发的另一个问题就聊到这里。希望这篇文章对大家有所帮助。如果大家在 R 的使用过程中遇到了其他问题,欢迎留言讨论。
最后,我想说的是,R 是一个非常强大的工具。但是,使用 R 时也需要小心谨慎。特别是当 R 的 class 与其他库的 class 发生冲突时,需要仔细排查问题的原因,并找到合适的解决方法。
好了,今天就到这里。我们下期再见!