Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Shrinking a Kotlin Binary by 99.2% (jakewharton.com)
100 points by farmerbb on Aug 26, 2020 | hide | past | favorite | 29 comments


If you're going to get rid of all your Kotlin std lib usages by explicitly replacing them, after inspecting the actual class files generated by the compiler, with standard Java methods (and hope that no future change will re-introduce them) and you care so much about the size of your jars, I would say you're better off simply going back to Java, perhaps using the very latest version (since you're willing to use Kotlin that shouldn't be a problem) which has lots of niceties and to me, at least, looks a lot better than using Kotlin with these kinds of hacks. Add a nullability static analysis check and you're gold.


The takeaway is "make sure you use a tool like R8 or ProGuard to remove unused code paths". That's where most of the savings came from, and the advice applies to Java projects as well.

But true, if you care about every last byte, you might use Java instead. You might also use another language entirely.


Haven't watched it yet, but looks like the author gave a talk recently about differences between Kotlin and the latest Java: https://jakewharton.com/whats-new-in-java-19-the-end-of-kotl...


The final section of the post was a Java port and size comparison. It also noted that while it was smaller, I simply don't want to use Java and the minute any external dependency is added (such as for CLI parsing) I'm right back to needing R8 for minifying.

I removed that part since the post was long enough.


At that point, why not use Rust or Go or D or whatever with significantly smaller binary sizes and (un-optimized) faster speeds.


The goal here was to get the binary size to be small enough, not the smallest it could possibly be. The tool was written in 2 hours because I used a language I was proficient in. While I love me some Rust it would easily double if not triple the time it would take me to build the thing in the first place


If that's the real Jake Wharton, I have to say I am a big fan of the tools/libs (and their quality) you wrote for Android. They were especially useful 7-5 years ago, when the "official" libs were pathetically crappy :).


Not on Android


On Android you are stuck with Java8 so GP's point melts away.


C or C++ then


Rust has first class support for Android.


Cool, so I can start a new service and bind activities to it from Rust without having to deal with a nasty JNI or an unstable ABI that changes under my feet?


That's hardly the full story. Rust provides all the support it is able to provide for Android, yes. But it still relies on the NDK and has to sit behind a JNI layer. A "Kotlin vs Rust" comparison for Android is apples and oranges.


What do you mean by "first class support" here? Some quick searches doesn't give me results that goes in this sense (nothing official from the rust and/or android projects).


I love D but it's ecosystem just isn't there. If you need the jvm ecosystem you're probably better off with Graal.


Those compile to native.


Disagree. A lot of the value of Kotlin is stuff that doesn't even make it into the binary, like scope functions.


Doesn't it make more sense to look at this work as proof-of-concept for what the Kotlin compiler could do automatically?


If you have used Kotlin you would have known that no sane person wants to go back to Java after being exposed to Kotlin.


What a silly way to look at things.

If it matters, I've been programming more in Kotlin than in Java nowadays, and I have no trouble at all going back to Java... it's a nice language for its age and it gets the job done robustly. I even prefer Java for small applications that I want to distribute to users as small jars, for example. Kotlin has some nice benefits, but it's not a panacea that makes Java irrelevant - it actually strengthens the Java ecosystem in my opinion - it's another tool for the Java developer to use when appropriate.


> - val old = args[0].let(::File).readText() > - val new = args[1].let(::File).readText() > + val old = args[0].let(Paths::get).let(Paths::readString) > + val new = args[0].let(Paths::get).let(Paths::readString)

Missed index. 0!=1.


That’s an impressive catch. I would never have noticed this. How did you get this good at code review?


It helps that the correct code is just above the buggy replacement…


Reading lots of code. And as the other commenter says, it's a bit easier when the correct code is right above it.



It used to help a lot (30-40%) to sort the class files of a librarly on canonical name before you add them to its jar. Class files contain that name, and are (typically) small. Your compression factor will grow. (maybe that trick is standard now)


Removing `-dontobfuscate` will change all the package, type, and function names to be single letters for the same result of greater compression.

The goal here was to just get it small enough, not the smallest it could possibly be.


any idea how to get the swift upload network traffic down?

My 500K objc ipa ported to swift http://mro.name/ShaarliOS now is ~8MB (OTA test ipa), ~28MB for production upload causing ~300MB traffic until finally uploaded via xcrun, taking 1h on my rural uplink.

Has anybody an idea how to get back to sane dimensions?


Looks like most of the size reduction was from running R8. The rest seems meaningless.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: