I have created Observable with subscripiton:
private Subscription mSearchSubscription = null;
mSearchSubscription = Observable.fromCallable(() -> {
many network requests
}).subscribe(doing sth);
When my activity is destroyed I am executing following line:
if (mSearchSubscription != null){
mSearchSubscription.unsubscribe();
}
According to informations found in internet this line should cancel all code execution from this block. But it doesn't :( All network requests inside this block are wrapped Observables with subscribe so when one request is finished next one is executing and so on. As I noticed after unsubscribe is executed and activity is destroyed code inside Observable is still executing.
Is there any better way to stop it? I am using in this project RxJava1
Update:
I have MVC architecture with controller Injected into Activity. By overriding onDestroy from activity I call method in controller to unsubscribe. This is whole code. Every network request inside block fromCallable(()-> ) is like this mWebService.getSth(body parameters).subscribe(doSth) (in interface this return Observable ) . I am not expecting to stop during execution of internal observable, but stop executing this block of code, just like return. This is wrapped because of many requests needed to be executed one by one and return result to activity when everything is ready.
You haven't given much details on how and why you do that, so my best guess-suggestion is to use create:
Observable.create(emitter -> {
CompositeSubscription resources = new CompositeSubscription();
emitter.setSubscription(resources);
if (resources.isUnsubscribed()) {
return;
}
// add these if you want to cancel the inner sources
// resources.add(
mWebService.getFirst(/* params */).subscribe(/* postprocess */);
// );
if (resources.isUnsubscribed()) {
return;
}
mWebService.getSecond(/* params */).subscribe(/* postprocess */);
if (resources.isUnsubscribed()) {
return;
}
mWebService.getThird(/* params */).subscribe(/* postprocess */);
emitter.onNext(/* the result object*/
emitter.onComplete();
}, Emitter.BackpressureMode.BUFFER)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(/* work with the data */);
Related
I'm adapting some sample code from what3words for accessing their API via their Java SDK. It uses RXJava.
The sample code is:
Observable.fromCallable(() -> wrapper.convertTo3wa(new Coordinates(51.2423, -0.12423)).execute())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
if (result.isSuccessful()) {
Log.i("MainActivity", String.format("3 word address: %s", result.getWords()));
} else {
Log.e("MainActivity", result.getError().getMessage());
}
});
First of all. this gives a deprecation warning when building and a IDE warning (Result of 'Observable.subscribe()' is ignored).
To resolve this first issue I have added Disposable myDisposable = in front of the Observable. Is this correct? (See below for where it is added)
Next I need to add a timeout so that I can show a warning etc if the request times out. To do this I have added .timeout(5000, TimeUnit.MILLISECONDS) to the builder.
This works, but the way timeouts seem to work on Observables is that they throw an exception and I cannot figure out how to catch and handle that exception.
What I have right now is:
Disposable myDisposable = Observable.fromCallable(() -> wrapper.convertTo3wa(new Coordinates(51.2423, -0.12423)).execute())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.timeout(5000, TimeUnit.MILLISECONDS)
.subscribe(result -> {
if (result.isSuccessful()) {
Log.i("MainActivity", String.format("3 word address: %s", result.getWords()));
} else {
Log.e("MainActivity", result.getError().getMessage());
}
});
This builds and runs fine, and the API/deprecation warning is not shown, BUT when no network is available this correctly times out and throws the unhandled exception.
So, the code seems to be correct, but how on earth do add the exception handling to catch the timeout TimeoutException that is thrown?
I've tried numerous things, including: adding a try-catch clause around the whole Observable - this warns that TimeoutException is not thrown by the code in the `try; and adding an error handler.
Adding the error handler has got me closest, and so the code below is as far as I have got:
Disposable myDisposable = Observable.fromCallable(() -> wrapper.convertTo3wa(new Coordinates(51.2423, -0.12423)).execute())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.timeout(5000, TimeUnit.MILLISECONDS)
.subscribe(result -> {
if (result.isSuccessful()) {
Log.i("MainActivity", String.format("3 word address: %s", result.getWords()));
} else {
Log.e("MainActivity", result.getError().getMessage());
}
}, error -> {
runOnUiThread(new Runnable() {
#Override
public void run() {
myTextView.setText(R.string.network_not_available);
}
});
});
This catches the Timeout correctly and updates my UI without error, however when the network is restored it seems that the Observable might be trying to return and a null pointer exception is thrown.
(Update, this NPE might actually be being thrown sometimes after a short time whether the network is restored or not... but it is always thrown when the network restores.)
I get FATAL EXCEPTION: RxCachedThreadScheduler-1 and java.lang.NullPointerException: Callable returned a null value. Null values are generally not allowed in 3.x operators and sources.
Do I need to destroy the Observable or something to prevent the NPE?
You need to add an onError handler to your subscribe call:
.subscribe(result -> {
if (result.isSuccessful()) {
Log.i("MainActivity", String.format("3 word address: %s", result.getWords()));
} else {
Log.e("MainActivity", result.getError().getMessage());
}
},
error -> {
// handle error here
});
When a an exception makes it to a subscribe call that does not have an onError handler, it will throw a OnErrorNotImplementedException, like this:
io.reactivex.exceptions.OnErrorNotImplementedException: The exception was not handled due to missing onError handler in the subscribe() method call. Further reading: https://github.com/ReactiveX/RxJava/wiki/Error-Handling | java.util.concurrent.TimeoutException: The source did not signal an event for 1 seconds and has been terminated.
Adding the onError handler will prevent that, and the onError handler will get called instead.
There's a few things going on here:
First of all. this gives a deprecation warning when building and a IDE warning (Result of 'Observable.subscribe()' is ignored).
subscribe() returns a Disposable. The idea is that when you're no longer interested in receiving the output of your observable, you call dispose() on the disposable and the work terminates. This can also prevent memory leaks.
As an example, imagine you have an Activity, and you start an Observable to run a long network query which finally posts something to the Activity UI. If the user navigates away before this task completes, or the Activity is otherwise destroyed, then you're no longer interested in its output because there is no longer a UI to post to. So you may call dispose() in onStop().
So, the code seems to be correct, but how on earth do add the exception handling to catch the timeout TimeoutException that is thrown?
Using the error block in subscribe is one option, but there are others. For example, if you wanted to keep using your Result class, you could use something like onErrorReturn(throwable -> Result.error(throwable)). Obviously I'm guessing what that class looks like:
.timeout(5000, TimeUnit.MILLISECONDS)
.onErrorReturn(throwable -> Result.errorWithMessage(R.string.network_not_available))
.subscribe(result -> {
if (result.isSuccessful()) {
Log.i("MainActivity", String.format("3 word address: %s", result.getWords()));
} else {
myTextView.setText(result.getErrorMessage());
}
});
java.lang.NullPointerException: Callable returned a null value. Null values are generally not allowed in 3.x operators and sources.
This:
wrapper.convertTo3wa(new Coordinates(51.2423, -0.12423)).execute()
is returning null. You can do something like:
Observable.fromCallable(() -> {
Result<?> out = wrapper.convertTo3wa(new Coordinates(51.2423, -0.12423)).execute();
if(out == null)
out = Result.error(/*Returned null*/);
}
return out;
}
I have a Spring Boot application that will call several microservice URLs using the GET method. These microservice URL endpoints are all implemented as #RestControllers. They don't return Flux or Mono.
I need my application to capture which URLs are not returning 2xx HTTP status.
I'm currently using the following code to do this:
List<String> failedServiceUrls = new ArrayList<>();
for (String serviceUrl : serviceUrls.getServiceUrls()) {
try {
ResponseEntity<String> response = rest.getForEntity(serviceUrl, String.class);
if (!response.getStatusCode().is2xxSuccessful()) {
failedServiceUrls.add(serviceUrl);
}
} catch (Exception e){
failedServiceUrls.add(serviceUrl);
}
}
// all checks are complete so send email with the failedServiceUrls.
mail.sendEmail("Service Check Complete", failedServiceUrls);
}
The problem is that each URL call is slow to respond and I have to wait for one URL call to complete prior to making the next one.
How can I change this to make the URLs calls be made concurrently? After all call have completed, I need to send an email with any URLs that have an error that should be collected in failedServiceUrls.
Update
I revised the above post to state that I just want the calls to be made concurrently. I don't care that rest.getForEntity call blocks.
Using the executor service in your code, you can call all microservices in parallel this way:
// synchronised it as per Maciej's comment:
failedServiceUrls = Collections.synchronizedList(failedServiceUrls);
ExecutorService executorService = Executors.newFixedThreadPool(serviceUrls.getServiceUrls().size());
List<Callable<String>> runnables = new ArrayList<>().stream().map(o -> new Callable<String>() {
#Override
public String call() throws Exception {
ResponseEntity<String> response = rest.getForEntity(serviceUrl, String.class);
// do something with the response
if (!response.getStatusCode().is2xxSuccessful()) {
failedServiceUrls.add(serviceUrl);
}
return response.getBody();
}
}).collect(toList());
List<Future<String>> result = executorService.invokeAll(runnables);
for(Future f : result) {
String resultFromService = f.get(); // blocker, it will wait until the execution is over
}
If you just want to make calls concurrently and you don't care about blocking threads you can:
wrap the blocking service call using Mono#fromCallable
transform serviceUrls.getServiceUrls() into a reactive stream using Flux#fromIterable
Concurrently call and filter failed services with Flux#filterWhen using Flux from 2. and asynchronous service call from 1.
Wait for all calls to complete using Flux#collectList and send email with invalid urls in subscribe
void sendFailedUrls() {
Flux.fromIterable(erviceUrls.getServiceUrls())
.filterWhen(url -> responseFailed(url))
.collectList()
.subscribe(failedURls -> mail.sendEmail("Service Check Complete", failedURls));
}
Mono<Boolean> responseFailed(String url) {
return Mono.fromCallable(() -> rest.getForEntity(url, String.class))
.map(response -> !response.getStatusCode().is2xxSuccessful())
.subscribeOn(Schedulers.boundedElastic());
}
Blocking calls with Reactor
Since the underlying service call is blocking it should be executed on a dedicated thread pool. Size of this thread pool should be equal to the number of concurrent calls if you want to achieve full concurrency. That's why we need .subscribeOn(Schedulers.boundedElastic())
See: https://projectreactor.io/docs/core/release/reference/#faq.wrap-blocking
Better solution using WebClient
Note however, that blocking calls should be avoided when using reactor and spring webflux. The correct way to do this would be to replace RestTemplate with WebClient from Spring 5 which is fully non-blocking.
See: https://docs.spring.io/spring-boot/docs/2.0.3.RELEASE/reference/html/boot-features-webclient.html
I'm working on a small subsystem that integrates two simple components using RxJava 2.
These two components work in a simple client-server manner where the first component produces observable data opening a resource under the hood.
The resource is not exposed to the second component.
Moreover, it must be open as long as the observable is in use, however the observable object cannot determine when it should be closed.
Speaking in code, an example implementation is like this:
private Disposable disposable;
public void onCreate() {
final Maybe<Object> maybeResource = Maybe.defer(() -> {
System.out.println("open");
// here is the resource under the hood, it is encapsulated in the observable and never gets exposed
final Closeable resource = () -> { };
return Maybe.just(resource)
.doOnDispose(() -> {
// this "destructor" is never called, resulting in a resource leak
System.out.println("close");
resource.close();
})
// arbitrary data, does not represent the data I'm working with, but it hides the resource away
.map(closeable -> new Object());
});
disposable = maybeResource.subscribe(data -> System.out.println("process: " + data));
}
public void onUserWorflow() {
// ...
System.out.println("... ... ...");
// ...
}
public void onDestroy() {
disposable.dispose();
}
The output I'd anticipate to get is:
open
process: <...>
... ... ...
close <-- this is never produced
but the last line, close, is never produced as the doOnDispose method is not invoked and does not work as I might think it's supposed to.
Therefore the resource gets never released.
There is also Maybe.using that does a similar thing, but it does not allow to "span" across the "user workflow".
Is there an RxJava/RxJava 2 way that allows managing "closeable" resources closed on disposing a subscriber?
i guess you need to use Observable.create() instead of Maybe.
Something like that:
final Observable<Object> resourceObservable = Observable.create<Object> {(emitter ->
// do you staff
emitter.onNext(new Object()); //to make observable emit something
emitter.setCancellable (
System.out.println("close");
resource.close();
)
);
disposable = resourceObservable.subscribe(data -> System.out.println("process: " + data));
I've code like this in a repository:
return Completable.fromAction {
// Some code
loginService.login(id)
.subscribe(
{ response ->
if(response.isNotSuccessful()) {
throw Exception()
}
// Some code
},
{ e ->
throw e
}
)
}
I've code like this in a ViewModel:
fun onLoginAction(id) {
repository.login(id)
.subscribe(
{
showSuccess()
},
{
showFailure()
}
)
}
Basically, the ViewModel calls the login method in the repository which returns the Completable.
This results in an UndeliverableException when the response is not successful. I want the Completable's subscriber's onError() method to be called. How do I do this?
I don't have enough knowledge to actually say this with certainty, but I still think this has some value to you and it's too big for a comment.
Here's what I think it's happening. When onError fails rx won't run this through the same observable stream. Instead, it will propagate this to the RxPlugins error handler and eventually to the default exception handler in your system. You can find this here.
This is to say that when loginService.login(id) throws the exception in the onError, the Completable stream won't have a chance to catch it and forward it to the onError of the outer subscribe. In other words, the completable stream is independent of the login service one.
Usually, you'd want to create one single stream and let the view model subscribe to it. If you have more than one stream, rx has loads of operators to help you chain these. Try and make the repository return one stream from the service. Something like this:
fun login(id) = loginService.login(id)
And now on the view model, you can check if the call was or not successful using the same method - response.isNotSuccessful()
I'm using RxJava in a web service to create multiple observables, each that do something during it's onNext and onError functions. I want to wait until all of these observables finish completing its work before returning a response to the client, but I also want these tasks to be executed in parallel
I've merged all of these using:
Observable<List<Obj>> mergedObs = Observable.mergeDelayError(tasks)
I then convert this into a blocking observable via
mergedObs.toList().toBlocking.subscribe(...)
However, I've noticed that while the merged observable waits for all tasks to call subscribe(), it does not wait for the tasks within the subscribe function to complete. Here's some sample code with unimportant details omitted:
public void doWork() {
Observable<Obj> mergedTasks = getTasks();
mergedTasks.toList().toBlocking().subscribe(
results -> LOG.info("Done!)
);
return; // eventually returns a web response
}
private Observable<Obj> getTasks() {
List<Observable<Obj>> tasks = new ArrayList<>();
Observable<Obj> task1 = getTask1();
Observable<Obj> task2 = getTask2();
tasks.add(task1);
tasks.add(task2);
// Both tasks execute in parallel
tasks.toList().subscribe(
result -> processSearch(), // this can execute after the service returns a response!
exception -> handleException() // this can execute after the service returns a response!
);
return Observable.mergeDelayError(tasks);
}
private Observable<Obj> getTask1() {
// implementation detail
}
private Observable<Obj> getTask2() {
// implementation detail
}
While this code does wait for both tasks to call subscribe(), it doesn't wait for the work performed within the onSuccess or onError.