序言

咱们在做Android包体积优化时分会将Apk拖入AS中剖析,很自然发现Apk是由Dex、So、资源文件(resource.arsc,xml,asests等)三大部分组成,针对每一部分都能够进行相应的深入优化。可是咱们往往会疏忽Apk文件自身也是能够优化的,有点身入其间,不识庐山真面目的意思。

从ZIP文件看包体积优化
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文件看包体积优化

数据区

咱们在日常运用都中都会往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来检查文件的紧缩状态

从ZIP文件看包体积优化

从ZIP文件看包体积优化

从ZIP文件看包体积优化

如上有.webp , .mp3 , .jar文件没有进行紧缩,但通过测验发现仅有jar文件能紧缩并有用。

jar文件的紧缩前后变化

紧缩前:

从ZIP文件看包体积优化

紧缩后:

从ZIP文件看包体积优化

  • webp自身便是一种数据高度紧缩的文件格局,许多webp通过Defalte紧缩之后会更大,因而一些ZIP紧缩器在紧缩过程中挑选将其以Stored方法寄存。

  • mp3却是紧缩之后能够获取80KB的收益,但因为MediaPlayer在播映assets或许raw目录下的mp3时分经常会用到如下两个api,会在native层直接mmap,而mmap要求不紧缩而且四字节对齐,不然会报错。

从ZIP文件看包体积优化

从ZIP文件看包体积优化

更优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上也能够看出收益。

从ZIP文件看包体积优化
advzip 库重紧缩会把apk内一切的文件都紧缩,不支撑装备一些文件不紧缩,这个需求修正代码扩展一下功用。

删去无用文件

从削减数量上看,首要仍是无用文件删去,这个首要仍是依赖APK内部文件所对应的优化手段去完成,本文不做具体讨论。

文件兼并

从进步ZIP文件全体紧缩视角看,还有另一个切入点文件兼并,因为ZIP文件是单个文件紧缩,无损紧缩的方法只要重复数据紧缩、编码紧缩两种,而多个文件兼并到一起之后重复数据会更多,而且编码紧缩需求的字典也只需求一份,因而整体上能进步紧缩率。该部分关于Dex这种巨细有约束的文件并没有什么空间,如果能将一些So文件兼并或许事务上的资源文件兼并应该会有些优化作用,现在为止这部分并没有重大作用的优化实践。

总结

本文先对ZIP文件格局做了简单的介绍,并在ZIP文件的视角下剖析或许对Apk体积优化的当地。通过逐个对ZIP文件进行拆分以及挖掘,引出了的资源混杂shrinkResources进步紧缩率文件兼并等优化。或许很立异的当地不是许多,许多知识都是旧的知识,可是以一种更加体系的剖析方法出现出来,期望能对大家有所帮助,也期望后来者能有更多的探究和立异吧。

参阅文档

ZIP紧缩算法具体剖析及解压实例解说

zip 的紧缩原理与完成

浅析ZIP格局

紧缩包Zip格局详析