How can I rewrite this via a Java Lambda?
This question is different from "Lambda Expression and generic method" issue. The issue is because of throwing an Exception.
public Observable<String> getUserInfo() {
return Observable.create(new ObservableOnSubscribe<String>() {
#Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
try {
emitter.onNext( getPlayerInfo());
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
When I rewrite this via a normal Lambda, then I get this error:
Incompatible types: Required: Observable <java.lang.String>. Found:
Observable <java.lang.Object>.
This is what I tried:
public Observable<String> getUserInfo() {
return Observable.create( emitter -> {
try {
emitter.onNext(getPlayerInfo();
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
The inference engine of the compiler cannot determine the type parameter of Observable, so you need to explicitly specify it:
return Observable.<String>create( emitter -> {
Type inference fails specifically because of the trailing calls to subscribeOn and observeOn. Another solution is introducing an intermediate method or variable, for example:
private Observable<String> getUserInfo() {
Observable<String> userInfo = Observable.create( emitter -> {
try {
emitter.onNext(getPlayerInfo();
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
});
return userInfo
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
Related
New to java 8, I would like to optimise my code bellow:
public Response create() {
try{
...
} catch (Exception e) {
codeA;
} finally {
codeB;
}
}
public Response update() {
try{
...
} catch (Exception e) {
codeA;
} finally {
codeB;
}
}
I have a lot of methods using this same way to catch exceptions and do the same finally, is that possible to replace the bellow common code by a method in java 8? So that I could optimise all my methods who use this common code.
} catch (Exception e) {
codeA;
} finally {
codeB;
}
Depends what you do in the .... You could do something like this:
private Response method(Supplier<Response> supplier) {
try{
return supplier.get();
} catch (Exception e) {
codeA;
} finally {
codeB;
}
}
and invoke like:
public Response create() { return method(() -> { ... for create }); }
public Response update() { return method(() -> { ... for update }); }
You could wrap your payload and put it to the separate method. One thing; what do you expect to return on exception catch. This time this is null, but probably you could provide default value.
public static <T> T execute(Supplier<T> payload) {
try {
return payload.get();
} catch(Exception e) {
// code A
return null;
} finally {
// code B
}
}
Client code could look like this:
public Response create() {
return execute(() -> new CreateResponse());
}
public Response update() {
return execute(() -> new UpdateResponse());
}
This could be a generic solution.
//here describe supplier which can throw exceptions
#FunctionalInterface
public interface ThrowingSupplier<T> {
T get() throws Exception;
}
// The wrapper
private <T> T callMethod(ThrowingSupplier<T> supplier) {
try {
return supplier.get();
} catch (Exception e) {
//code A
}finally {
// code B
}
}
I'm a newbie in RxJava and I have some difficulties while constructing Observable.
My tasks are:
Query to server getExpPointsIdArrayByHouse
On server response. We get an object (RpcDeviceInfoResponse) that contain a list of integers
For each of int value a separate query to server is needed to be executed. Result of the each query is an object "ExpPoint"
Final result is a list of expPoints
What I've already done:
Observable
.defer(new Func0() {
#Override
public Object call() {
try {
return Observable.just(apiHttpClient.getExpPointsIdArrayByHouse(houseId));
} catch (IOException e) {
e.printStackTrace();
return Observable.error(e);
} catch (RightsException e) {
e.printStackTrace();
return Observable.error(e);
}
}
})
.flatMap(new Func1<RpcDeviceInfoResponse, Observable<Integer>>() {
#Override
public Observable<Integer> call(RpcDeviceInfoResponse rpcResponse) {
if (rpcResponse.getResult().size() == 0) {
errorReport(false, context.getResources().getString(R.string.error_while_getting_exp_points), "");
return null;
}
RpcDeviceInfoResponse.TaskListResult result = rpcResponse.getResult().get(0);
return Observable.from(result.getResult());
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Observer<Integer>() {
#Override
public void onCompleted() {
Log.v("onCompleted", "onCompleted");
}
#Override
public void onError(Throwable e) {
Log.v("onError", "onError");
}
#Override
public void onNext(Integer integer) {
Log.v("onNext", "onNext");
}
});
I got stuck at the point 3. I have a list of integers and for each I need to execute a separate query.
You already have an Observable<Int> so you can use flatMap
Observable.defer(
//first query
).flatMap(
// convert result to Observable<Int>
)
.flatMap(
someInt -> doSomeQuery(someInt)
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(...)
On the change "SortBy", my program will do a NetworkIO to retrieve the top movies and display them.
However, it seems that though I have done subscribeOn(Schedulers.io()), the NetworkIO MovieDB.getPopular() and MovieDB.getTopRated() in the function call in map are excuted on the main thread and I get a android.os.NetworkOnMainThreadException.
I was wondering how to make the public Movie[] call(SortBy sortBy) asynchronous.
sortObservable.map(new Func1<SortBy, Movie[]>() {
#Override
public Movie[] call(SortBy sortBy) {
try {
switch (sortBy) {
case POPULAR:
return MovieDB.getPopular(); // NETWORK IO
case TOP_RATED:
return MovieDB.getTopRated(); // NETWORK IO
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return new Movie[0];
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Movie[]>() {
#Override
public void call(Movie[] movies) {
imageAdapter.loadData(movies);
}
});
Please check if the below works for you. It uses flatMap instead of map.
sortObservable.flatMap(new Func1<SortBy, Observable<Movie[]>>() {
#Override
public Observable<Movie[]> call(SortBy sortBy) {
try {
switch (sortBy) {
case POPULAR:
return Observable.just(MovieDB.getPopular()); // NETWORK IO
case TOP_RATED:
return Observable.just(MovieDB.getTopRated()); // NETWORK IO
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return Observable.just(new Movie[0]);
}
}).subscribe(new Action1<Movie[]>() {
#Override
public void call(Movie[] movies) {
imageAdapter.loadData(movies);
}
});
From your source code on Github, it seems like you are using synchronous mode of executing requests using OkHttp. OkHttp also supports asynchronous requests and that can be preferred. Below would be the changes required in few of the methods.
run method should consume enqueue instead of execute.
Observable<String> runAsync(String url){
return Observable.create(subscriber -> {
Request request = new Request.Builder().url(url).build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onResponse(Call call, Response response) throws IOException {
subscriber.onNext(response.body().string());
}
#Override
public void onFailure(Call call, IOException e) {
subscriber.onError(e);
}
});
});
}
getApi can return an Observable<Movie[]> instead of Movie[]
public Observable<Movie[]> getApiAsync(String type){
return runAsync("http://api.themoviedb.org/3/movie/" + type
+ "?api_key=412e9780d02673b7599233b1636a0f0e").flatMap(response -> {
Gson gson = new Gson();
Map<String, Object> map = gson.fromJson(response,
new TypeToken<Map<String, Object>>() {
}.getType());
Movie[] movies = gson.fromJson(gson.toJson(map.get("results")),
Movie[].class);
return Observable.just(movies);
});
}
Finally I sort it out by myself:
sortObservable.flatMap(new Func1<SortBy, Observable<Movie[]>>() {
#Override
public Observable<Movie[]> call(SortBy sortBy) {
switch (sortBy) {
case POPULAR:
return Observable.fromCallable(() -> MovieDB.getPopular()).subscribeOn(Schedulers.io());
case TOP_RATED:
return Observable.fromCallable(() -> MovieDB.getTopRated()).subscribeOn(Schedulers.io());
default:
return Observable.fromCallable(() -> new Movie[0]).subscribeOn(Schedulers.io());
}
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Movie[]>() {
#Override
public void call(Movie[] movies) {
imageAdapter.loadData(movies);
}
});
My use case is: I have an Android app, I need to call one api, when that data becomes available call the second api.
For this I was planning to use RxAndroid, with retries.
I was able to do 1 api call with retries, but I need to chain the 2, and be able to show 2 different errors, one for call 1 not available, and one for call2 not available.
I can have the retry either on the whole chain, but I would prefer to retry each operation individually.
My code is as follow, I need to add a "callApi1()" before the callApi2, and I would prefer, like I said to have a different observable with retries.
Observable.create(new Observable.OnSubscribe<String>() {
#Override
public void call(Subscriber<? super String> subscriber) {
try {
subscriber.onNext(callApi2());
subscriber.onCompleted();
} catch (Exception e) {
subscriber.onError(e);
}
}
})
.retryWhen(new RetryWithDelay(20, 3000))
.timeout(TIME_OUT_SECONDS, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<String>() {
#Override
public void onNext(String data) {
//show something
}
....
}
You could use the Observable.concat operator e.g.
Observable<String> obs1 = Observable.create(new Observable.OnSubscribe<String>() {
#Override
public void call(Subscriber<? super String> subscriber) {
try {
subscriber.onNext(callApi1());
subscriber.onCompleted();
} catch (Exception e) {
subscriber.onError(e);
}
}
})
.timeout(TIME_OUT_SECONDS, TimeUnit.SECONDS)
.retryWhen(new RetryWithDelay(20, 3000))
.subscribeOn(Schedulers.io());
Observable<String> obs2 = Observable.create(new Observable.OnSubscribe<String>() {
#Override
public void call(Subscriber<? super String> subscriber) {
try {
subscriber.onNext(callApi2());
subscriber.onCompleted();
} catch (Exception e) {
subscriber.onError(e);
}
}
})
.timeout(TIME_OUT_SECONDS, TimeUnit.SECONDS)
.retryWhen(new RetryWithDelay(20, 3000))
.subscribeOn(Schedulers.io());
Observable<String> chained = Observable.concat(obs1,obs2);
I have a Observable that emits a list of Location objects. These Location objects have a method getUser() which returns a User (really!?). What I'm trying to do is return a new Observable that emits a list of User objects given this list of Location objects. Here's my code:
Locations Observable
private Observable<List<QBLocation>> retrieveLocations(){
QBLocationRequestBuilder getLocationsBuilder = new QBLocationRequestBuilder();
getLocationsBuilder.setPerPage(100);
getLocationsBuilder.setLastOnly();
getLocationsBuilder.setCurrentPosition(mLastLocation.getLatitude(), mLastLocation.getLongitude());
getLocationsBuilder.setSort(SortField.CREATED_AT);
getLocationsBuilder.setSort(SortField.DISTANCE, SortOrder.DESCENDING);
return Observable.create(subscriber -> {
Bundle mBundle = new Bundle();
try {
ArrayList<QBLocation> qbLocations = QBLocations.getLocations(getLocationsBuilder, mBundle);
subscriber.onNext(qbLocations);
subscriber.onCompleted();
} catch (QBResponseException e) {
subscriber.onError(e);
}
});
}
User Observable
#Override
public Observable<List<QBUser>> retrieveUsers() {
return retrieveLocations()
.flatMap(qbLocations -> Observable.from(qbLocations))
.flatMap(qbLocation -> qbLocation.getUser())
//How do I return an observable list with these users?
}
You can use toList() operator to collect you items back into a list:
#Override
public Observable<List<QBUser>> retrieveUsers() {
return retrieveLocations()
.flatMap(qbLocations -> Observable.from(qbLocations))
.flatMap(qbLocation -> qbLocation.getUser())
.toList();
}
Your code could be simplified:
#Override
public Observable<List<QBUser>> retrieveUsers() {
return retrieveLocations()
.flatMapIterable(qbLocations -> qbLocations)
.map(qbLocation::getUser)
.toList();
}
Also, I'd rewrite your retrieveLocations as follows:
return Observable.defer(() -> Observable.just(QBLocations.getLocations(getLocationsBuilder, mBundle));
Which with some static imports and exception handling becomes:
return defer(() -> {
try {
return just(getLocations(getLocationsBuilder, mBundle));
} catch(Exeption e){
return Observable.error(e);
}
});
Abstracting the error handling will make it even easier to read:
#FunctionalInterface
public interface <T,R,E> ThrowningFunction { R apply(T t) throws E; }
#FunctionalInterface
public interface <R,E> ThrowningSupplier { R get() throws E; }
public static <T,R> Func1<T,Observable<R>> protect(ThrowningFunction<T,R,?> func) {
return t -> {
try {
return Observable.just(func.apply(t));
} catch(Exception e) {
return Observable.error(e);
}
}
}
public static <R> Func0<Observable<R>> protect(ThrowningSupplier<R,?> func) {
return () -> {
try {
return Observable.just(func.get());
} catch(Exception e) {
return Observable.error(e);
}
}
}
This makes your actual getLocations return like this:
return defer(() -> protect(() -> getLocations(getLocationsBuilder, mBundle)));
Whops, this has meandered out of control...