RxJava - wait for async task and then call Api - java

Trying to get lastLocation and once it's done call api. But somehow once location is obtained my api calls always running in mainThread, so i'm getting exception:
android.io.NetworkOnMainThreadException
Here is my location observer:
fun getLocation(): Single<Location> {
return Single.create<Location> { subscriber ->
fusedLocationClient.lastLocation.addOnSuccessListener {
if (it != null) {
subscriber.onSuccess(it)
} else {
subscriber.onError(Exception("No location"))
}
}
}
}
Code that does some transformations
val locationObserver = getLocation()
observables.add(locationObserver.flatMap { _ -> sendDataToServer(data)})
Observer
Single.zip(observables) { args1 -> args1 }.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe({
Timber.i("Success")
}, {
Timber.i("Error %s", observables.size, it.localizedMessage)
it.printStackTrace()
})
I've set subscribeOn so it shouldn't be on mainThread, but looks like something missed.
Found that if i will use something like Single.just("One").flatMap{ ... } that will work fine and will be executed on non-main thread.
Is there something to do with getLocation function?

The order of subscribeOn, observeOn, subscribe, and transformations matters. Apparently, it's needed to do the transformations, in this case, the flatMap after specifying the observer thread with observeOn to make sure the code is executed in the right thread.

Related

Coroutine job completing even after cancellation from external function

I have this function:
suspend fun functionCall(): Job {
return MainScope().launch {
var i = 0
while(i < 3) {
i++
delay(3000)
yield()
}
}
cancel()
}
And I am calling from an external function when a button is clicked:
MainScope().launch {
if(functionCall().isActive) {
functionCall().cancelAndJoin()
}
}
Both of these functions are being run in a repository class.
And it is still iterating through the whole while loop even after the above if statement is triggered. What I'm noticing while debugging is "i" is also being reset to 0 which could indicate the job is being triggered more than once but it is definitely being triggered only once so I'm confused about what is happening.
What I want to happen is after that if statement for the entire job to cancel and for the entire function to return and run no more code.
I've also tried while(ensureActive) and the same thing is happening.
How do I do this?
Since this is Android, you should launch your UI-related coroutines from lifecycleScope. If you have a job that needs to survive screen rotations, you should launch it from inside a ViewModel from viewModelScope, and it must not touch any UI elements.
If you want to cancel a specific coroutine when an event happens, you should store that coroutine Job in a property so you can call cancel() on it. So a typical pattern inside an Activity for example might be:
private var fooJob: Job? = null
private fun fooSomething() {
fooJob = lifecycleScope.launch {
repeat(5) {
delay(1000)
Log.i("count", it.toString())
}
}
}
private fun cancelCurrentFoo() {
fooJob?.cancel()
}
Suppose you have a coroutine job you can start by calling one of the functions of your ViewModel, but you want the Activity/Fragment to be able to cancel it early. Then you expose a function that returns the coroutine Job:
fun foo() = viewModelScope.launch {
repeat(5) {
delay(1000)
Log.i("count", it.toString())
}
}
The Activity can call this function and it gets a Job instance in return that it can call cancel() on whenever it wants.

How to test asynchronous method

I need to asynchronously fetch some data from DB, and then synchronously update currently cached data, with the one that was fetched.
At the moment my code looks like this:
#Asynchronous
public void fetchData() {
CompletableFuture.supplyAsync(() -> {
//Do some logic to fetch data
return fetchedData;
}).thenAccept(fetchedData -> updateCache(fetchedData));
}
My problem is that when I call this method in my tests, it instantly goes to thenAccept but fetchedData is empty. I've tried to change my method, and return CompletableFuture<List<Data>>, and call fetchData().join() in tests. It worked fine, but first - my app wouldn't build, since #Asynchronous need to return Future or void, and I don't think changing method just to test it is a good idea.
I've heard about Await library, but can not use it in current project.
I'am also not sure, if #Asynchronous is needed in my case? If I'm not mistaken, CompletableFuture should run in own thread by default?
Jacek, I think #Asynchronous annotation is not required. You can use the pure form of completable future. I provide below the sample code based upon the code you have provided.
public String getInfoFromDB() {
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Data from DB";
}
public void updateCache(String dataFromDB) {
System.out.println("Data from D : " + dataFromDB);
System.out.println("Doing some operations with data from DB");
}
public void fetchData() {
CompletableFuture cf =
CompletableFuture.supplyAsync(() -> getInfoFromDB())
.thenAccept(dataFromDB -> updateCache(dataFromDB));
cf.join();
}
You can use this useful library: awaitility

Subject.onNext/ onComplete does not run subscribeOn thread

