http://wetest.qq.com/lab/view/?id=96?from=ads_test2_qqtips&sessionUserType=BFT.PARAMS.192184.TASKID&ADUIN=192726456&ADSESSION=1465349342&ADTAG=CLIENT.QQ.5479_.0&ADPUBNO=26582

小编导读:无论是开发还是发行,不可避免的会遇到包体过大需要压缩的情况。
对于发行商来说,尽管现在wifi遍地,但就算移动运营有一天开放4G免费,包体越小的你依然具备优势。
对于玩家来说则更为简单明了,用户喜欢连续点击几个应用同时下载,包体小的很快就跑完读条更容易被玩家接受。
但真正压缩的时候遇到的麻烦事可真的不少:对游戏整体的压缩却不影响场景,对图片的压缩却不影响品质。最惨的对代码进行压缩,简直是让程序们熬白了头发只为包体再小几K。
今天,直面减包优化这件事,分享一下腾讯人是怎么进行减包的!

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

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 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等同,这也是个优化点,具体可以参考文章 从字节码视角看java字符串的拼接 。

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

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

改成

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

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

转载于:https://www.cnblogs.com/exmyth/p/5569933.html

【腾讯内部干货分享】分析Dalvik字节码进行减包优化相关推荐

  1. 彻底弄懂dalvik字节码【三】

    [一].[二]中从代码的角度分析了dalvik字节码解释执行的过程,这篇文章以一个例子来实际分析一下. 我们以这篇文章中提到的crackme为例,下载链接参见那篇文章.我们只分析dalvik字节码,因 ...

  2. 【腾讯Bugly干货分享】Android进程保活招式大全

    [腾讯Bugly干货分享]Android进程保活招式大全 本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57ac4a0ea374 ...

  3. 应用程序文件Android安全分析挑战:运行时篡改Dalvik字节码

    发一下牢骚和主题无关: 本文章由Jack_Jia编写,转载请注明出处. 文章接链:http://blog.csdn.net/jiazhijun/article/details/8833710 作者:J ...

  4. 开发一个基于Dalvik字节码的相似性检测引擎,比较同一款Android应用程序的不同版本之间的代码差异(二)

    上文我们说过,<针对Dalvik字节码的相似性检测引擎,比较同一款Android应用程序的不同版本之间的代码差异>这篇文章计划分两个部分来讲解,上文只介绍了如何利用Quarkslab公司开 ...

  5. 彻底弄懂dalvik字节码【一】

    之前曾经简单跟踪过代码,知道dalvik的字节码是可以支持解释执行的,所谓的解释执行,其实就是c/c++编写的用于解释并执行dalvik字节码的程序,说白了就是dalvik字节码到cpu字节码的转换. ...

  6. 深入理解Dalvik字节码指令及Smali文件

    转自:http://blog.csdn.net/dd864140130/article/details/52076515 今天来介绍有关Dalvik虚拟机相关的知识,首先便是介绍我们最关心的Dalvi ...

  7. linux 网卡只收到包不发包,【干货分享】Linux虚拟机网卡只能收包不能发包?

    [干货分享]Linux虚拟机网卡只能收包不能发包?: U1 d; M2 ~  ]7 Q: J5 M- v# J3 @ * v; Y  P1 Q$ ]: I' T8 z在ovs场景主机与同主机上的虚拟机 ...

  8. Dalvik字节码类型对照表

    注意:"boolean"用大写的"Z"表示,"long"用大"J"表示,"java类类型"用大写&q ...

  9. 从0到1 Android安全学习之路 -- Java 字节码和 Dalvik 字节码

    Java 字节码和 Dalvik 字节码 概述 源代码样例 Java 字节码 Dalvik 字节码 总结 概述   本篇博客将讲述 Java 源代码到字节码,字节码转汇编,以及 Android 中 J ...

  10. 【Groovy】Groovy 扩展方法 ( 实例扩展方法配置 | 扩展方法示例 | 编译实例扩展类 | 打包实例扩展类字节码到 jar 包中 | 测试使用 Thread 实例扩展方法 )

    文章目录 一.扩展方法示例 二.实例扩展方法配置 三.编译实例扩展类 四.打包静态扩展类字节码到 jar 包中 五.测试使用 Thread 实例扩展方法 一.扩展方法示例 为 Thread 扩展 he ...

最新文章

  1. 推荐8个舍不得分享的实用软件和网站,解决很多需求
  2. 张亚勤:对于产业来讲,深度学习的黄金时代刚刚开始
  3. springside / springside4—CRUD页面教程
  4. openstack常用命令及控制节点端口一览
  5. 自然语言处理在医学领域的应用
  6. 大学计算机无线论文范文大全,大学计算机论文范文
  7. DOM Element对象的offsetXXX方法
  8. C语言rec文件如何打开,REC 文件扩展名: 它是什么以及如何打开它?
  9. 使用Navicat计划任务备份数据库
  10. 一步一步写算法(之n!中末尾零的个数统计)
  11. python发送图片邮件exchangelib_使用Python-Exchangelib库的电子邮件正文中的表格式
  12. “Talk is cheap, show me the code”你一行代码有多贵?
  13. .NET WebAPI 用ExceptionFilterAttribute实现错误(异常)日志的记录(log4net做写库操作)...
  14. python花瓣飘零_PYTHON抓取花瓣网高清美图
  15. 如何快速分解CAD图纸中多个合并的CAD图形?
  16. C\C++ | FILE文件基本操作函数
  17. 华为自动生成html文件夹,华为手机怎么建桌面文件夹
  18. 蜀门 - 青城加点完美攻略
  19. 对Redis中主从复制、哨兵模式和集群进行部署
  20. 打造老年人的健康监测产品很有市场(随记)

热门文章

  1. 顺序存储循环队列的基本操作
  2. docker重启容器
  3. Caffe学习3:Layer
  4. 基础知识(四)C++常用函数.txt
  5. 2021-08-04 Mysql自连接
  6. 怎样访问远程服务器文件夹,远程访问服务器文件夹
  7. mysql 连接 互联网_互联网技术分享社区 MySQL字符串连接函数
  8. mysql手写data.sql ,使用语句创建数据库(创建数据库/表 , 设置时间(date)的默认值(default),设置字符集)
  9. javascript 本地存储(cookies、sessionStorage和localStorage解释及区别)
  10. 抄答案就是了,两套详细的设计方案,解决头疼的支付掉单问题