5.2.2 直接修改 apk 的 byte 数组
Apk 其实就是一个 zip 文件,关于 zip 文件的介绍可以参考 Zip 的官方文档。
简单总结一下,zip 文件是由数据区、中央目录记录区、中央目录尾部区组成(高版本的 zip 文件增加了新的内容)。
- 中央目录尾部区:通过尾部区我们可以知道 zip 包中文件的数目、中央目录记录区的位置等信息;
- 中央目录记录区:通过尾部区我们可以快速找到中央目录记录区中的每一条文件记录,这些记录主要描述了 zip 包中文件的基本属性如文件名、文件大小、是否压缩、压缩后的大小、文件在数据区中的偏移等;
- 数据区:数据区用来存放文件真实的内容,根据中央目录记录区记录的内容,可以快速在数据区找到对应的文件元数据以及文件的真实数据(如果压缩,则是压缩后的数据)。
开始干活
了解了 zip 文件的格式后,我们只需要按照文件格式协议,在 apk 中找到我们需要修改的文件数据在 apk 中的偏移量,然后结合前面修改 axml/arsc 文件的方式,直接修改对应的 byte 数组即可。借助 java 为我们提供的 RandomAccessFile 工具,我们可以快速的文件的任意位置进行读取/写入。
修改过程中发现,apk 中的 xml 文件大部分是被压缩的( res/xml 目录下的一般不压缩),这就导致我们从 apk 中拿出来的 byte 数组是 axml 被压缩后的数据,我们要对这段数据进行修改,需要先利用 Deflate 算法对它进行解压( zip 文件中一般都是用的 Deflate 算法),然后进行修改再压缩,但是经过我们修改后,可能重新压缩出来的数据就与修改前的数据长度不匹配了,如果是缩短还好,修改一下文件元数据即可,如果文件长度变长可能会导致后面文件的偏移量都要改变,牵一发而动全身。
好在插件的打包过程我们是可以侵入的,前面介绍“插件编译时工作”时,我们在编译时拿到了需要修改的文件,因此我们只需要控制 apk 打包时不要对这些文件进行压缩(事实上 Android Target30 也要求 arsc 文件不进行压缩)。这样就很简单的解决了问题,当然会导致插件包体积的增加。
最终测试在直播插件中,开启这个功能会导致包体积增加 20kb,对于接近 30mb 体积的直播插件来说,这个增量是可以接受的,而且也不会影响宿主包体积。(这个增量取决于插件有多少 xml 使用了宿主资源,一般插件的增量应该都是小于直播插件的。)
改造完成后,经测试,直播插件在各个版本手机上修改时长大约在 300~700ms 之间,修改速度提升了 10~90 倍。大部分插件也比直播插件小,耗时可以保证在 100ms 之内。同时这个修改过程仅在插件第一次安装或者宿主升级时做,并且是在后台完成,所以是完全可以接受的。
欢迎加入 AppHealth 团队
AppHealth 是字节跳动的一个客户端技术专家团队,专注于动态化、App 性能、稳定性、构建等方向。旨在通过对操作系统内核、虚拟机、工具链、编译器等方向的深度优化和建设,为字节跳动内部几十款 App(包括抖音、头条、飞书等)提供行业领先的终端体验优化能力,助力公司业务的高效、高质发展。 扫码投递,或发送简历到邮箱:xuekai.xk@bytedance.com