devto 2026-07-02 원문 보기 ↗
Build Analyzer series, article 2.
In the previous article, I built 16 Android APKs while changing one variable at a time. After that, I had the APK numbers. What I did not have yet was a good explanation for where those bytes came from.
When I finished the build-size experiment, I almost published the wrong number.
Unity handed me three different "build sizes," and for a moment I was not sure which one represented the file users actually download.
That confusion is why I wrote a small BuildReport parser. I already had the final APK deltas, but I wanted evidence from inside Unity's build output too.
It helped, but not in the clean way I expected.
My first instinct was to look for "the build size" in Unity's BuildReport.
That turned out to be the wrong mental model.
In the final measured Android build, I had three different size numbers:
| Metric | Value |
|---|---|
| Final APK file size | 17.10 MiB |
BuildReport.summary.totalSize |
143.54 MiB |
Sum of BuildReport.packedAssets entries |
5.60 MiB |
At first glance that looks suspicious. How can the same build be 17 MiB, 143 MiB, and 5.6 MiB?
The answer is that they are not measuring the same thing.
BuildReport.summary.totalSize: Unity's reported build output sizeOnce I separated those three, the rest of the experiment became much easier to reason about.
I did not start by building a polished UI. I only wanted a repeatable text report after every build.
Unity's IPostprocessBuildWithReport was enough for that.
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
internal sealed class BuildAssetSizeReporter : IPostprocessBuildWithReport
{
public int callbackOrder => 1000;
public void OnPostprocessBuild(BuildReport report)
{
var rankedAssets = CollectAssetSizes(report);
var packedAssetBytes = rankedAssets.Aggregate(
0UL,
(total, asset) => total + asset.Value);
var artifactBytes = GetArtifactBytes(report.summary.outputPath);
LogSummary(report, rankedAssets, packedAssetBytes, artifactBytes);
WriteReports(report, rankedAssets, packedAssetBytes, artifactBytes);
}
}
The full source is here:
The important part is not the hook itself. It is what I decided to record:
BuildReport.summary.totalSizeI wanted the report to make it hard for me to mix those up later.
BuildReport.packedAssets contains the packed files generated during the build. Each packed file contains PackedAssetInfo entries.
The fields I used were:
sourceAssetPathsourceAssetGUIDpackedSizeOne thing I had to handle: the same source asset can show up more than once. Unity is reporting packed entries, not a neat "one source file, one row" table.
So I grouped by source path before sorting.
private static KeyValuePair<string, ulong>[] CollectAssetSizes(BuildReport report)
{
var sizesByPath = new Dictionary<string, ulong>(StringComparer.Ordinal);
foreach (var packedFile in report.packedAssets)
{
foreach (var asset in packedFile.contents)
{
var path = string.IsNullOrEmpty(asset.sourceAssetPath)
? $"<generated:{asset.sourceAssetGUID}>"
: asset.sourceAssetPath;
sizesByPath.TryGetValue(path, out var currentSize);
sizesByPath[path] = currentSize + asset.packedSize;
}
}
return sizesByPath
.OrderByDescending(pair => pair.Value)
.ThenBy(pair => pair.Key, StringComparer.Ordinal)
.ToArray();
}
This was enough to check the asset-shaped parts of the experiment:
For those questions, the parser was useful right away.
I did not use BuildReport.summary.totalSize as the APK size.
For the artifact number, I read the file on disk:
private static ulong? GetArtifactBytes(string outputPath)
{
return File.Exists(outputPath)
? (ulong)new FileInfo(outputPath).Length
: null;
}
That produced a plain summary like this:
artifact_bytes=17928357
artifact_mib=17.0978
build_report_total_bytes=150514033
build_report_total_mib=143.5414
packed_asset_bytes=5873204
packed_asset_mib=5.6011
This is not fancy, but that is the point. I wanted boring measurement code because boring code is harder to argue with.
The parser also writes a ranked CSV. This is the top of Results/raw/step-07-dotween-runtime-assets.csv from the final experiment step:
rank,packed_bytes,packed_mib,source_asset_path
1,2796376,2.666832,"Built-in Texture2D: Splash Screen Unity Logo"
2,1871556,1.784855,"Assets/Experiment/Generated/ExperimentTexture.png"
3,656420,0.626011,"Resources/unity_builtin_extra"
4,477252,0.455143,"Assets/Experiment/Generated/ExperimentAudio.wav"
5,69510,0.066290,"Assets/Experiment/Generated/NotoSansKR-Regular.ttf"
The biggest packed texture was not even my generated test texture. It was Unity's splash-screen logo.
That was a useful reminder. A ranked build report is not a list of "my files only". It includes built-in assets Unity packs into the player too.
The raw reports are here:
https://github.com/hellohanostudio/BuildAnalyzer/tree/main/Results/raw
The parser explained textures, audio, and fonts pretty well.
Then I added DOTween.
DOTween Free increased the APK by 0.46 MiB. But the packed-asset CSV attributed only 192 bytes to the DOTween DLL path.
That was the moment the shape of the problem changed for me.
The parser was not "wrong". It was just looking through one narrow window. DOTween affected generated code and metadata, so most of the growth appeared in files such as libil2cpp.so and global-metadata.dat, not in BuildReport.packedAssets.
That was probably the most useful result of the whole parser exercise: it showed me exactly where the parser stopped being enough.
After this experiment, I would use this kind of parser for asset questions:
I would not use it alone for code-size questions:
For that, I need to inspect the final artifact too.
The main thing I kept from this week is simple:
Do not say "build size" as if it is one number.
For every build, I now want these three values side by side:
BuildReport.summary.totalSizeBuildReport.packedAssets[].contents[].packedSize
Then I use the packed-asset ranking as evidence, not as the final truth.
That small separation prevented the biggest mistake I could have made in the experiment: confusing what Unity packed as assets with what users actually download.
BuildReport.packedAssets
PackedAssetInfo
BuildSummary.totalSize