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.


Dependency Analysis

Below is a list of dependencies that this version of the Medium app relied on.

General Java

Android Specific

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).


Deep-Dive

Intent Factories and Building

As an Android Developer, we use Intents all the time. We use them to start Activities, Broadcast Receivers, and Services.

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.

A nice bonus here: Notice the bounded wildcards for the class type parameter to ensure that any intents that get created are always to launch subclasses of their base service/activity class (something the default intent constructor does not enforce.)

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.

Using this intent builder pattern wrapping the extras, you could pass in any fundamental types you use in your app. Perhaps you have some Loggable interface that outputs a String, or an XMLSerializable type that can output a string you can serialize and deserialize into some xml object.
Action’s are optional in Intent’s and so the action field in the intent builder is Optional. While the normal android intent interface expects a String as the data, there is a limited and specific amount of actions supported by the os. Here instead of an unsafe String, they concretize the valid supported actions in an Enum to make intent action setting more typesafe and readable.
URI handling is done using the default android.net.URI builder. Obviously, by abstracting this out, there is a nice api for uri handling, and again, has only these safe methods to make sure the URI is valid and well-formed.
Finally we have the build, where everything comes together and we return an Intent to start. Notice in this particular implementation, they DRY out the domain and scheme for the URI building and even do some String case management for the component.

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.

One-line, foolproof, semantically named and context-parameterized Intent creation. Yes please!

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.


Extras

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.

-A subset of the entire Guava library — Only 7 packages out of the full 16.

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):

  1. 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.”
  2. 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.
  3. 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.
  4. 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.

Wrap-up

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.

We decompile the TED app. We focus on CLEAN architecture, and have a few fun extras around the NullObject pattern, and pre-populating your application database.

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!