Android Decompile-Deepdive : Medium
Android Decompile-Deepdive is a series where we take a popular, successful, impressive application and decompile it, seeing what things we can learn in the process.
For this post of Android Decompile-Deepdive we’ll be looking at the great Medium app.
We’ll be deep-diving into the intent handling, an awesome intent builder implementation, and a pattern for defining intents for your activities.
We’ll also cover a method count tip, and a note on Optionals.
Below is a list of dependencies that this version of the Medium app relied on.
- Square Phrase
- Square Picasso
- Square Retrofit
- Bumptech Glide
- JakeWharton Timber
- JakeWharton ButterKnife
- JakeWharton DiskLRUCache
- Chrisjenx Calligraphy
- Flipboard BottomSheet
- Facebook SDK
- Twitter SDK
- Android Support and Google Play Services
It’s rather typical list, with heavy usage of the JakeWharton and Square offerings, as well as some of Google’s libraries.
It’s also worth noting Dagger being in this list. Dependency Injection pervades the entire application with modules and components for the application, activities, view presenters, data layer stores and caches, picasso/retrofit/glide instances, and way, way more.
It’s also interesting that as of this build, they are using Dagger 1 (maintained by Square) instead of the new and much improved Dagger 2 (maintained by Google).
Intent Factories and Building
But like many of the Android APIs, they aren’t exactly pleasant to work with. Intent can have extras, action, and data. And when we start up an activity (or other system component) we need to remember to set up the intent with all the extras, action, and data the subsequent component will expect.
The Medium app handles both of these annoyances with simple, clean solutions.
The Problem of creating and setting up intents is handled with their IntentBuilder class. This class follows the Builder pattern to make creating and setting up an intent incredibly simple and straight-forward.
IntentBuilder (or how to improve a mediocre API)
The IntentBuilder class has two static factory methods as entry points.
After initializing the builder, you can do a multitude of set-up operations.
Extras handling is done with a few named methods for Strings, JsonSerializables, and Serializables.
Different applications will have some different operations in the build method, but separating the construction process out like this gives you a great deal of flexibility, validation, dry-ness, and conciseness (not to mention, a more readable, idiot-proof API for creating Intents).
IntentBuilder Consumers in the Wild
Here are a few examples in the app which hook into the IntentBuilder.
This is an overall an excellent pattern. While Activities cannot have parameterized constructors (as their creation and lifecycle is handled by the OS) having a (or multiple named) intent factories makes it clear what that activity expects.
Even better, any data-type processing, null-checking, data-wrangling, or decision-making around an intent to launch this activity can be done at this level, in one place.
And, to top it all off, creating an proper intent to launch the activity with everything it needs is only one line, instead of the usual five or six.
This is a great little pattern and one that any multi-activity application should consider implementing. Especially if they have a complicated deep-linking URI schemes, or fully-decked-out Intents.
Let’s look at a few tips,tricks, kudos, or niceties.
Regarding Guava and the 64k method count
While the 64k method count isn’t necessarily an impassable-wall, multi-dexing isn’t something that most android developers don’t want to deal with.
The java community has embraced Google Guava, but it’s a massive library with a huge amount of methods. The Medium app only includes the bits of Guava that they use, vastly reducing the filesize and method impact of the library.
We can have optionals too!
Kotlin and Swift have Optionality built into their type-system, and embedded in their language mechanics which is great. However we shouldn’t forget, we can still use Optionals in Java, even if it isn’t built into the language itself.
The Medium app uses Guava’s Optional implementation to great effect. For fields that should rarely or never be null, they just use the type. But for data that is often null, they use Optionals.
A few examples:
There’s a lot of great things about the usages here (and the many more throughout the codebase):
- It’s semantic and self-documenting. By making a field/method return type Optional, it’s a way of documenting without comments littering accessors or methods. It says, “This thing is often null. It isn’t an exception or something wierd to be null, it’s perfectly expected for it not to be there.”
- It’s safe. Using optionals properly means you don’t have to worry about another developer (or yourself in 2 months) expecting a value to be there and blowing up.
- It’s not one of the android nullability annotations. Let’s be real, while @Nullable and @NonNull can have value, it’s not as semantic, documenting, or safe as using Optionals properly.
- It isn’t overused. Optionals are around, but not every class even has any. It doesn’t clutter the codebase, but gives clarity around real optionality.
There are some great things happening in the Medium code, and it was fun to go through it. While the DI is a bit excessive for my tastes (particularly with runtime generated DAG of Dagger 1) there are plenty of great takeaway’s from the code.
Edit: JakeWharton corrected me about the DAG generation at being generated at runtime. “Dagger 1 isn’t a runtime generated DAG. That’s Guice. The DAG is generated at compile time but linked at runtime.”
Their intent related code is fantastic, the “Why not both?” approach for Guava and sane method counts is great, and the use of Optionals makes their classes safer and easier to grok.
Next in the Android Decompile-Deepdive Series:
We check out the TED Android App.
Enjoy the snippets?
Disagree with my analysis?
Have burning-hot fiery opinions about some random sentence above?
I’d love to hear from you, leave your comments below!