onSuccessTask vs addOnSuccessListener? - android

I'm using Google Firestore for by Android database and want to know the different between onSuccessTask and addOnSuccessListener.
For example, here is me updating a Firestore document:
val doc = db.collection("books").document(book).update(data)
For the above, to take action when the update completes, I can do either:
.onSuccessTask { } or .addOnSuccessListener { }
which to me, yields the exact same result.
Can someone clear up what the difference is and which one should be used?

There are three flavours of Task's addOnSuccessListener() method which are:
addOnSuccessListener(Executor executor, OnSuccessListener listener):
Adds a listener that is called if the Task completes successfully.
addOnSuccessListener(OnSuccessListener listener):
Adds a listener that is called if the Task completes successfully.
addOnSuccessListener(Activity activity, OnSuccessListener listener):
Adds an Activity-scoped listener that is called if the Task completes successfully.
And two flavours of Task's onSuccessTask() method whic are:
onSuccessTask(Executor executor, SuccessContinuation successContinuation):
Returns a new Task that will be completed with the result of applying the specified SuccessContinuation to this Task when this Task completes successfully.
onSuccessTask(SuccessContinuation successContinuation):
Returns a new Task that will be completed with the result of applying the specified SuccessContinuation to this Task when this Task completes successfully.
As you can probably see, the main difference is that, in case of addOnSuccessListener() the object that is returned is of type abstract Task<TResult>, so everytime you use it, you'll need to provide an implementation for that while when using onSuccessTask() method, the type of object that is returned is <TContinuationResult> Task<TContinuationResult> (which is not abstract).

Basically, you would use addOnSuccessListener when you just want to work with the result, while onSuccessTask can be used with other methods such as continueWith or continueWithTask in order to chain tasks.
Here's an article about chaining tasks.

Related

How many times onUpdate function will trigger on batch update?

I want to update two fields in a document in a collection called recipes and i used the batch update to do it:
let recipeRef = db.collection('recipes').doc(`${recipeId}`);
let batch = db.batch();
batch.update(recipeRef, {ratingsCount : admin.firestore.FieldValue.increment(1)});
batch.update(recipeRef, {totalRating : admin.firestore.FieldValue.increment(snap.data().rating)})
return batch.commit();
And I have a trigger function on the recipes collection like this:
exports.RecipeUpdated = functions.firestore
.document('recipes/{recipeId}')
.onUpdate((change, context) =>
{
//code
});
My question is as there are two updates in the above batch, will this also trigger the onUpdate function twice Or since the batch writes completes atomically, trigger will be called only ones? I want the trigger to be called only one time.
As #DougStevenson mentioned in his comment, you should just simply run the code and see the behaviour. But note, even if you are using the same recipeRef, you are creating two difference updates in your batch. In this case, the result that you'll get will be that onUpdate() will fire twice.
If you want to be called only once, then you should not create two different updates, you can create a single one. update() function allows you to pass multiple properties that can be updated. If you are using objects, than simply get a reference to the document, get it, make the changes and write it back.

How to get WorkManager Status Synchronously

I am working with WorkManager Alpha 05.
I'm developing a Service that enqueues task on demand of other applications.
It has two methods:
createTask (Create a new task, given a name and a set of data, it returns and ID)
checkTaskStatus (The application asks the services given a ID, the status of the task)
The communication is done via bound services using messages. That means both client and services has the correct implementations to communicate information.
Method 1 is working fine.
I have problems with method 2.
WorkManager.getInstance().getStatusById(taskID)
.observe(LifecycleOwner, Observer {
status -> if (status !=null){
val myResult = status.state.toString()
statusString = myResult
Log.d("Task Status",myResult)
}
})
The observer is logging the status correctly, but I can't send back that message to the client. Is there a way to check the status in a sync way?
I don't really need to have the task attached to a LiveData.
Seems like SynchronousWorkManager was removed on October 11:
Removed WorkManager.synchronous() and WorkContinuation.synchronous() and all related methods. Added ListenableFuture as the return type of many methods in the API. This is a breaking API change.
How to use ListenableFuture:
You can now synchronously get and observe by using ListenableFutures. For example, WorkManager.enqueue() used to return void; it now returns a ListenableFuture. You can call ListenableFuture.addListener(Runnable, Executor) or ListenableFuture.get() to run code once the operation is complete.
More info can be found here.
The WorkManager instance has a synchronous method which returns the SynchronousWorkManager, This will give you a set of methods to perform synchronous operations. Take into account that this is meant to be used in a background thread.

Pass parameter to gradle task

I have multiple tasks. I run a task from command line via : gradle tasks.gradle TaskA -Pparam1=value1 -Pparam2=value2
TaskA calls TaskB via : tasks.TaskB.execute().
How can I pass parameters to this task programatically?
As in this comment, please do not call execute on task directly. Not only it may spoil the task dependency graph but this is also not the case that task action is always mapped to execute method.
To read the properties in the task, use:
project.findProperty("<PROPERTY_NAME>")
It returns null if property is missing or value if it was passed.

How to create custom tasks for Firebase using the Google Play services Task API

I'd like to create custom tasks like these ones in firebase in order to chain my API async calls. How can I achieve that?
There are a few ways to create a custom task using the Play services Task API.
First, there is Tasks.call(Callable). The Callable you pass is scheduled for immediate execution on the main thread, and you get a Task in return, with a generic parameter of the return type of the Callable. This Task resolves successfully with that return value, or an error if the Callable throws an exception.
The other method is Tasks.call(Executor, Callable), which is exactly like the other method, except the given callable is scheduled for immediate execution on a thread managed by the given Executor. It's up to you to find or create an Executor that's appropriate for your work.
Lastly, there is also TaskCompletionSource, which lets you create a Task and manually resolve it to success or failure from the result of some other item of work not directly related to a Task.
For more details, check out my blog series on Tasks. I cover these methods in part three and part four.
Suppose you have a Document class, you could do as follow:
Create successfuly resolved task
Tasks.<Document>forResult(document);
Create a failed task
Tasks.forException(new RuntimeException("Cool message"));
Create from Callable
interface CreateDocument extends Callable<Document> {
#Override
Document call();
}
Tasks.call(new CreateDocument());
Create using task completion source
Task<Document> createDocument() {
TaskCompletionSource<Document> tcs = new TaskCompletionSource();
if (this.someThingGoesWrong()) {
tcs.setException(new RuntimeException("Cooler message"));
} else {
tcs.setResult(Document.factory());
}
tcs.getTask();
}

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?
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.
Just to add to what Doug said, I would put it like this:
continueWith will wrap the result of the then method in a Task. continueWithTask will not; it expects the then method to return a Task, and thus avoids the double wrapping of a task within a task.
continueWithTask is perfect when you want to use a Continuation and a TaskCompletionSource together.
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:
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.
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.
There's a big difference between continueWith() and continueWithTask() related to task cancellation.
More info here - Task API docs.
continueWith() : If the previous Task is canceled, the returned Task will also be canceled and the Continuation would not execute.
continueWithTask() : If the previous Task is canceled, the Continuation would still execute and task.isCanceled() is true can be observed in the Continuation.

Categories

Resources