Who killed callbacks? the murder mystery resolution

In the previous blog entry we covered how the Android framework affects our callback blocks in asynchronous APIs, and why the need to remove, unwire, and kill them dead to avoid leaks.

To solve this problem we gathered all suspects and reviewed what their approach to how an API to wire and unwire callbacks would look like. Some were well known, others seemed familiar, and I promised two more approaches for this entry.

Let's solve this case.

Promises for a near Future - Reverend Green

Our reverend has always been a patient man. If you want something you have to pray for it and receive a promise that your request may be fulfilled in the future.

Futures, Promises or Tasks are constructs based off Inversion of Control on asynchrony. What does this mean? Rather than you providing a piece of code to be executed after a result, your API immediately gives back an object that represents the execution.

interface Database {  
    Promise<List<String>> getUsers();
}

There are several different approaches across languages and frameworks that provide this behavior. Let's review some of their properties.

The most important one is that a result will only be available once it's ready. Each different implementation of the promise provides different ways of waiting for this result. The most common one is having a method call that takes internally a callback object and hides threading from the user. The most common one in Android is AsyncTask. You may ask yourself why this isn't just rounding the circle back to the start. The benefit of this approach is that these promises already have their own callback implementation inside that you don't have to reimplement yourself. These implementations are also usually thread-safe too, which makes them great to just stick on your current codebase.

Some promises also provide the possibility of waiting for a result on the current thread, effectively blocking it until the result is seen. Never EVER do this on the main thread. This approach is useful when used in a background thread, and is mostly used by the promise library creators internally to bootstrap their own implementation of a nice asynchrony API on top of existing tools like java.util.concurrent.Future.

Another important property is that most of these promises can be cancelled. They will internally tear down any state or hard references they hold, and allow themselves to be cleared by the garbage collector.

Lastly, you have to take into account that some promises may not even be executed when the method is called, but when the first request for the operation result is subscribed.

In this example we use an AsyncTask to request a purchase's information.

interface NetworkRequester {  
    MyAsyncTask<PurchaseResultDto> startPurchase(String itemId);
}

class MyActivity extends Activity {  
    MyAsyncTask<PurchaseResultDto> purchase;

    @Override public onCreate(Bundle bundle) {
        purchase = NetworkManager.getNetworkRequester()
            .startPurchase("456-3345");
    }

    void onViewClick(View v){
        purchase.execute(
            result ->
                onSuccessResult();
            , failure ->
                onFailureResult();
            );
    }

    void onSuccessResult() { /* */ }

    void onFailureResult() { /* */ }

    @Override public onDestroy() {
        purchase.cancel();
    }
}

Promises suffer from the same problem other callback systems do: chaining results is a complicated process that requires following execution around the application. Having two, three, or four calls in a row may result in a pyramid of doom or jumpy execution.

In the example below we try to chain the previous purchase with two more requests: validation of the purchase, and refresh current list of bought items.

MyAsyncTask<PurchaseResultDto> purchase;

MyAsyncTask<Boolean> validate;

MyAsyncTask<List<Purchases>> refresh;

void onViewClick(View v){  
    purchase.execute(
        result -> {
            validate.execute(
                result -> {
                    refresh.execute(
                        result -> {
                            onSuccessResult()
                        }, 
                        failure -> {
                            onFailureResult();
                        });
                }, 
                failure -> {
                    onFailureResult();
                });
        }, 
        failure -> {
            onFailureResult();
        });
}

Academic approach - Professor Plum

Professor Plum didn't understand how promises were fulfilled and decided to experiment with them, twist and turn them around until understanding came to him.

From the original approach promises give around Inversion of Control, there came a new approach by using several powerful constructs: observable sequences, immutability and composition!

The first realisation is that every callback is a sequence of values over time. Some callbacks are called once, like a network request. Some others are set once and called an infinite number of times until disconnection, like the OnClickListener on a view. None of them can be assured that they'll be called immediately or in the future, or at all.

From the first realisation came the discovery that many other common code patterns can be expressed as a sequence if looked from the right angle. An Iterable, like List<String>, is a sequence of each of its values. A pulse that happens every second is another infinite sequence of values. Any promise like Future is a sequence of one value.

