Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google Play services Task API: continueWith vs continueWithTask

This is about Task.

What's the difference between task.continueWith() and task.continueWithTask(), can you provide an example for each one?

like image 214
Fabricio Avatar asked Oct 20 '16 17:10

Fabricio


2 Answers

The primary difference between continueWith and continueWithTask is one of the generic types of the Continuation you pass to it.

You can think of a Continuation as something that converts some input type to some output type. If you define a Continuation<IN, OUT>, where IN is the input type passed to its then method via a Task<IN>, and OUT is the type that method returns.

When calling continueWith, you pass a Continuation<IN, OUT>, and the then method is expected to compute and return the OUT value given a Task<IN> value as input. You might choose to do this if you don't have any blocking work to do for the conversion, such as reducing an integer array to the sum of its elements or counting the number of words in a String.

When calling continueWithTask, you pass a Continuation<IN, Task<OUT>>, and the then method is expected to return a Task<OUT> that eventually generates the OUT value, given the IN value as input. You might choose this if you are able to delegate the conversion work to an existing reusable Task.

Practically speaking, you aren't required to choose one or the other to do your work. It's a matter of preferred style, or if you have a nice Task ready to delegate your conversation rather than a Continuation. Typically you only use a Continuations if you have a pipeline of conversions to chain together.

The javadoc links here show some examples of Continuations. Also, to learn more, you can read about them in part three of my blog series. To be fair, continueWithTask is the only part of the Task API I don't directly discuss anywhere in that series, mostly because conceptually it doesn't differ very much from continueWith.

like image 101
Doug Stevenson Avatar answered Sep 17 '22 19:09

Doug Stevenson


I'd like to add, that continueWith and continueWithTask really got me into trouble, obviously because I did not truly understand the API, but also did the naming confuse me. Maybe an example of my failure can prevent others from doing the same.

tl;dr

When to use which method:

  1. Use continueWith if you want to use the result of the previous task and return a new result within your Continuation's then method. And you need to hand it over to some other continuation or use it afterwards in listeners. The return value of continueWith is a Task that just WRAPS your return value of the then method.

  2. Use continueWithTask if you want to use the result of the previous task, and somewhat use it in a new task, that you create within your Continuation's then method. The return value of continueWithTask is a task that YOU create inside of then and has an own generic result.


Do NOT return a task in your continuation's then and use it with continueWith. It might compile and execute for years without a warning, but also without doing it's job.

IF you directly append a listener after continueWith, this listener will give you a task THAT ONLY WRAPS the result of your then return value. If this return value is a task itself, do not expect it to be executed(!!!).


Long story!

I had a calling chain like this:

private Task<SomeResult> getTask() {

    PreloadingTask.create().continueWith(additionalTask()).addOnCompleteListener(task -> {
        if (task.isSuccessful()) {
            source.setResult(someResult);
        } else {
            source.setException(new Exception());
        }
    });
    return source.getTask();
}

So as you can see, additionalTask() must return some sort of Continuation<IN, OUT>, that implements the method then as

@Override 
public OUT then(Task<IN> task){ ... }};

In my case I did not need to check for OUT, because I just wanted to do some additional computations AFTER the PreloadingTask was done and forwards it's result to my additionalTask() continuation.

I wanted to execute the task from additionalTask(), and afterwards the onCompleteListener should have been called.

private Continuation<PreviousResult, Task<Void>> additionalTask() {
        return task -> {
            PreviousResult r = task.getResult();
            simpleResultModification(r);
            return new AdditionalTask(r);
            );
        };
    }

What happend? The onCompleteListener was called directly, because my then method got executed and returned it's result, which was an instance of AdditionalTask. This AdditionalTask then got wrapped into another task and handed over for the onCompleteListener as the result.

And my AdditionalTask got never executed.

That's why you should use continueWithTask if you return a task, within then.

like image 20
JacksOnF1re Avatar answered Sep 21 '22 19:09

JacksOnF1re