【腾讯内部干货分享】安卓包体压缩——分析Dalvik字节码进行减包优化

小编导读:无论是开发还是发行,不可避免的会遇到包体过大需要压缩的情况。

对游戏整体的压缩却不影响场景,对图片的压缩却不影响品质。最麻烦的是对代码进行压缩,简直是让程序们熬白了头发只为包体再小个几K。

一般人通常通过检查Drawable资源、DailyBuild检查代码Diff等方式来做,这位手机qq空间的开发工程师却另辟蹊径,反编译DEX看Dalvik字节码,直接从最底层定位是什么占用了包体积。

尽管如今wifi遍地,但包体越小仍然可以具备优势,因为其降低了其他干扰因素存在的可能性。

作者:腾讯移动客户端开发工程师 彭旭康

Android手机Qznoe最近几个版本在包大小配额上超标了,先后采用了包括图片压缩,功能H5,无用代码移除等手段减包,还是有着很大的减包压力。组内希望我能从代码的角度减少一些包大小,感觉有点压力山大。经过一段时间对手q安装包反编译后的Dalvik字节码的分析,发现通过调整Java代码可以减少编译后的Dalvik字节码,从而减少包大小。在这方面我做了许多的尝试,有成功有失败,拿出来给大家分享分享。

优化思路

通过dexdump反编译apk中的dex,得到对应Dalvik字节码,找到寻找冗余的字节码,尝试去除或替换冗余的字节码。

目前主要是替换或去除原有的java代码,减少对应的Dalvik指令,从而减少安装包大小。

现在主要是从Dalvik字节码分析来调整Java代码,之后希望能够通过ASM等框架直接调整字节码减少现在的包大小。

优化效果

去除初始化赋值方案 ————减少整个手q的发布包大小80k左右。

插桩函数优化———减少整个手q的发布包大小2k左右。

其它尝试方案,包括字符串拼接、移除interface很多空方法等,因为效果比较小、难以统一修改等问题,只是列举下分析结果,大家如果项目中出现的量比较多也是可以尝试去优化的。

优化方案如下:

1、去除初始化赋值冗余

1.1、问题分析:

静态变量为类的所有对象共享,在类加载的准备阶段就会初始设置为系统零值(如下图),比如String被设置初始值为null,而在类中存在

这样的赋值行为会在之后的<cinit>()类构造器方法中执行,重复设置String A为null,增加了对应的<cinit>()方法的Dalvik指令,没有必要,可以干掉。

成员变量在对象创建内存分配完成后,对应的内存空间会被初始设置为系统零值(和静态变量一样),比如int类型被设置为0,而在类中存在

public int B=0;

这样的赋值行为会在之后的<init>()对象构造方法中执行,重复设置int B为0,增加了对应的<init>方法中的Dalvik指令,没有必要,可以干掉。

对于初始化赋值为系统分配默认零值的静态变量和成员变量,去掉初始化赋值,直接使用系统赋的系统零值,可以减少<cinit>和<init>中的Dalvik指令,从而减少包大小,而且可以提高类加载和对象创建的效率。

1.2、优化要点

注意对于static final的变量必须赋初值;

interface的变量都是static final类型的;

注意只有赋值为系统赋予的零值的静态变量和成员变量才能按照这种方式优化,其它比如局部变量的改动会导致编译不通过等问题。

1.3、冗余示例:

优化前:

对应字节码:

优化后:

对应字节码:

减少了两行Dalvik指令的执行,最后分析结果平均优化一处可以减少安装包8个字节左右。

1.4、优化结果:

目前在手Q6.3.0分支上利用自行写的过滤脚本(可以私下找我要对应的优化脚本用于对应的工程)可以看到优化的效果,如果对整个手q执行这个方案,预计能够优化80k左右,修改了4677个文件,修改了17164处冗余

2、调整插桩对应的代码

Qzone补丁包引入了插桩这一步,需要在所有qzone类的构造函数中加入对mqq.app.MobileQQ类的引用。 优化的方案是将插桩插入到对象构造函数中的语句由

改为

以Qzone某个类的<init>为例,由原本的字节码

变成了

这里替换一处代码,将System.out.print改成getName,可以减少对象构造函数的一行Dalvik指令,替换了1314处初始化函数中插入的代码,最终将对应的qzone_plugin.apk减少了2459字节,整个手q减少2457字节左右。<font color=#FF0000>一行代码,2k收益</font>,其实还是很划算的。

