常见的Android编译优化问题
编译常见问题
在开发过程中,有碰到过一些由于编译优化导致的代码修改并不符合我们预期的情况。这也就是之前为什么我经常说编译产物其实是不太可以被信任的。
- 方法签名变更,底层仓库的方法变更但是上层模块并没有跟随一起重新编译导致的这个问题。
- 常量优化,将一些常量的调用点直接替换成常量的值。
- 删除空导包, 没有用的一些导包就会做一次剔除。
最近倒霉了
我们最近碰到一个pipeline
相关而且很妖怪的问题。我们一个pipeline
会检查apk产物中是否存在异常的方法调用,就是之前介绍的在R8的基础上开发出来的A8。但是最近有一个类被删除了之后呢,但是代码中还有一处调用点。但是这个检测竟然被通过了,然后这部分代码就被合入了master。
这个引用的文件就如上图所示,是一个debug buildType
中的,所以并不是所有的apk中都会存在这部分代码。
然后呢,这个MergeRequest
就被合入了master
分支,因为当天是我们出下一个版本包的时间,然后交付给测试的就是全量编译的debug
和release
包。别的开发同学rebase完master之后就发现piepline
都跑不过了,就导致了他们当天的代码无法被合入。
这个就是事情大概的起因和经过,但是各位有没有想过为什么会发生这个问题吗。这个是不是我们的pipeline
出现了bug,导致了这种问题无法被识别出来了呢。
以前有说过,如果简单的说我们的快编系统就是把模块替换成对应的aar,从而达到编译提速。所以因为我们使用的是这个模块对应的aar产物,所以大概率就是因为这个模块的编译产物和源代码有差异导致了这个问题。
其实这个问题一出现我就已经知道大概率是由空导包优化导致的这个问题,因为在pipeline
检查的时候,检测的apk产物中确实不存在这个导包。因为我们使用的是一个历史版本的aar,其中无效导包的部分已经被编译器做了删除空导包的优化了。接下来我们看下我写的一个demo中的无效导包。
图一呢是源代码java文件,图二呢则是jar包中的代码。可以简单的看出来行号呢是可以对应的上的,但是这个AppCompatActivity
的无效导包在产物中已经被优化掉了。这里也就回答了在编译过程中会保留行号,但是也会优化掉一部分不需要的代码,让我们编译出来的产物更小。
所以也就导致了我们的产物和我们的源代码之间的差异,另外一个角度就是说从apk中我们确实是不存在这个类的导包。但是呢在我们把这部分代码重新编译成aar的时候,就会出现source缺失,导致的语法树无法生成,之后导致的编译失败问题。
这也就是所以我一直和大家说编译产物是不可以被信任的呢。
以前倒霉过
这个是之前的一个故事了,我们之前呢在模块中定义了一些静态常量吧,然后用来标识当前SDK的版本,然后这个值在别的模块中被引用到了。
有一次因为需求变更,我们更改了这个静态变量的值,然后呢我就把这个需求提测了。之后测试反馈给我为什么这边的这个值没有变化啊。
我的天,当时我就是这样,发生了什么情况。然后呢我全量打了个包好了,我当时也就以为只是编译时的一个bug而已。然后后来呢,我查了下资料发现这个就是一个java编译时的常量优化问题。过了一阵子吧,我面试了下字节跳动,然后我和面试官也聊了下这个话题,然后呢在这个方法签名变更的问题上,当时我略输一筹,哈哈哈哈。接下来我们就看下一个demo。
图1呢也是java代码,图2呢则是aar中的编译产物。其中我们可以看到,这个静态常量在编译成产物之后就会被编译成这样。
所以这个就解释了我一开始碰到的这个问题,他就是由于我们的编译器已经把aar中的这部分静态常量编译成了直接的值,然后呢我们的源变化之后如果没有重新编译对应的模块,就会导致这个值一直无法被更新到最新的值。
结论
如果大家对安卓编译相关有兴趣的话,这些问题很可能都会在面试的时候被问到。希望这不仅仅只是一篇我对于这些问题的思考,也能对各位有所帮助吧。
链接:https://juejin.cn/post/7131901763245834277
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。