DEV Community

Cover image for I Opened the APK to Find the 0.46 MiB PackedAssets Could Not Explain
HanoStudio
HanoStudio

Posted on

I Opened the APK to Find the 0.46 MiB PackedAssets Could Not Explain

Build Analyzer series, article 3.

In article 1, DOTween increased the APK by 0.46 MiB, but Unity's packed-asset report attributed only 192 bytes to the DOTween DLL path.

In article 2, I wrote down the rule that came out of that: APK size, BuildReport.summary.totalSize, and packed assets are three different numbers.

This time I opened the APK itself.

The question was small, but it bothered me:

If BuildReport.packedAssets only showed 192 bytes for DOTween, where did the 0.46 MiB APK increase actually go?

The answer was not in a Unity asset table.

It was inside the APK.

This sounds harder than it is. I did not decompile anything or read native code. An APK is a ZIP file, so I opened the two builds like ZIP archives and compared the size of each file inside. That is the whole technique.

The two builds I compared

I compared the last two steps from the controlled experiment:

Step Build APK bytes APK MiB
6 Font subset, no DOTween 17,441,073 16.6331
7 Same build + DOTween runtime reference 17,928,357 17.0978
Delta 487,284 0.4647

Both APKs came from the same Unity project and the same fixed Android settings:

  • Unity 6000.3.7f1
  • Android APK
  • IL2CPP
  • ARM64 only
  • Development Build off
  • Managed Stripping Level: Medium

The generated APKs are under Builds/Experiment/ after running the public experiment project. They are not committed to the repository, but the project can regenerate them.

The quick way to compare APK entries

An APK is a ZIP file, so I compared ZIP entries by path.

This is the shape of the script I used:

from zipfile import ZipFile

def entries(path):
    with ZipFile(path) as apk:
        return {
            item.filename: (item.compress_size, item.file_size)
            for item in apk.infolist()
        }

before = entries("step-06-font-subset.apk")
after = entries("step-07-dotween-runtime.apk")

for name in sorted(set(before) | set(after)):
    before_compressed, before_raw = before.get(name, (0, 0))
    after_compressed, after_raw = after.get(name, (0, 0))
    delta_compressed = after_compressed - before_compressed
    delta_raw = after_raw - before_raw

    if delta_compressed or delta_raw:
        print(delta_compressed, delta_raw, name)
Enter fullscreen mode Exit fullscreen mode

The CSV I kept from this comparison is here:

https://github.com/hellohanostudio/BuildAnalyzer/blob/main/Results/apk-entry-diff-step-06-to-07.csv

Where the 0.46 MiB went

The goal here was not to fully reverse-engineer the cause. I only wanted to see which files inside the APK received the growth.

Almost all of the compressed APK growth was in three files:

APK entry Compressed Uncompressed
libil2cpp.so +380,447 bytes +1,392,400 bytes
global-metadata.dat +64,350 bytes +188,984 bytes
libunity.so +42,344 bytes +92,416 bytes

Full APK paths are in the CSV linked above.

The final APK grew by 487,284 bytes. Those three entries alone account for 487,141 compressed bytes.

That is basically the whole change.

I can explain the libil2cpp.so and metadata growth from the new managed code path. I do not yet know the exact reason libunity.so moved by 42 KB, so I am treating that row as an observed artifact-level delta rather than a conclusion.

The packed-asset row for the DOTween DLL was still real:

9,192,0.000183,"Assets/Plugins/DOTween/DOTween.dll"
Enter fullscreen mode Exit fullscreen mode

It just was not the whole story.

What this changed for me

Before this, I was thinking in two layers:

  1. Final APK file size
  2. Unity packed asset data

After this comparison, I need at least three:

  1. Final artifact size
  2. Packed asset contribution
  3. Files inside the artifact

Packed assets explained the texture, audio, and font steps very well.

They did not explain the DOTween step, because DOTween changed generated code and metadata. The growth appeared mostly in libil2cpp.so and global-metadata.dat.

That does not make BuildReport.packedAssets useless. It just tells me what kind of question it can answer.

The important distinction

For an asset-format question, BuildReport.packedAssets is the right lens:

  • Did ASTC reduce the texture payload?
  • Did Vorbis reduce the audio payload?
  • Did a font subset remove font data?

For a code-growth question, the APK itself is the better lens:

  • Did a plugin increase libil2cpp.so?
  • Did metadata grow?
  • Which packaged file actually changed?

That is the lesson I would carry into a real build analyzer: do not collapse these into one number.

Show the artifact delta first. Then explain it with the right layer.

Sources

Top comments (0)