Starting Activities with Kotlin — My Journey

P.S. Android Intent/Bundle Extras in Kotlin

This is a (very late) response to “Starting Activities with Kotlin — My Journey” from passsy, which in turn is a response to my original article “You won’t believe this one weird trick to handle Android Intent extras with Kotlin”. I strongly suggest to read both before proceeding, if you haven’t already, or you might not be able to follow along.

It looks like I wasn’t clear on some of the motivations for both the premise of the article and the usefulness of the associated library (apologies!), so I finally took the time to properly address both.

At least this time there’s no click-bait title!

The first point is about starting activities through static getters/setters that configure the relative Intent, which I tried (and perhaps failed) to carefully phrase as “one way that is sometimes necessary”. Let me elaborate.

When you have simple activities that only do one thing and that need a fixed set of specific parameters, the way I usually handle it is also by having a static function to set all of them internally. During onCreate, either all the data can be extracted from the Intent and it’s valid, or it will crash/finish depending on the use case. Here exposing a static constructor for the Activity is simple and works well enough. Nothing exciting.

When dealing with activities that have multiple entry points, or that can dynamically show different views based on how they’re configured externally, it gets more complicated. This can easily be the case for single-activity applications. When it happens, using the static getters/setters to configure the Intent separately is one possible pattern to handle the problem without too much boilerplate.

That’s why I first discovered and started using this specific way of handling those cases, which inspired me to formalise a better way to handle the extras. This pattern happened to be the perfect example to show the “evolution” from Java to Kotlin, but it was always meant to be nothing more than an that: an example!

Now let’s talk about Android Extras Delegates: the library that I released some time ago and that makes handling Intent/Bundle extras reusable, composable and type safe.

It’s important to understand that the library is useful every time you need to handle extras, not just in activities. As an example, to illustrate how it can be used regardless of what techniques are employed to start an Activity I’ll refactor some examples from passsy’s article, briefly explain what’s going on.

The first step is to extract the getter/setter logic into an (optionally reusable) “intent extra delegate”. It can be as simple as:

If this looks weird, make sure you’ve read the original article

This snippet declares a function that returns a property delegate, which in turn can be used to get/set a String? on an Intent. Note that the type is nullable, although from the examples the opposite is always expected, so why don’t we add that logic here?

`error` is an real function from the Kotlin standard library

By providing an explicit pair of reader/writer we can control how the raw string is transformed into something else, in this case a not nullable one, because if the string is null we throw an error! This trick is enough to gain type safety: now it’s impossible to assign a null string, and trying to read a null string will result in a crash.

Note also that the name of the intent extra will “magically” be based on the name of the variable this delegate will be bound to. This is great when you don’t care what that is (as long as it’s consistent), but if you need the name to be something specific you can simply do the following:

See the library documentation for a more in-depth explanation and interesting advanced usages.

Now we can start using this in the first example provided, with the following result:

Our intent extra delegate is created by invoking IntentExtra.UserId() and bound to an extension over Intent called userId. Because that is declared inside the companion object, it can be used both there and inside the instance of the Activity.

Notice how this is less code than before, while being “self-documenting” at the same time, and how we can potentially re-use the same extracted logic every time it’s needed. In addition, the implementation details of the persistence of our data in the Intent are encapsulated in a composable, type-safe container, and all of that is still hidden inside the Activity.

Let’s jump to the very last example and refactor that one (the examples in-between are similar):

(`OtherActivity` remains unchanged)

Here the intent extra delegate has been moved outside of the Activity, while still remaining private, so it can also be used by the public Context extension.

In conclusion, despite these being a very simple examples, the advantages of accessing Intent extras in a type-safe manner are already visible. Another benefit is that every time we need to change the type of an extra we can simply change the delegate, and the code won’t compile until all usages are properly fixed. We’ve been using this in production for some time and it has saved us from some very frustrating bugs!

If you want to know more, you can find the library here and the article about it here.