ZIP File Compression

The AP_ File

In the Android build process, the actual work of merging dex, resources, assets, so, and other files into an APK is done in the package Task. Before the APK is generated, there's already a prototype - the AP_ file, which is the artifact of the processRes Task. It contains:

  1. AndroidManifest.xml
  2. res/*
  3. resources.arsc

Where res/* are all merged resources, and resources.arsc is the index table for res/* (see: Resource Table Overview). We can check the file format using the file command:

$ file ./build/intermediates/processed_res/debug/out/resources-debug.ap_
1

This produces the following output:

./build/intermediates/processed_res/debug/out/resources-debug.ap_: Zip archive data
1

It turns out the AP_ file is just a regular ZIP file. Let's view its contents using the unzip command:

$ unzip -lv ./build/intermediates/processed_res/debug/out/resources-debug.ap_
1

This produces the following output:

Archive:  ./build/intermediates/processed_res/debug/out/resources-debug.ap_
 Length   Method    Size  Cmpr    Date    Time   CRC-32   Name
--------  ------  ------- ---- ---------- ----- --------  ----
    2240  Defl:N      816  64% 01-01-1980 08:00 ad4de798  AndroidManifest.xml
     388  Defl:N      217  44% 01-01-1980 08:00 1d501a5f  res/anim/abc_fade_in.xml
     388  Defl:N      218  44% 01-01-1980 08:00 0bab7627  res/anim/abc_fade_out.xml
     852  Defl:N      376  56% 01-01-1980 08:00 6225a06b  res/anim/abc_grow_fade_in_from_bottom.xml
     508  Defl:N      258  49% 01-01-1980 08:00 41a0b5fb  res/anim/abc_popup_enter.xml
     508  Defl:N      260  49% 01-01-1980 08:00 aa2f234a  res/anim/abc_popup_exit.xml
     852  Defl:N      376  56% 01-01-1980 08:00 687167d1  res/anim/abc_shrink_fade_out_from_bottom.xml
     396  Defl:N      228  42% 01-01-1980 08:00 505f4409  res/anim/abc_slide_in_bottom.xml
     396  Defl:N      229  42% 01-01-1980 08:00 62c18818  res/anim/abc_slide_in_top.xml
     396  Defl:N      227  43% 01-01-1980 08:00 7280bebd  res/anim/abc_slide_out_bottom.xml
     396  Defl:N      228  42% 01-01-1980 08:00 6c5848d3  res/anim/abc_slide_out_top.xml
     388  Defl:N      217  44% 01-01-1980 08:00 a5fe5082  res/anim/abc_tooltip_enter.xml
     388  Defl:N      217  44% 01-01-1980 08:00 82fd0cc5  res/anim/abc_tooltip_exit.xml

     ......

    2060  Stored     2060   0% 01-01-1980 08:00 1d1cb314  res/mipmap-mdpi-v4/ic_launcher.png
    2783  Stored     2783   0% 01-01-1980 08:00 c64dbd08  res/mipmap-mdpi-v4/ic_launcher_round.png
    2963  Stored     2963   0% 01-01-1980 08:00 78bc849d  res/mipmap-hdpi-v4/ic_launcher.png
    4905  Stored     4905   0% 01-01-1980 08:00 ac8a9f01  res/mipmap-hdpi-v4/ic_launcher_round.png
    4490  Stored     4490   0% 01-01-1980 08:00 bd833a1f  res/mipmap-xhdpi-v4/ic_launcher.png
    6895  Stored     6895   0% 01-01-1980 08:00 56433f6e  res/mipmap-xhdpi-v4/ic_launcher_round.png
    6387  Stored     6387   0% 01-01-1980 08:00 ef9c5596  res/mipmap-xxhdpi-v4/ic_launcher.png
   10413  Stored    10413   0% 01-01-1980 08:00 32b2e261  res/mipmap-xxhdpi-v4/ic_launcher_round.png
    9128  Stored     9128   0% 01-01-1980 08:00 84b40e39  res/mipmap-xxxhdpi-v4/ic_launcher.png
   15132  Stored    15132   0% 01-01-1980 08:00 d8318666  res/mipmap-xxxhdpi-v4/ic_launcher_round.png
     448  Defl:N      222  50% 01-01-1980 08:00 f568abe3  res/mipmap-anydpi-v26/ic_launcher.xml
     448  Defl:N      222  50% 01-01-1980 08:00 f568abe3  res/mipmap-anydpi-v26/ic_launcher_round.xml
  260132  Stored   260132   0% 01-01-1980 08:00 ab649898  resources.arsc
--------          -------  ---                            -------
  609517           510626  16%                            440 files
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

Column 2 Method shows the compression method used when storing files in the ZIP file. Refer to ZipEntry.setMethod(int)open in new window in JDK References:

setMethod
public void setMethod(int method)

Sets the compression method for the entry.

Parameters:
method - the compression method, either STORED or DEFLATED
Throws:
IllegalArgumentException - if the specified compression method is invalid

About the definitions of STORED and DEFLATED:

STORED
public static final int STORED

Compression method for uncompressed entries.
DEFLATED
public static final int DEFLATED

Compression method for compressed (deflated) entries.

Therefore, files packaged with ZIP are not necessarily all compressed; some are uncompressed. This is easy to understand - files like images, audio, and video are already encoded and compressed, and compressing them again with ZIP won't reduce their size much. Some may even become larger after compression. This opens another door for APK size optimization.

The Solution

Since the AP_ file is the artifact of the processRes Task, we can re-compress the AP_ file directly after the processRes Task completes. The logic is simple and clear:

@AutoService(VariantProcessor::class)
class ProcessedResourcesCompressionVariantProcessor : VariantProcessor {

    override fun process(variant: BaseVariant) {
        val results = CompressionResults()

        variant.processResTask.doLast {
            variant.compressProcessedRes(results)
            variant.generateReport(results)
        }
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13

Getting Started

To enable ZIP compression, simply include booster-task-compression-processed-resopen in new window, as shown below:

buildscript {
    ext {
        kotlin_version = "1.5.31"
        booster_version = "4.16.3"
    }
    repositories {
        mavenLocal()
        mavenCentral()
        google()
        jcenter()
        maven { url 'https://oss.sonatype.org/content/repositories/public/' }
        maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.0'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "com.didiglobal.booster:booster-gradle-plugin:$booster_version"

        /* Include this module */
        classpath "com.didiglobal.booster:booster-task-compression-processed-res:$booster_version"
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

7-zip Compression

7-zipopen in new window works on the same principle as ZIP compression, but 7-zip uses the higher compression ratio LZMA and LZMA2 algorithms for better size reduction. It's recommended to use AndResguardopen in new window. Although 7-zip compression results are very significant, there can be some side effects - it may cause Google Play's optimization algorithm to fail.

Is It Really Necessary to Compress arsc or so?

From a technical perspective, Google officially does not recommend compressing resources.arsc and so, as this prevents them from being directly mmaped into memory. However, from a business perspective, if the APK size becomes a factor hindering user growth, and compressing resources.arsc and so has significant positive benefits for user growth, why not?