Google Play services Task API: continueWith vs continueWithTask - android

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.

Related

onSuccessTask vs addOnSuccessListener?

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.

Difference of setValue() & postValue() in MutableLiveData

There are two ways that make change value of MutableLiveData. But what is difference between setValue() & postValue() in MutableLiveData.
I could not find documentation for same.
Here is class MutableLiveData of Android.
package android.arch.lifecycle;
/**
* {#link LiveData} which publicly exposes {#link #setValue(T)} and {#link #postValue(T)} method.
*
* #param <T> The type of data hold by this instance
*/
#SuppressWarnings("WeakerAccess")
public class MutableLiveData<T> extends LiveData<T> {
#Override
public void postValue(T value) {
super.postValue(value);
}
#Override
public void setValue(T value) {
super.setValue(value);
}
}
Based on the documentation:
setValue():
Sets the value. If there are active observers, the value will be
dispatched to them. This method must be called from the main thread.
postValue():
Posts a task to a main thread to set the given value. If you called this method multiple times before a main thread executed a posted task, only the last value would be dispatched.
To summarize, the key difference would be:
setValue() method must be called from the main thread. But if you need set a value from a background thread, postValue() should be used.
All of the above answers are correct. But one more important difference. If you call postValue() and after that you call getValue(), you may not receive the value that you set in postValue(). If the main thread had already set the value, then you will get the value that you posted, but if the main thread hadn't set the value yet, then you don't get the value that you posted. So be careful if you work in background threads.
setValue() is called directly from caller thread, synchronously notifies observers and changes LiveData value immediately. It can be called only from MainThread.
postValue() uses inside something like this new Handler(Looper.mainLooper()).post(() -> setValue()), so it runs setValue via Handler in MainThread. It can be called from any thread.
setValue()
Sets the value. If there are active observers, the value will be dispatched to them.
This method must be called from the main thread.
postValue
If you need set a value from a background thread, you can use postValue(Object)
Posts a task to a main thread to set the given value.
If you called this method multiple times before a main thread executed a posted task, only the last value would be dispatched.
This is not a direct answer to the above problem. The answers from Sagar and w201 are awesome. But a simple rule of thumb I use in ViewModels for MutableLiveData is:
private boolean isMainThread() {
return Looper.myLooper() == Looper.getMainLooper();
}
private MutableLiveData<Boolean> mutVal = new MutableLiveData<>(false);
public LiveData<Boolean> getMutVal() { return this.mutVal; }
public void setMutVal(boolean val) {
if (isMainThread()) mutVal.setValue(val);
else mutVal.postValue(val);
}
Replace mutVal with your desired value.
setValue() method must be called from the main thread. If you need to set a value from a background thread, you can use postValue().
More here.
postValue - can be used from anywhere
setValue - only from main/UI thread
Basically, postValue should be used only from background thread as it might be slower compared to setValue, which reacts faster.
I've wrote a snippet that handles both case:
/**
* Live data thread-safe set-value
* Context: https://stackoverflow.com/a/52227632/6688493
*/
fun <T> MutableLiveData<T>.assignValue(newValue: T){
if(Looper.myLooper() == Looper.getMainLooper()) {
this.value = newValue
}
else {
this.postValue(newValue)
}
}
TL; DR
If you are working on the main thread, then both setValue and postValue will work in the same manner i.e. they will update the value and notify the observers.
If working in some background thread, then you can't use setValue. You have to use postValue here with some observer.
More here
In our app, we had used single LiveData that contains data for multiple views in an activity/screen. Basically N no of datasets for N no of views. This troubled us a bit because the way postData is designed for. And we have state object in LD that conveys to view about which view needs to be updated.
so LD looks like this:
LD {
state (view_1, view_2, view_3 …),
model_that_contains_data_of_all_views
}
There are couple of views (view_1 and view_2) that had to be updated when one event occurs..mean they should get notified at the same time when event occurs. So, I called:
postData(LD(view_1, data))
postData(LD(view_2, data)
This would not work for reasons we know.
What I understood is that basically one LD should represent only one view. Then there is no chance that you would've to call postData() twice in a row. Even if you call, the way postData handles it for you is what you would also expect (showing latest data for you in view). Everything fall well in place.
One LD -> one View. PERFECT
One LD -> multiple views THERE MAY BE A WEIRD BEHAVIOR
If setting the value will take a long time (if you have to retrieve additional data from a remote source that could be slow to respond, for example), use postValue() so you don't lock up the main thread.
When setting the value is guaranteed to be fast (as it most often is), setValue() is simplest and best.

RxJava: How to wait for all subscriptions to complete?

I am fairly new to rxJava, trying stuff by my own. I would like to get some advice if I'm doing it right.
Usecase: On the first run of my app, after a successful login I have to download and save in a local database several dictionaries for the app to run with. The user has to wait till the downloading process finishes.
Current solution: I am using retrofit 2 with rxjava adapter in order to get the data. I am bundling all Observables into one using the zip operator. After all downloads are done the callback triggers and saving into database begins.
Nothing speaks better than some code:
Observable<List<OrderType>> orderTypesObservable = backendService.getOrderTypes();
Observable<List<OrderStatus>> orderStatusObservable = mockBackendService.getOrderStatuses();
Observable<List<Priority>> prioritiesObservable = backendService.getPriorities();
return Observable.zip(orderTypesObservable,
orderStatusObservable,
prioritiesObservable,
(orderTypes, orderStatuses, priorities) -> {
orderTypeDao.deleteAll();
orderTypeDao.insertInTx(orderTypes);
orderStatusDao.deleteAll();
orderStatusDao.insertInTx(orderStatuses);
priorityDao.deleteAll();
priorityDao.insertInTx(priorities);
return null;
});
Questions:
Should I use the zip operator or is there a better one to fit my cause?
It seems a bit messy doing it this way. This is only a part of the code, I have currently 12 dictionaries to load. Is there a way to refactor it?
I would like to insert a single dictionary data as soon as it finishes downloading and have a retry mechanism it the download fails. How can I achieve that?
I think in your case it's better to use Completable, because for you matter only tasks completion.
Completable getAndStoreOrderTypes = backendService.getOrderTypes()
.doOnNext(types -> *store to db*)
.toCompletable();
Completable getAndStoreOrderStatuses = backendService.getOrderStatuses()
.doOnNext(statuses -> *store to db*)
.toCompletable();
Completable getAndStoreOrderPriorities = backendService.getOrderPriorities()
.doOnNext(priorities -> *store to db*)
.toCompletable();
return Completable.merge(getAndStoreOrderTypes,
getAndStoreOrderStatuses,
getAndStoreOrderPriorities);
If you need serial execution - use Completable.concat() instead of merge()
a retry mechanism if the download fails
Use handy retry() operator
It is not good, to throw null value object into Rx Stream (in zip your return null, it is bad).
Try to not doing that.
In your case, you have 1 api call and 2 actions to save response into the database, so you can create the chain with flatMap.
It will look like:
backendService.getOrderTypes()
.doOnNext(savingToDatabaseLogic)
.flatMap(data -> mockBackendService.getOrderStatuses())
.doOnNext(...)
.flatMap(data -> backendService.getPriorities())
.doOnNext(...)
if you want to react on error situation, in particular, observable, you can add onErrorResumeNext(exception->Observable.empty()) and chain will continue even if something happened
Also, you can create something like BaseDao, which can save any Dao objects.

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();
}