Thirdly, the realisation that any sequence can be casted to one, or multicasted to multiple receivers. This makes non-terminating infinite sequences into one single entry point through the whole app, effectively inverting its control indefinitely.

The most important realisation

Then came the most important realisation. State, understood as the values that are important to your program -user strings, the color of buttons, loading spinners showing, the contents of a RecyclerView-, are also a sequence of values over time. One value in one specific moment in time is always the same, but that same value can change over time.

Traditionally, those changes have been done by modifying the object in-place, what is called mutability. In the sequence approach, every element in the sequence is an immutable value and any change just creates a new version of the element in the sequence rather than mutating the existing one.

This means that, at any given point in time, your UI and threads know that the current object or collection of objects, is and will always be valid until the next element in the sequence is received. This is the base for a great stateless UI, also know as passive or dumb. This approach, and probably the reason why you're reading this blog, comes with many benefits in simplicity -because you only have to repaint yourself for the current state until a new one comes-, testability -sequences can be created on the spot-, and maintainability -a new possible value is just the code required to paint it-.

From this understanding comes one of the most shared images in recent presentations:

Wisdom of the Ancients

If any promise handles internally its callbacks, a sequence handles internally its promises. It's a well-oiled machine! But as any other useful construct, it has to come back and face its shortcomings: chaining multiple operations. Prof. Plum travelled back in time, to the books of many old erudite, to find a response. In there he found techniques that allowed sequence composition. While not necessary to continue reading this blog series, or accept the functional style of programming, if you are curious about the techniques Professor Plum found, here's a good introduction to Category Theory.

This sequence composition technique allows for linking multiple sequences in a parallel or sequential fashion, or operate on each of the values of the sequence with transforming operations. Each sequence of the chain decides how to modify or forward the input from the previous sequence, which makes outputs propagate to a single access for success and failure:

interface NetworkRequester {  
    Seq<PurchaseResultDto> startPurchase(String itemId);

    Seq<Boolean> validate(PurchaseResultDto purchase);

    Seq<List<Purchases>> refresh();
}

class MyActivity extends Activity {
    final CompositeSubscription subscriptions = 
        new CompositeSubscription();

    final NetworkRequester requester = 
        new NetworkRequesterImpl();

    @Override 
    public onCreate(Bundle bundle) {
        Subscription sub =
            clicks()
                .andThen(view -> requester.startPurchase())
                .andThen(result -> requester.validate(result))
                .andThen(success -> requester.refresh())
                .subscribe(
                    result ->
                        onSuccessResult();
                    , failure -> 
                        onFailureResult();
                    );
        subscriptions.add(sub);
    }

    @Override 
    public onDestroy() {
        subscriptions.clear();
    }
}

With this example, I'm glad to finally introduce RxJava's approach to sequences, called the Observable. It's a superset of the Observer pattern, with a touch of tools brought from functional programming, and the base to enable Functional Reactive Programming (FRP) on Android.

End of the game

So, what have we learned?

The murderer is Professor Plum, with Observables, in the library. It provides the best API ergonomics while fulfilling the requirements imposed by the Android Framework.

Now, what does this imply? Our UI can be stateless by means of immutable values. We can build mostly anything using the observable sequence abstraction, it's just a case of modelling the problem correctly in an FPR style.

There is only one pending point left. What is this Observable that RxJava provides, and how to use it to model sequences? Find out in the next entry titled This is Not an RxJava Tutorial.

That will be the last entry on the introduction to functional concept series. Following this series, I'll start a new series on modelling our user experience problems with stateless, immutable UIs, and how to test them.

The "Intro to FRP" Series
  1. Functional libraries for Java
  2. Advantages of using generic method references, Functions and Actions as parameters
  3. Transforming APIs based on inheritance to simpler, composable, ones by means of Functions
  4. Review of Android lifecycle issues with traditional callbacks
  5. The reactive approach to composable and cancellable callbacks with observable sequences
  6. The Observable in RxJava as a composable sequence for your existing code