DEV Community

borodutch.eth 💎🤲
borodutch.eth 💎🤲

Posted on Originally published at blog.plainwallet.app

How Plain Wallet's Android app went from 3.27 MB to 368 KB, and why it is 644 KB now

Plain Wallet's Android APK is 644 KB (643,801 bytes) in version 0.5.6, the current release on Forgejo and the file behind the download button on plainwallet.app. Ten days earlier, in 0.5.3, it was 3.27 MB. One Gradle change, turning on R8, took it to 368 KB without touching the app, and 23 languages brought it back up to 644 KB. I opened all three APKs and counted where every byte goes, because "R8 made it smaller" is the kind of sentence that deserves a receipt.

I measured the three release files on 2026-10-31 by downloading them from the releases page and reading the zip directories with Python's zipfile. Nothing here is estimated.

How big is the Plain Wallet Android APK, version by version?

Version Released APK size classes.dex
0.5.3 2026-10-18 3,265,052 B 2,961,372 B
0.5.4 2026-10-19 367,644 B 91,224 B
0.5.6 2026-10-28 643,801 B 91,380 B

The 0.5.3 APK is 3.27 MB and 91% of it is a single file, classes.dex, at 2,961,372 bytes. It is stored uncompressed, which is normal for an APK that targets Android 11 and up, so what you see in that column is what you download.

Why was a 727-line Android app 3 MB?

Plain Wallet on Android is a thin shell. The wallet itself is the browser extension's TypeScript, built for the app and bundled as assets; the Java side is one activity of 727 lines (MainActivity.java) that hosts the WebViews. The entire hand-written native code is under a thousand lines including the store-specific rating prompt.

So the 3 MB of bytecode was not ours. When I searched the 0.5.3 dex for package names, the biggest groups were Kotlin's standard library (about 660 type references under kotlin/jvm alone, plus collections, sequences, text and ranges) and androidx/core, with androidx/webkit at a couple of hundred. The app uses androidx.webkit to run a script at the start of every page and to learn each page's real origin. That one dependency pulls in AndroidX core and Kotlin's runtime behind it, and without a shrinker all of it ships, used or not.

What did R8 change?

R8 is Android's shrinker. It follows every call from the app's entry points and drops the classes nothing reaches. The commit that enabled it (#36, "Enable R8 for the Android release build; release 0.5.4") is eight added lines in android/app/build.gradle.kts:

release {
    isMinifyEnabled = true
    isShrinkResources = true
    proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
}
Enter fullscreen mode Exit fullscreen mode

isMinifyEnabled turns on R8's shrinking and optimization, isShrinkResources drops resources nothing references, and the default proguard-android-optimize.txt is Android's stock rule set. There are no custom keep rules in the repository: androidx.webkit ships its own consumer rules, so the library keeps exactly what it needs.

The result in 0.5.4:

  • classes.dex went from 2,961,372 to 91,224 bytes, 97% smaller.
  • The dex now mentions a handful of androidx/core, androidx/lifecycle and androidx/versionedparcelable types and one type of ours. The Kotlin standard library is gone from it.
  • resources.arsc shrank from 20,812 to 5,944 bytes.
  • The wallet's JavaScript, 240 KB compressed, did not change at all: main-B1QthM9Q.js and background.js are byte-for-byte the same sizes in both files.

The release notes for 0.5.4 said "No app changes", and the numbers agree: the same wallet in a quarter of the space the dex alone used to take.

Why did the APK grow again in 0.5.5?

Version 0.5.5 (2026-10-28) added a UI in 23 languages that follows the device language. The APK did not grow because of R8 or the dex, which sits at 91,380 bytes in 0.5.6. It grew because each language is its own JavaScript chunk, loaded only when that language is needed. In 0.5.6 there are 22 such chunks (English lives in the main bundle), together 234,329 bytes compressed, or about 10.7 KB per language. The wallet's assets went from 239,997 to 487,989 bytes compressed, resources.arsc grew by about 23 KB for the translated app name and strings, and the total landed at 643,801 bytes.

Splitting by language was a choice I would make again: someone who reads Ukrainian downloads Ukrainian once and never pays for Hindi at runtime, and the install itself stays under a megabyte. The honest part of the story is that the APK is 75% bigger than it was a week ago, and it is still 80% smaller than before R8.

What I'd copy from this

  • Check what your APK is made of before you optimize. Rename the file to .zip and look at the sizes. Ours was one file, and it was not code we wrote.
  • A WebView shell does not need a framework. One activity, one AndroidX library for the features WebView lacks, and R8 for the rest.
  • Turn on shrinking early. It was an eight-line diff that took off 88% of the file, and the only reason I can say that is that I kept the old APKs.
  • Put your size in the release notes. "About 360 KB" in the 0.5.4 notes is how I knew what to measure.

Plain Wallet is open source and has no analytics; if you want to check any number above, the repository is at github.com/backmeupplz/plainwallet, the three APKs are on its releases page, and the app itself is a very minimal EVM wallet you can try from plainwallet.app.

Measured on 2026-10-31 with releases v0.5.3, v0.5.4 and v0.5.6, which are the F-Droid-flavor APKs attached to each release. The Google Play build also includes Play's rating card and is delivered as a bundle, so Play's download size differs.

Top comments (0)