A smaller, sleeker app using the APK Analyzer

The Genius app started out just over 1 year ago with File > New Project.  Somewhere along the way, our baby APK ballooned into an almost unrecognizable 22MB.  But we were too busy adding new features, and no one event spurred us into action … until we encountered that gift that keeps on giving, the dex method limit

Careful configuration of Proguard got us back under the dex limit, and it made our app smaller, too.  But not significantly smaller.  A less lazy engineer might have dug into the problem, but instead I just wondered why as I got on the plane to Google I/O ... where the Android Studio team announced a tool called the APK Analyzer.

A week later I landed in NYC, downloaded the Android Studio 2.2 preview, and tried the analyzer out.  (You can find it in Build > Analyze APK).  Here’s what we saw:

The Android Studio APK Analyzer with our previous production release.

The Android Studio APK Analyzer with our previous production release.

Most of our app's size was third party libraries—even after Proguard!  Let's look closer ...

70% of our app’s size was native code from a single 3rd party library*.  We had done all the “right things” -- made sure we didn’t ship unused resources, used vectors over PNGs in almost every case, even preferred StringDefs over enums -- and none of them made an effective difference.  We were doing a great job of optimizing the wrong thing. 


Why was the library so huge?  

The answer is that different Android devices have different CPU architectures.  When native libraries are built for Android, they’re compiled as separate versions for each architecture.  So the library giving us trouble was built as a “fat binary” with multiple architectures—e.g. mips, ARM, and x86 -- all packaged together.  

By default, gradle builds a single APK, no matter what libraries you include.  This means your APK contains the full library, which itself contains multiple copies of the same native code (just optimized for different architectures).

This single APK is uploaded to the Play Store, and downloaded to your device.  At that point, the package manager on your phone is finally smart enough to install only the code for its particular architecture—but that’s no relief to your cellular data plan.  To make it worse, users who are short on storage only see the original download size of your APK and may well decide not to bother.


Together, not the same

After a pointer from Romain Guy, we followed the steps in this blog post from Realm to reduce the size of our app's native code. (We've also spoken to the Third-Party-Who-Must-Not-Be-Named, but the fundamental problem here isn't their fault.) We streamlined our code to reference as little of the library as possible, deleted all the native files we could, and then used apk splits to generate one APK per architecture. 

splits {
    abi {
        enable true
        include 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'mips'
        universalApk false

The difference was staggering. For some architectures, our current release (1.11) was as little as 4.3MB.  That’s a huge difference if you are updating over the cellular network.

Why doesn’t everyone do this?!  

Using multiple APKs adds complexity to your updates, and it can affect app restore.  Uploading multiple APKs to the Play Store means you need separate version numbers for each.  If you upgrade to a new phone with a different architecture, app restore won’t work because the version number of Genius installed before no longer matches the device. (For security reasons, the Play Store will only update you to the exact build you had before.)*

The official Android documentation recommends that you only consider multiple APKs once your app reaches 100MB or larger. But to us (and our non-U.S. users, where unlimited cellular data plans are rare) that seems extremely high. Users on the go might wait to download our app until they have WiFi, by which point they've forgotten they wanted the lyrics to Panda.

There's also little risk involved—we can always re-publish a single APK if we change our minds. One of the great things about being a small team is being able to experiment.

For us, we decided the benefits outweighed the drawbacks.  We hope our users enjoy their new, smaller downloads and look forward to future updates!

— Lisa Wray (@lisa), Android engineer at Genius


* The other 8% was Realm, but well worth it ... more on that later!  

** Thanks for a tip from Xavier Ducrohet. To be clearer, I updated the sample code to match our actual build.gradle file (and the architectures in our screenshot). :)

Posted on June 9, 2016 .

Seven Habits of Highly Effective Gems

These days, writing a Ruby gem is incredibly easy, but writing a good one isn’t. I recently flew down to San Antonio to give a talk at RubyConf on specific ways that gem authors can make their users and contributors happy. Happy users means more traction for your library, which means more bug reports, more contributions, and, most importantly, more fame for you as the library author.

The habits cover a range of concerns, from documentation (make sure your Quick Start is incredibly short, and your README is incredibly long) to code design (use a layered architecture to enable your users to use your software in ways you didn’t expect) to ease of contribution (use Bundler and Vagrant to give your contributors a turnkey test environment).

After the talk, Austin Ziegler, maintainer of the mime-types and diff-lcs gems, told me that it was a good talk and I only got two things wrong. This number was later revised to three.

Enjoy the whole talk here:

Posted on November 25, 2015 .

Genius on Android: Typography and material design

The last few months have been an exciting rush of designing and building our recently released Android app—a brand new app, built from the ground up with the principles of material design. For our initial version, we designed a simple, clean UI with Genius’s bold brand colors.

That’s because the real showcase was our content—2 million song lyrics and millions more annotations from users and artists, full of rich content like quotes, in-line annotations, links, embedded videos, images, and (of course) GIFs.  We knew this content needed to render quickly and seamlessly now, and in the future, no matter how it was displayed—in a separate pane, in a complex tablet layout, or interposed with other UI elements.

To accomplish this, we used a custom TextView and custom Spans, an object that stores appearance information about a String.  Our video thumbnails, images, and GIFs are displayed by slicing this rich text and inserting it with ImageViews into a list.

I spoke at Droidcon NYC on August 28, 2015 about achieving the effects of professional print typography in Android’s native TextView, from essentials such as line spacing and custom typefaces to fully custom effects, using real-life examples from the code base of the Genius Android app.

You can watch the video now, and if you’re attending Droidcon London, you can catch an expanded version of this talk, or chat with me in person, October 29-30. 

And a reminder: if you too are excited about thoughtfully designed apps for people who love music, Genius is hiring mobile engineers for both Android and iOS

— Lisa Wray (@lisa), Android engineer at Genius

Posted on October 1, 2015 .