3、字符串拼接

下面是我针对String拼接的特殊情况“变量+”””和“””+变量”的不同形式举例分析Dalvik字节码。

字节码

从示例中可以看出各类字符串拼接方式的优劣,如果用String.valueOf()绝对是最优方案。只是通过对“变量+”””和“””+变量”的形式在手q整个项目调整以后大概能够优化6k左右,如果只是优化Qzone部分,效果比较微小,脚本方面不太好过滤对应情况,暂时没有加入,只是做了下试验。 PS:其实“String +”一般来说比StringBuffer的拼接更费字节码,这个部分可以自行验证,前提是a+b+…的形式中首位a这个为变量,而不是常量,如果a是常量,则实际上和StringBuffer等同,这也是个优化点。

4、调整interface到class,减少实现接口造成的空方法

很多代码中实现接口时有很多的空方法,并没有作用但还是会占用字节码,希望能够通过调整对应的interface为class,去除冗余的空方法,减少字节码,从而减少包大小。 示例如下:

改成

该方案的缺点在于修改必须手动,难度大,Qzone中场景不足以引起量变,而且因为Qzone中<init>中还加入了插桩函数的负担,所以整体优化效果不佳,优化完Qzone才2k不到的大小缩减,优化难度高收益小,弃坑。

这些减包思路希望能够给一起在减包路上踩坑的朋友们一些帮助吧。

本文为腾讯WeTest授权发布

转载请申请授权

关于腾讯WeTest

腾讯WeTest是腾讯游戏官方推出的一站式游戏测试平台,用十年腾讯游戏测试经验帮助广大开发者对游戏开发全生命周期进行质量保障 腾讯WeTest提供:兼容适配测试;云端真机调试;安全测试;耗电量测试;服务器压力测试;舆情监控等服务

原文发布于微信公众号 - 腾讯WeTest(TencentWeTest)

原文发表时间:2016-06-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Pythonista

Python之路,Day1 - Python基础1

python的创始人为吉多·范罗苏姆(Guido van Rossum)。1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解...

18220
来自专栏知识分享

12-MQTT介绍

看到这个项目第一想法肯定需要一个服务器,所有的wifi设备和手机都去连接这个服务器,然后服务器进行信息的中转

24540
来自专栏坚毅的PHP

HBase client访问ZooKeeper获取root-region-server DeadLock问题(zookeeper.ClientCnxn Unable to get data of zn

2012年11月28日 出现故障," Unable to get data of znode /hbase/root-region-server" 问题比较诡异...

56540
来自专栏个人随笔

Java核心技术(Java白皮书)卷Ⅰ 第一章 Java程序设计概述

第1章 Java程序设计概述 1.1 Java程序设计平台  具有令人赏心悦目的语法和易于理解的语言,与其他许多优秀语言一样,Java满足这些要求.  可移植...

384100
来自专栏轮子工厂

如果你想学好Python,这几本书说不定可以帮助到你哦

8120
来自专栏C语言及其他语言

【工具资源】迈进C世界的第一步

开始学c的小伙伴 肯定对两个问题焦头烂额 如何选择编译器 到哪里去下载想要的编译器 下面就让小编来帮大家解决这两个问题 ? 细心的小伙伴其实已经发现 咱们C语言...

36670
来自专栏企鹅号快讯

ForeSpider教程连载之链接抽取

自从来到前嗅,小编从一个爬虫小白到现在能够熟练的采集各种网站各种数据真的是有很大的成长,当然,成长过程中肯定少不了踩坑(很多网站都有防爬措施),为了让各位用户能...

23570
来自专栏程序猿DD

程序员你为什么这么累【续】:编码习惯-函数编写建议

之前系列文章里面完整的代码已经上github,地址在文章最后 傻瓜都能写出计算机可以读懂的代码,只有优秀的程序员才能写出人能读懂的代码! 在我看来,编写简单的函...

224100
来自专栏H2Cloud

Event Store框架探究

摘要:   游戏开发中,经常会越到千奇百怪的Bug。后台程序都是以demon 方式运行,要么GDB,要么Log。一些确定性的bug可以直接使用GDB调试,比如特...

40670
来自专栏机器之心

资源 | 这是一份收藏量超过2万6的计算机科学学习笔记

项目地址:https://github.com/CyC2018/Interview-Notebook

12330

扫码关注云+社区

领取腾讯云代金券