I am stuck with a very annoying issue. I want to create an Observable that emits some values after the initialization of some component. The problem is that the other component behaves strange and calls back on the main thread.
strangeComponent.init(Callback)
its callback can be an error or success:
interface Callback {
fun onError()
fun onSuccess() <-- this is always called on the main thread
}
Now I want to create an Observable that will only run once the init call was done:
val initSubject = CompletableSubject.create()
<T> fun withInit(call : (ObservableEmitter<T>) -> Unit) =
initSubject
.doOnSubscribe { startInit() }
.andThen(Observable.create{ emitter ->
call(emitter)
})
fun startInit() {
if (!initSubject.hasComplete()) {
strangeComponent.init(object : Callback {
override fun onSuccess() {
// will be called from the main thread
initSubject.onComplete()
}
...
}
}
}
And I use this like:
withInit {
//---- heavy code ------
it.onNext("Hallo")
it.onNext("World")
//---------
}.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
log("received $it")
}
So it turns out that the heavy code part is always called from the main
thread instead of the computation thread. How do I access the given subscribeOn schedular and how do I wrap a callback so that its thread model does not affect the observable chain?
I tried all kind of constructs including serialized and AsyncSubject etc to
fix this but it is always consistent.

How to chain non-blocking action in CompletionStage.exceptionally

I am writing a Play2 application service method in Java that should do the following. Asynchronously call method A, and if that fails, asynchronously call method B.
To illustrate assume this interface for the backend called by the service:
public interface MyBackend {
CompletionStage<Object> tryWrite(Object foo);
CompletionStage<Object> tryCleanup(Object foo);
}
So in my service method, I want to return a Future that can complete with these:
Success of tryWrite completed
Fail of tryWrite and Success of tryCleanup completed and failing with exception of tryWrite()
(Note: Of course tryWrite() could do any cleanup itself, this is a simplified example to illustrate a problem)
The implementation of a service calling the backend like this seems difficult to me because the CompletionStage.exceptionally() method does not allow Composing.
Version 1:
public class MyServiceImpl {
public CompletionStage<Object> tryWriteWithCleanup(Object foo) {
CompletionStage<Object> writeFuture = myBackend.tryWrite(foo)
.exceptionally((throwable) -> {
CompletionStage<Object> cleanupFuture = myBackend.tryCleanup(foo);
throw new RuntimeException(throwable);
});
return writeFuture;
}
}
So version 1 calls tryCleanup(foo) in a non-blocking way, but the CompletionStage returned by tryWriteWithCleanup() will not wait for cleanupFuture to complete. How to change this code to return a future from the service that would also wait for completion of cleanupFuture?
Version 2:
public class MyServiceImpl {
public CompletionStage<Object> tryWriteWithCleanup(Object foo) {
final AtomicReference<Throwable> saveException = new AtomicReference<>();
CompletionStage<Object> writeFuture = myBackend
.tryWrite(foo)
.exceptionally(t -> {
saveException.set(t);
// continue with cleanup
return null;
})
.thenCompose((nil) -> {
// if no cleanup necessary, return
if (saveException.get() == null) {
return CompletableFuture.completedFuture(null);
}
return CompletionStage<Object> cleanupFuture = myBackend.tryCleanup(foo)
.exceptionally(cleanupError -> {
// log error
return null;
})
.thenRun(() -> {
throw saveException.get();
});
});
return writeFuture;
}
}
Version2 uses an external AtomicReference to store the failure, and makes the asynchronous second call in another thenCompose() block, if there was a failure.
All my other attempts to do so ended up so unwieldy that I don't want to paste them here.
Unfortunately CompletionStage/CompletableFuture does not provide exception handling API's with composition.
You can work around this though by relying on a handle() with a BiFunction that returns a CompletionStage. This will give you nested stages (CompletionStage<CompletionStage<Object>>) that you can the "unnest" using compose(identity()):
public CompletionStage<Object> tryWriteWithCleanup(Object foo) {
return myBackend.tryWrite(foo)
.handle((r, e) -> {
if (e != null) {
return myBackend.tryCleanup(foo)
.handle((r2, e2) -> {
// Make sure we always return the original exception
// but keep track of new exception if any,
// as if run in a finally block
if (e2 != null) {
e.addSuppressed(e2);
}
// wrapping in CompletionException behaves as if
// we threw the original exception
throw new CompletionException(e);
});
}
return CompletableFuture.completedFuture(r);
})
.thenCompose(Function.identity());
}
You may simply wait for the completion inside the handler:
public CompletionStage<Object> tryWriteWithCleanup(Object foo) {
return myBackend.tryWrite(foo).exceptionally(throwable -> {
myBackend.tryCleanup(foo).toCompletableFuture().join();
throw new CompletionException(throwable);
});
}
This will defer the completion of the result CompletionStage to the completion of the cleanup stage. Using CompletionException as wrapper will make the wrapping transparent to the caller.
However, it has some drawbacks. While the framework might utilize the thread while waiting or spawn a compensation thread, if it is a worker thread, the blocked thread might be the caller thread if the stage returned by tryWrite happens to be already completed when entering exceptionally. Unfortunately, there is no exceptionallyAsync method. You may use handleAsync instead, but it will complicate the code while still feeling like a kludge.
Further, exceptions thrown by the cleanup may shadow the original failure.
A cleaner solution may be a bit more involved:
public CompletionStage<Object> tryWriteWithCleanup(Object foo) {
CompletableFuture<Object> writeFuture = new CompletableFuture<>();
myBackend.tryWrite(foo).whenComplete((obj,throwable) -> {
if(throwable==null)
writeFuture.complete(obj);
else
myBackend.tryCleanup(foo).whenComplete((x,next) -> {
try {
if(next!=null) throwable.addSuppressed(next);
}
finally {
writeFuture.completeExceptionally(throwable);
}
});
});
return writeFuture;
}
This simply creates a CompletableFuture manually, allowing to control its completion, which will happen either directly by the action chained to tryWrite’s stage in the successful case, or by the action chained to the cleanup stage in the exceptional case. Note that the latter takes care about chaining a possible subsequent cleanup exception via addSuppressed.

