分析Dalvik字节码进行减包优化

Android结合版最近几个版本在包大小配额上超标了,先后采用了包括图片压缩,功能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 Anull,增加了对应的<cinit>()方法的Dalvik指令,没有必要,可以干掉。

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

public int B=0;

这样的赋值行为会在之后的<init>()对象构造方法中执行,重复设置int B0,增加了对应的<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等同,这也是个优化点,具体可以参考文章 从字节码视角看java字符串的拼接 

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

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【概括介绍】 Dalvik字节码编辑器是一个强大安卓程序修改器,它运行在手机上,并且免费,主要用来汉化破解。 【基本介绍】 使用本工具需要基本编程经验和安卓的程序知识,我们知道安卓上的程序,大部分是用java开发的,编译生成字节码,这就注定程序更易于破解。传统的破解很麻烦,要在pc上进行,搭建开发环境,反编译dex代码回java,修改后,再编译成dex(困难),打apk Dalvik字节码编辑器则选择了完全不同的技术路线.既然java编译字节码,而字节码的文档是公开的,那就能够直接解释字节码,在程序上进行修改,就不用反编译/编译的繁琐过程,也不用java的复杂开发环境(类的的工具有八神的apk编辑器,要收费) 【使用方法】 首先从Lcom/lonelycatgames/Xplore/Browser;类入手,打开这个类,然后打开on*方法,可发现这段代码 1. 复制代码 这段代码大概意思是,判断软件是不是从安卓商店下载的,如果不是则得到当前系统时间并和作者设定的时间作比较,在设定的范围之内可以用,否则弹出去安卓商店下载之类的提示。如果是从商店下载的则直接跳过时间比较 这样我们只要修改 isInstalledFromMarket方法的返回值就行了,可发现isInstalledFromMarket方法在 Lcom/lonelycatgames/Xplore/BrowserUtils;类里面。 找到该类并打开,找到 isInstalledFromMarket方法打开,在return v2之前加入const/4 v2 1,也就是一直让这个方法返回true,保存.... 破解Total*er 打开Lcom/ghisler/android/Total*er/Total*er;类(也就是到 /com/ghisler/android/Total*er/目录下,打开Total*er), 找到on*方法打开,可发现 2.复制代码 大概意思就是当前时间与作者设定的时间作比较,如果超过设定时间就弹出Dialog提示,且不能继续使用。 看这段代码 if-lez v0 :label_1094这是程序跳转的关键点,判断寄存器v0的值是否 看这段代码 if-lez v0 :label_1094这是程序跳转的关键点,判断寄存器v0的值是否小于等于0,如果成立,跳转到label_1094(也就是可以继续使用)所以只要让v0的值不大于0就行了,在 if-lez v0 :label_1094前面加上const/4 v0 0。保存.... 去除QQ2011升级提示 用dalvik字节码编辑器v1.2版打开QQ2011,之后打开classes.dex/ com/ tencent/ secure/ jni/ SecureEngine/ Methods/<init>里的校验代码,在if-nez v0 :label_19上面加上const/4 v0 1然后 按功能键 保存代码,再按两次返回键,按功能键 保存Dex, 打开META-INF文件夹删除里面的ANDROID.RAS和ANDROID.SF。按功能键 保存。 修改到此完成。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值