RxAndroid Simplify a common pattern? - java

I find myself writing over and over again:
Observable.create(new Observable.OnSubscribe</* some type */>() {
#Override
public void call(Subscriber<? super /* some type */> subscriber) {
try {
subscriber.onNext(/* do something */);
subscriber.onCompleted();
} catch (IOException e) {
subscriber.onError(e);
}
}
}).observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread());
for network operations.
Is there any way to make it less repetative ?

The first create can be replaced by fromCallable.
Observable.fromCallable(() -> calculationReturnsAValue());
The application of schedulers can be achieved by creating a Transformer:
Transformer schedulers = o ->
o.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
and composing with it:
source.compose(schedulers);

Related

How to avoid repetition with same params

I am trying to find out a way to avoid the kind of repetition below
try {
await().pollInterval(5, TimeUnit.SECONDS).pollDelay(500, TimeUnit.MILLISECONDS)
.atMost(30, TimeUnit.SECONDS).until(() -> {
// 1. methods in here
});
} catch (final Exception e) {
//TODO: can be handled internally
}
It happens in several places in my code, and I want to make it less repetitive, but I am not finding a way to do so. I thought about lambdas, but I don`t know much about it nor if it would fit in here.
Inside of it can be many different things, it is not the same for all nor they have the same inheritance.
public static void main(String... args) {
awaitUntil(() -> {
// payload 1
return true;
});
awaitUntil(() -> {
// payload 2
return true;
});
}
public static void awaitUntil(Callable<Boolean> conditionEvaluator) {
try {
Awaitility.await()
.pollInterval(5, TimeUnit.SECONDS)
.pollDelay(500, TimeUnit.MILLISECONDS)
.atMost(30, TimeUnit.SECONDS)
.until(conditionEvaluator);
} catch(Exception e) {
//TODO: can be handled internally
}
}

Create Flowable from while loop

I am pretty new to RxJava and I need to create repository with several datasources. It is complex to me because there are several smaller subtasks which I don't know how to implement with RxJava.
First I have self written dao, which process InputStream, and provides Items in the specified range. Currently it simply collects data in a list, but I want to provide Items one by one using flowable; Currently it privides Maybe<List<Item>>. Also there several errors need to be transmitted to higher level (datasource). Such as EndOfFile, to notify DataSource that data fully cached;
Dao.class:
List<Item> loadRange(int start, int number) throws ... {
...
while(...) {
...
//TODO contribute item to flowable
resultList.add(new Item(...))
}
return resultList;
}
Maybe<List<Item>> method just created Maybe.fromCallable();
Please help me!
Something like this should work for this :
Flowable<Item> loadRange(int start, int number) {
return Flowable.create(emitter -> {
try {
while (...){
emitter.onNext(new Item());
}
emitter.onComplete();
} catch (IOException e) {
emitter.onError(e);
}
}, BackpressureStrategy.BUFFER);
}
I assume once the loop is done you want to complete, also send errors downstream, rather than handle on the method signature. Also you can change the BackPressureStrategy to suit your usecase i.e DROP, LATEST etc..
As you're new to RxJava, the anonymous class would be :
Flowable<Item> loadRange(int start, int number) {
return Flowable.create(new FlowableOnSubscribe<Item>() {
#Override public void subscribe(FlowableEmitter<Item> emitter) {
try {
while (...){
emitter.onNext(new Item());
}
emitter.onComplete();
} catch (IOException e) {
emitter.onError(e);
}
}
}, BackpressureStrategy.BUFFER);
}

Try & Catch When Calling supplyAsync

I am new to CompletableFuture, I will like to call a method MetadataLoginUtil::login which can throw an exception. However, the code below is not compiled although I have 'exceptionally' written. It says that I must wrap the MetadataLoginUtil::login' within try & catch.
Please advise.
Thanks ahead !
public void run() throws ConnectionException {
CompletableFuture<Void> mt = CompletableFuture.supplyAsync(MetadataLoginUtil::login)
.exceptionally(e -> {
System.out.println(e);
return null;
})
.thenAccept(e -> System.out.println(e));
}
This is not a deficiency of how CompletableFuture works in general, but of the convenience methods, all using functional interfaces not allowing checked exceptions. You can solve this with an alternative supplyAsync method:
public static <T> CompletableFuture<T> supplyAsync(Callable<T> c) {
CompletableFuture<T> f=new CompletableFuture<>();
CompletableFuture.runAsync(() -> {
try { f.complete(c.call()); } catch(Throwable t) { f.completeExceptionally(t); }
});
return f;
}
This is basically doing the same as the original supplyAsync, but allowing checked exceptions. So you can use it right like in your original attempt, only redirecting the initial supplyAsync call.
CompletableFuture<Void> mt = supplyAsync(MetadataLoginUtil::login)
.exceptionally(e -> { System.out.println(e); return null; } )
.thenAccept(e -> System.out.println(e));
CompletableFuture.supplyAsync(Supplier<U>) expects a java.util.function.Supplier<U> instance, and Supplier.get() method's signature does not allow for checked exceptions. To see this clearly, notice that CompletableFuture.supplyAsync(MetadataLoginUtil::login) is equivalent to
CompletableFuture<Void> mt = CompletableFuture.supplyAsync(new Supplier<Void>() {
#Override
public Void get() {
return MetadataLoginUtil.login();
}
})
which clearly cannot compile.
You can handle the exception inside your Supplier, changing CompletableFuture.supplyAsync(MetadataLoginUtil::login).exceptionally(e -> {System.out.println(e); return null; } ) to
CompletableFuture.supplyAsync(() -> {
try {
return MetadataLoginUtil.login();
} catch (Exception e) {
System.out.println(e);
return null;
}
})
It's not pretty, but CompletableFuture's API doesn't seem to work with checked exceptions very well.

Create ReactiveX observable in Java 6 without lambda expression

I found an example for how to create an observable object (ReactiveX):
static Observable<Integer> getDataSync(int i) {
return Observable.create((Subscriber<? super Integer> s) -> {
// simulate latency
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
s.onNext(i);
s.onCompleted();
});
}
But my project does not support Java 8 for lambda expression. I couldn't find example of how to use ReactiveX observable without lambda expression.
Are you just looking for the correct lambda-less syntax for what you are doing?
That would be:
static Observable<Integer> getDataSync(int i) {
return Observable.create(new Observable.OnSubscribe<Integer> {
#Override
public void call(Subscriber<? super Integer> s) {
// simulate latency
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
s.onNext(i);
s.onCompleted();
}
});
}

RxJava: Conditionally catch error and stop propagation

I use Retrofit with RxJava Observables and lambda expressions. I'm new to RxJava and cannot find out how to do the following:
Observable<ResponseBody> res = api.getXyz();
res.subscribe(response -> {
// I don't need the response here
}, error -> {
// I might be able to handle an error here. If so, it shall not go to the second error handler.
});
res.subscribe(response -> {
// This is where I want to process the response
}, error -> {
// This error handler shall only be invoked if the first error handler was not able to handle the error.
});
I looked at the error handling operators, but I don't understand how they can help me with my usecase.
Method 1: Keep the two Subscribers but cache the Observable.
Just keep everything as it is now, but change the first line to:
Observable<ResponseBody> res = api.getXyz().cache();
The cache will make sure that the request is only sent once but that sill both Subscribers get all the same events.
This way whether and how you handle the error in the first Subscriber does not affect what the second Subscriber sees.
Method 2: Catch some errors with onErrorResumeNext but forward all others.
Add onErrorResumeNext to your Observable to produce something like this (in the "inner" object):
Observable observable = Observable.error(new IllegalStateException())
.onErrorResumeNext(new Func1<Throwable, Observable<?>>() {
#Override
public Observable<?> call(Throwable throwable) {
if (throwable instanceof NumberFormatException) {
System.out.println("NFE - handled");
return Observable.empty();
} else {
System.out.println("Some other exception - panic!");
return Observable.error(throwable);
}
}
});
And only subscribe once (in the "outer" object):
observable.subscribe(new Subscriber() {
#Override
public void onCompleted() {
System.out.println("onCompleted");
}
#Override
public void onError(Throwable e) {
System.out.println("onError");
e.printStackTrace();
}
#Override
public void onNext(Object o) {
System.out.println(String.format("onNext: %s", String.valueOf(o)));
}
});
This way, the error is only forwarded if it cannot be handled in the onErrorResumeNext - if it can, the Subscriber will only get a call to onCompleted and nothing else.
Having side effects in onErrorResumeNext makes me a bit uncomfortable, though. :-)
EDIT: Oh, and if you want to be extra strict, you could use Method 3: Wrap every case in a new object.
public abstract class ResultOrError<T> {
}
public final class Result<T> extends ResultOrError<T> {
public final T result;
public Result(T result) {
this.result = result;
}
}
public final class HandledError<T> extends ResultOrError<T> {
public final Throwable throwable;
public Result(Throwable throwable) {
this.throwable = throwable;
}
}
public final class UnhandledError<T> extends ResultOrError<T> {
public final Throwable throwable;
public Result(Throwable throwable) {
this.throwable = throwable;
}
}
And then:
Wrap proper results in Result (using map)
Wrap handle-able errors in HandledError and
un-handle-able errors in UnhandledError (using onErrorResumeNext with an if clause)
handle the HandledErrors (using doOnError)
have a Subscriber<ResultOrError<ResponseBody>> - it will get notifications (onNext) for all three types but will just ignore the HandledErrors and handle the other two types.

Categories