Clearing resources on unsubscribe

I am having some trouble with executing some logic when a subscription has been unsubscribed. I've been at this for hours and I have made little progress so far. This is a simplified version of my code:
public class Command<E> {
public CommandActionObservable execute() {
final CommandAction<E> command = createCommand();
final OnSubscribe<CommandAction<E>> onSubscribe = (subscriber) -> {
/* Create a listener that handles notifications and register it.
* The idea here is to push the command downstream so it can be re-executed
*/
final Listener listener = (event) -> {
subscriber.onNext(command);
}
registerListener(listener);
/* This is where I'm having trouble. The unregister method
* should be executed when the subscriber unsubscribed,
* but it never happens
*/
subscriber.add(Subscriptions.create(() -> {
unregisterListener(listener);
}));
// pass the initial command downstream
subscriber.onNext(command);
kickOffBackgroundAction();
}
final Observable<CommandAction<E>> actionObservable = Observable.create(onSubscribe)
.onBackpressureLatest()
.observeOn(Shedulers.io())
.onBackpressureLatest();
return new CommandActionObservable((subscriber) -> {
actionObservable.unsafeSubscribe(subscriber);
})
}
public class CommandActionObservable extends Observable<CommandAction<E> {
// default constructor omitted
public Observable<E> toResult() {
return lift((Operator) (subscriber) -> {
return new Subscriber<CommandAction<E>>() {
// delegate onCompleted and onError to subscriber
public void onNext(CommandAction<E> action) {
// execute the action and pass the result downstream
final E result = action.execute();
subscriber.onNext(result)
}
}
}
}
}
}
I am using the Command in the usual way, adding the resulting subscription to a CompositeSubscription and unsubscribing from it in onDestroy(). Here is an example:
final Observable<SomeType> obs = new Command<SomeType>()
.execute()
.toResult();
subscription.add(obs.subscribe(// impl here));
public void onDestroy() {
super.onDestroy();
subscription.unsubscribe();
}
As mentioned, I can't get the unsubscription logic to work and unregister the listener, which causes memory leaks in the app. If I call doOnUnsubscribe() on obs it gets called, so I am unsubscibing correctly, but maybe the nesting of the observables and lifting causes some issues.
I'd be glad to head opinions on this one.
Turns out it was way easier than I anticipated.
After a bit of digging around I was able to come up with the answer on my own. Just posting this for people that may end up in the same situation as me.
So, as I mentioned in my question, if I added a doOnSubscribe() action to the observable I was getting in my Activity, it gets notified. Next I tried adding the same action on the inner Observables I'm creating in the execute() method. They were not getting called. So, I came to the conclusion that the chain was getting broken somewhere between the observable in my activity and the observables I was creating in execute().
The only thing that was happening to the stream was the application of my custom Operator implemented in toResult(). After a Google search, I came across this excellent article - Pitfalls of Operator Implementation. I was indeed braking the chain in my operator and the upstream observables were not notified of the unsubscription.
After I did what the author advices, all is good. Here is what I needed to do:
lift((Operator) (subscriber) -> {
// connect the upstream and downstream subscribers to keep the chain intact
new Subscriber<CommandAction<E>>(subscriber) {
// the implementation is the same
}
}

Categories

Resources