序言
咱们在做Android包体积优化时分会将Apk拖入AS中剖析,很自然发现Apk是由Dex、So、资源文件(resource.arsc,xml,asests等)三大部分组成,针对每一部分都能够进行相应的深入优化。可是咱们往往会疏忽Apk文件自身也是能够优化的,有点身入其间,不识庐山真面目的意思。 APK文件自身是一个ZIP文件,理解ZIP格局,从ZIP文件下手优化APK也是包体积优化不行疏忽的一部分。
ZIP格局简介
ZIP文件作为一个紧缩文件的归档格局,在大家在日常工作和学习中广泛运用,可谓是计算机文件传输宗族的顶梁柱,关于它的深入了解我认为是非常必要的,接下来咱们来看一下ZIP文件的格局组成。依照 ZIP规范 中,一个ZIP文件的全体格局如下,首要由三大部分组成数据区、中心目录记载区、中心目录记载尾部区。
----- 数据区
[local file header 1]
[file data 1]
[data descriptor 1]
.
.
.
[local file header n]
[file data n]
[data descriptor n]
----- 中心目录记载区
[archive decryption header] (EFS)
[archive extra data record] (EFS)
[central directory]
----- 中心目录记载尾部区
[zip64 end of central directory record]
[zip64 end of central directory locator]
[end of central directory record]
如下运用010 editor解析后的一个ZIP文件,该文件中只包括3个webp文件,基本上也能够看出是依照上述三大部分进行解析的。
数据区
咱们在日常运用都中都会往ZIP文件中放入多文件,直观地感觉是一切的文件被作为一个全体被紧缩的,但其实每个文件的数据都是单独紧缩的。这也不奇怪咱们在运用编程语言API读取文件时分是能够随机读取出任何一个Entry的,如果是一切文件全体紧缩的话,是很难高效单独读取的,因为紧缩算法的原理一般解压数据是需求先解压前面的,才干解压出后面的,这意味着只想解压一个存储靠后的文件效率时分非常低的,需求简直把一切文件悉数解压。铺垫了这么多,来看看一个文件紧缩后在ZIP中存储的相关信息对应的结构,首要由如下三个子部分:
[local file header]
[file data]
[data descriptor]
local file header
Offeset | Bytes | Description | |
---|---|---|---|
0 | 4 | local file header signature | 文件头标识 (固定值0x04034b50) |
4 | 2 | version needed to extract | 解压时遵循ZIP规范的最低版别 |
6 | 2 | general purpose bit flag | 通用标志位 |
8 | 2 | compression method | 紧缩方法 |
10 | 2 | last mod file time | 最终修正时刻(MS-DOS格局) |
12 | 2 | last mod file date | 最终修正日期(MS-DOS格局) |
14 | 4 | crc-32 | 冗余校验码(crc-32) |
18 | 4 | compressed size | 紧缩后的巨细 |
22 | 4 | uncompressed size | 未紧缩之前的巨细 |
26 | 2 | file name length | 文件名长度(n) |
28 | 2 | extra field length | 扩展区长度(m) |
30 | n | file name | 文件名 |
30+n | m | extra field | 扩展区 |
其间咱们首要重视两个信息:
- 解压最低版别:
2个字节,记载解紧缩文件所需的最低支撑的ZIP规范版别,apk解压版别默认是20, 即Deflate紧缩方法。当时最低功用版别界说如下:(紧缩包记载的解压版别都是需求版别*10,比方:2.0 * 10 = 20)
1.0 - 默认值
1.1 - 文件是卷标
2.0 - 文件是一个文件夹(目录)
2.0 - 运用 Deflate 紧缩来紧缩文件
2.0 - 运用传统的 PKWARE 加密对文件进行加密
2.1 - 运用 Deflate64™ 紧缩文件
2.5 - 运用 PKWARE DCL Implode 紧缩文件
2.7 - 文件是补丁数据集
4.5 - 文件运用 ZIP64 格局扩展
4.6 - 运用 BZIP2 紧缩文件紧缩
5.0 - 文件运用 DES 加密
5.0 - 文件运用 3DES 加密
5.0 - 运用原始 RC2 加密对文件进行加密
5.0 - 运用 RC4 加密对文件进行加密
5.1 - 文件运用 AES 加密进行加密
5.1 - 运用更正的 RC2 加密对文件进行加密
5.2 - 运用更正的 RC2-64 加密对文件进行加密
6.1 - 运用非 OAEP 密钥包装对文件进行加密
6.2 - 中心目录加密
- 紧缩方法
记载当时文件的紧缩方法,有如下12种,其间0表明原文件寄存不紧缩,8表明运用Deflate算法紧缩。JDK 7的Zip完成只支撑0和8两种,其他的均不支撑。关于Andorid Apk而言,大部分文件都是运用Defalte紧缩,也有一些情况下为了进步文件的运行时加载速度是挑选不紧缩的,比方resource.arsce, so文件等, 在不紧缩的情况下能够直接mmap,加速IO的速度。
0 - The file is stored (no compression)
1 - The file is Shrunk
2 - The file is Reduced with compression factor 1
3 - The file is Reduced with compression factor 2
4 - The file is Reduced with compression factor 3
5 - The file is Reduced with compression factor 4
6 - The file is Imploded
7 - Reserved for Tokenizing compression algorithm
8 - The file is Deflated
9 - Enhanced Deflating using Deflate64™
10 - PKWARE Data Compression Library Imploding
11 - Reserved by PKWARE
12 - File is compressed using BZIP2 algorithm
file data
file data紧跟在local file header之后,存储文件的具体数据,根据其紧缩方法不同或许是源文件自身数据也或许是紧缩后的数据。
data descriptor
只要当 local file header的 general purpose bit flag 字段第3位bit置1时,data descriptor才会存在。 它是字节对齐的,紧跟在文件数据的最终一个字节之后。当且仅当无法在ZIP 文件中查找时才运用此描述符,例如:当输出的ZIP文件是规范输出或不行查找设备时运用文件描述。 说人话便是,看看就好,正常情况下都不需求运用。
Offset | Bytes | Description | |
---|---|---|---|
4 | 4 | crc-32 | 冗余校验码 |
8 | 4 | compressed size | 紧缩后的巨细 |
12 | 4 | uncompressed size | 未紧缩之前的巨细 |
中心目录记载区
中心目录记载区是由一系列的file header所组成,一个file header对应数据区中的一个紧缩文件。
[file header 1]
.
.
.
[file header n]
[digital signature]
file header中存储的信息如下:
Offset | Bytes | Description | |
---|---|---|---|
0 | 4 | central file header signature | 文件头标识 (固定值0x02014b50) |
4 | 2 | version made by | 高位字节表明文件特点信息的兼容性, 低位字节表明紧缩软件支撑的ZIP规范版别 |
6 | 2 | version needed to extract | 解压时遵循ZIP规范的最低版别 |
8 | 2 | general purpose bit flag | 通用标志位 |
10 | 2 | compression method | 紧缩方法 |
12 | 2 | last mod file time | 最终修正时刻(MS-DOS格局) |
14 | 2 | last mod file date | 最终修正日期(MS-DOS格局) |
16 | 4 | crc-32 | 冗余校验码 |
20 | 4 | compressed size | 紧缩后的巨细 |
24 | 4 | uncompressed size | 未紧缩之前的巨细 |
28 | 2 | filename length | 文件名长度(n) |
30 | 2 | extra field length | 扩展区长度(m) |
32 | 2 | file comment length | 文件注释长度(k) |
34 | 2 | disk number start | 文件开端方位的磁盘编号 |
36 | 2 | internal file attributes | 内部文件特点 |
38 | 4 | external file attributes | 外部文件特点 |
42 | 4 | relative offset of local header | 对应 [local file header] 的偏移方位 |
46 | n | file name | 文件名 |
46+n | m | extra field | 扩展域 |
46+n+m | k | file comment | 文件注释内容 |
中心目录记载尾部区
中心目录记载尾部首要作用是用来定位中心目录记载区的开端方位,一起记载紧缩包的注释内容
Offset | Bytes | Description | |
---|---|---|---|
0 | 4 | end of central dir signature | 中心目录结束标识 (固定值0x06054b50) |
4 | 2 | number of this disk | 当时磁盘编号 |
6 | 2 | number of the disk with the start of the central directory | 中心目录开端方位的磁盘编号 |
8 | 2 | total number of entries in the central directory on this disk | 该磁盘上所记载的entry数量 |
10 | 2 | total number of entries in the central directory | 中心目录中总共的entry数量 |
12 | 4 | size of the central directory | 中心目录巨细 |
16 | 4 | offset of start of central directory with respect to the starting disk number | 中心目录开端方位相关于.ZIP archive开端的位移 |
20 | 2 | .ZIP file comment length | ZIP文件注释内容长度(n) |
22 | n | .ZIP file comment | ZIP文件注释内容 |
APK体积优化剖析
上述咱们现已对ZIP文件有了基本了解,知道三大部分中中心目录记载尾部只要固定数量的字节,是很小的,因而从ZIP视角看首要是针对中心目录记载区、数据区进行优化。
中心目录记载区优化
中心目录区是由一系列的file header组成的,占用的空间大致受file heaer巨细和数量两个因素影响。
资源混杂
从削减单个file header巨细的视点出发,剖析其间包括的的信息格局,字段巨细基本上是固定的,有些无从下手,通过一通猜测有两个当地咱们或许是能够优化的,因为它们的内容长度可变,一个是file name, 另一个是file comment。 但关于Android Apk文件而言一般打包过程中并不会写入file comment. 那么file name到底能不能优化呢?
Apk中的许多file name咱们是能够自界说的,比方res目录下的文件res/xxhdpi/test.webp
,咱们完全能够叫做res/xxhdpi/a.wep
,这样就比本来的字符更加短,所占用的空间也更加少。那咱们是不是能够将其缩短为r/a.webp
或许更极端缩短为a.webp
,答案是能够的,这也便是咱们耳熟能详资源混杂。因为咱们代码或许编译过后的xml中基本上都是运用资源id来进行资源加载的,而资源id和资源文件途径的对应联系时分存储在resoure.arsc文件中的,这样就给了咱们可乘之机,咱们通过修正文件途径,而且一起修正resource.arsc文件,即可确保运行时资源加载的正确性。这个优化除了优化ZIP中心目录记载区之外,也一起能优化resource.arsc文件巨细。
shrikResources
从削减file header的数量视点出发,首要便是尽或许删去APK内的无用文件,因为Apk中数量最多的是资源文件,所以shrinkResources对这部分有显着的奉献,最好开启方法作用更好。不过删去无用文件的收益首要仍是来自文件巨细自身,削减file header仅仅其”隐形“的附加的收益。
总的来说中心目录记载区占用的巨细并不是很大,优化空间也比较有限。
数据区优化
数据区是占用空间的大头,同样受单个巨细和数量两个因素影响。
进步紧缩率
单个巨细的主体是文件紧缩后的数据,从ZIP的视角看便是如何进步紧缩率。上面我知道ZIP支撑许多紧缩方法,整体切入点有两个。
- APK内并不是一切的文件都是紧缩的,有些文件是直接Store的,能够考虑将Sotore改为紧缩状态。
- APK运用的Deflate其实并不是紧缩率最高的算法,能够考虑替换紧缩率更高的算法。不过替换紧缩算法的话需求考虑解压器,Android(JDK) 的ZIP完成只支撑Store和Deflate两种,这就约束APK只能运用Deflate算法,那这样就没有优化空间了么?不然,Deflate算法仅仅个规范,具体的完成也是有优劣的,JDK的Deflate紧缩并不算很优,运用更优Defalte算法也是测验的方向之一。
Store改为紧缩
public class PackagingUtils {
/**
* List of file formats which are already compressed or don't compress well, same as the one
* used by aapt.
*/
public static final ImmutableList<String> DEFAULT_AAPT_NO_COMPRESS_EXTENSIONS =
ImmutableList.of(
".jpg", ".jpeg", ".png", ".gif", ".opus", ".wav", ".mp2", ".mp3", ".ogg",
".aac", ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", ".rtttl", ".imy",
".xmf", ".mp4", ".m4a", ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", ".amr",
".awb", ".wma", ".wmv", ".webm", ".mkv");
}
如上代码所示,考虑到运行时的性能,AGP在package阶段针对如上的文件格局不进行紧缩,这些文件以Store方法寄存到APK中。从包体积的视点考虑的话能够装备这些类型的文件紧缩,AndResguard也供给了这项才能,如下咱们的使用现已装备了其间四种格局的文件进行紧缩。
andResGuard {
use7zip = true
compressFilePattern = [
"*.png",
"*.jpg",
"*.jpeg",
"resources.arsc",
]
}
但仍然有一些文件是未紧缩的,通过unzip -lv test.apk > zipinfo.txt
来检查文件的紧缩状态
如上有.webp , .mp3 , .jar文件没有进行紧缩,但通过测验发现仅有jar文件能紧缩并有用。
jar文件的紧缩前后变化
紧缩前:
紧缩后:
-
webp自身便是一种数据高度紧缩的文件格局,许多webp通过Defalte紧缩之后会更大,因而一些ZIP紧缩器在紧缩过程中挑选将其以Stored方法寄存。
-
mp3却是紧缩之后能够获取80KB的收益,但因为MediaPlayer在播映assets或许raw目录下的mp3时分经常会用到如下两个api,会在native层直接mmap,而mmap要求不紧缩而且四字节对齐,不然会报错。
更优Defalte算法
依照 7z官网的说法 7-Zip创立的 zip 格局比大大都其它紧缩软件创立的都小 2-10%,因而AndResguard运用命令 7z a -tzip out.apk ./apkdir/* -mx=9
对APK进行从头紧缩,此时运用的仍然是Deflate算法,紧缩等级为最大9,得到的APK的确小了2%左右。我现在并未对Deflate以及7z的完成进行深入研究,依照微信的说法7z运用了大字典优化。
咱们也测试和验证了另一个对ZIP重紧缩的库advzip,其运用libdeflate算法,发现其比7z紧缩之后的更小,能够再小1%左右,现已在咱们的使用上做了验证。紧缩前后信息比照方下图,左侧为7z紧缩之后的,右侧为advzip紧缩之后的。其间Defl:X表明紧缩的最好,Defl:N表明正常紧缩,从紧缩前后entry的size上也能够看出收益。 advzip 库重紧缩会把apk内一切的文件都紧缩,不支撑装备一些文件不紧缩,这个需求修正代码扩展一下功用。
删去无用文件
从削减数量上看,首要仍是无用文件删去,这个首要仍是依赖APK内部文件所对应的优化手段去完成,本文不做具体讨论。
文件兼并
从进步ZIP文件全体紧缩视角看,还有另一个切入点文件兼并,因为ZIP文件是单个文件紧缩,无损紧缩的方法只要重复数据紧缩、编码紧缩两种,而多个文件兼并到一起之后重复数据会更多,而且编码紧缩需求的字典也只需求一份,因而整体上能进步紧缩率。该部分关于Dex这种巨细有约束的文件并没有什么空间,如果能将一些So文件兼并或许事务上的资源文件兼并应该会有些优化作用,现在为止这部分并没有重大作用的优化实践。
总结
本文先对ZIP文件格局做了简单的介绍,并在ZIP文件的视角下剖析或许对Apk体积优化的当地。通过逐个对ZIP文件进行拆分以及挖掘,引出了的资源混杂、shrinkResources、进步紧缩率,文件兼并等优化。或许很立异的当地不是许多,许多知识都是旧的知识,可是以一种更加体系的剖析方法出现出来,期望能对大家有所帮助,也期望后来者能有更多的探究和立异吧。
参阅文档
ZIP紧缩算法具体剖析及解压实例解说
zip 的紧缩原理与完成
浅析ZIP格局
紧缩包Zip格局详析