Rx-java pass by reference or pass by value?

In java methods everything is passed-by-value so i can change the object attributes passed to the method and expect that the original object attributes are changed. but in this method i get different result:
I have this method:
public Observable<Menu> makeMenu(Menu menu, NumberSettingChanges.MenuChanges changes) {
// Start flow with added and edited extensions
return Observable.from(changes.added.entrySet())
.mergeWith(Observable.from(changes.edited.entrySet()))
//Upload announcement voices or do nothing if extension is not an announcement
.flatMap(e -> {
if (AppTypeContract.APP_TYPE_ANNOUNCEMENT.equals(e.getValue().type)) {
return mMediaManager.uploadAsync(e.getValue().config.localPrompt)
.doOnNext(response -> {
//Update extension prompt with the storage path.
menu.config.extensions.get(e.getKey()).config.prompt = response.mPath;
menu.config.extensions.get(e.getKey()).config.localPrompt = "";
})
.flatMap(response -> Observable.just(e));
} else {
return Observable.just(e);
}
}
)
}
and i manipulate menu attributes in the flatmap:
menu.config.extensions.get(e.getKey()).config.localPrompt = "";
I call the method in the same class:
public Observable<NumberSetting> saveSettings(NumberSetting o, NumberSetting n) {
NumberSettingChanges changes = compareNumberSetting(o, n);
return makeMenu(n.day, changes.day)
.mergeWith(makeMenu(n.night, changes.night));
}
and finally:
saveSettings(ns, mNumberSettingNew).subscribe();
What i expect is that the mNumberSettingNew.menu.config.extensions.get(e.getKey()).config.prompt is changed but no change is happening after this call and the mNumberSettingNew has no change at all.
Note that i am sure that changing prompt line is done in the debug.
I don't think I could explain Java's parameter semantics any better than (or even half as good as) the link you referenced in your first paragraph so I won't try. The main point is: Everything in Java is passed by value (i. e. copied) but with objects what is copied is not the object itself but the reference to the object. So in other words the reference is passed by value.
So with respect to your particular problem: Yes, if you pass a reference to a mutable object to some rx-java code that reference will point to the same instance of the object. If you mutate the instance then the caller code will also be able to see the changes because they were made on the same instance. That's because rx-java is still only Java and cannot change the language semantics on that level.
Without seeing the whole code I am unsure what could be the problem here... When are you checking whether mNumberSettingsNew actually has the changes you were making in your doOnNext? If you check that immediately after saveSettings(ns, mNumberSettingNew).subscribe(); your uploadAsync may not have returned yet. You could try adding an actual Subscriber in your subscribe and check the result there.
On a more general note, I think you should try to avoid side-effects like this as much as you can when using rx-java. Your case - taking an input object, applying a set of (possibly asynchronous) changes to that object, and waiting for the changed output object - is a bit tricky, but I think it could be done with scan. Maybe something vaguely like this:
Observable.from(changes.added.entrySet())
.mergeWith(Observable.from(changes.edited.entrySet()))
.scan(menuBeforeAnyChanges, new Func2<Menu, Change, Menu>() {
public Menu call(final Menu previousVersionOfTheMenu, final Change nextChange) {
// since I don't know of a version of scan that can return
// an Observable you would I think you would have to adapt
// your code in here to be fully synchronous - but of
// course the scan itself could run asynchronously
final newVersionOfTheMenu = previousVersionOfTheMenu.applyChange(nextChange);
return newVersionOfTheMenu;
}
)
This would take the original Version of the menu, consecutively apply all the changes from added and edited and /emit/ every updated version of menu. So you would not have any side effects but simply subscribe to that observable with a Subscriber<Menu> and then take the last() Menu and that would be the one with all changes applied.
EDIT: Oh, I just saw that there is another method called reduce that does just that: first scan and then last or takeLast.

Categories

Resources