I have an Observable<MoviesResponse>. My MovieResponse class contains a getResults() methods returning a List<Result>. This Result class has a getTitle() methods returning a String. I want to call the getTitle() methods of all my Result objects to get all the titles of my movies.
I achieved this with the code below using a foreach loop but I think there is a better way to do this by chaining RxJava operators, I just can't figure it out...
Subscription :
Observable<MoviesResponse> moviesResponseObservable = apiService.getTopRatedMoviesObservable(API_KEY);
subscription = moviesResponseObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<MoviesResponse>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(MoviesResponse moviesResponse) {
List<Result> results = moviesResponse.getResults();
for (Result r:results) {
Log.d(LOG_TAG,r.getTitle());
}
}
});
Interface :
public interface ApiService {
#GET("movie/top_rated")
Observable<MoviesResponse> getTopRatedMoviesObservable(#Query("api_key") String apiKey);
}
You can use a flatmap to transform your observable into an Observable<Result> and then use map to turn that into Observable<String>, which you can then subscribe to.
moviesReponseObservable
.subscribeOn(Schedulers.io())
.flatMapIterable(new Function<MoviesResponse, Iterable<Result>>() {
#Override
public Iterable<Result> apply(#NonNull MoviesResponse moviesResponse) throws Exception {
return moviesResponse.getResults();
}
})
.map(new Function<Result, String>() {
#Override
public String apply(#NonNull Result result) throws Exception {
return result.getTitle();
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
#Override
public void onNext(String s) {
Log.d("TAG", s);
}
/* ... */
});
I got the following error with #zsmb13 answer :
new Function : map (rx.functions.Func1) in
Observable cannot be applied to (anonymous
java.util.function.Function)reason:
no instance(s) of type variable(s) R exist so that Function conforms to Func1
Anyway this answer was very helpul I just replaced Function with Func1 and used call method.
subscription = moviesResponseObservable
.subscribeOn(Schedulers.io())
.flatMapIterable(new Func1<MoviesResponse, Iterable<Result>>() {
#Override
public Iterable<Result> call(MoviesResponse moviesResponse) {
return moviesResponse.getResults();
}
})
.map(new Func1<Result, String>() {
#Override
public String call(Result result) {
return result.getTitle();
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String s) {
Log.d(LOG_TAG, s);
}
});
Is it possible to resubscribe an Observable and get the error?
The Observable<T> retry() method resubscribes the observable but it consumes the error.
final PublishSubject<Integer> observable = PublishSubject.create();
observable
.flatMap(new Func1<Integer, Observable<Integer>>() {
#Override
public Observable<Integer> call(final Integer integer) {
if (integer % 2 == 0) {
return Observable.just(integer);
} else {
return Observable.error(new Exception("int: " + integer));
}
}
})
.retry()
.subscribe(new Action1<Integer>() {
#Override
public void call(final Integer integer) {
Timber.i("integer: %d", integer);
}
},
new Action1<Throwable>() {
#Override
public void call(final Throwable throwable) {
Timber.e(throwable, "throwable");
}
},
new Action0() {
#Override
public void call() {
Timber.w("onCompleted");
}
});
Observable
.range(0, 10)
.delay(2, TimeUnit.MILLISECONDS)
.subscribe(new Action1<Integer>() {
#Override
public void call(final Integer integer) {
observable.onNext(integer);
}
},
new Action1<Throwable>() {
#Override
public void call(final Throwable throwable) {
observable.onError(throwable);
}
},
new Action0() {
#Override
public void call() {
observable.onCompleted();
}
});
The onError part of observable is never called because .retry() consumes the error.
What you're looking for is retryWhen(). This allows you to pass a Func1 which provides you with the Throwable, that means you can place your onError logic there instead.
This is a good article.
I'm trying to get the first item from a list using RxJava. However, I don't want it to throw an error if the item doesn't exist. Instead I want to be able to handle that myself by providing a default item.
The code I created below works correctly in retrieving the first item in a list. Though I can't figure out how to incorporate .exists() into it.
api.getLibraryEntries(username)
.observeOn(AndroidSchedulers.mainThread())
.flatMap(new Func1<List<Entry>, Observable<Entry>>() {
#Override
public Observable<Entry> call(List<Entry> Entries) {
return Observable.from(Entries);
}
})
.first(new Func1<Entry, Boolean>() {
#Override
public Boolean call(Entry entry) {
return entry.getId() == id;
}
})
.subscribe(
new Action1<Entry>() {
#Override
public void call(Entry entry) {
view.showEntry(entry);
}
},
new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
view.showError(throwable.getMessage());
}
});
Any help is appreciated.
There is a firstOrDefault operator:
// ...
.firstOrDefault(yourDefaultValue, new Func1<Entry, Boolean>() {
#Override
public Boolean call(Entry entry) {
return entry.getId() == id;
}
})
// ...
I try to get and render some data like below
raw data class
#Data #AllArgsConstructor class Category {
String name;
List<String> items;
}
presentation class
#Data #AllArgsConstructor class ViewModel {
public static final int TYPE_HEADER = 0;
public static final int TYPE_ITEM = 1;
int type;
String category;
String itemName;
}
and below code is request and transform subscribed data to presentation object.
Observable.create(new Observable.OnSubscribe<Category>() {
#Override public void call(Subscriber<? super Category> subscriber) {
subscriber.onNext(new Category("1", Lists.newArrayList("", "a", "b")));
subscriber.onNext(new Category("2", Lists.newArrayList("")));// this data does not output
subscriber.onNext(new Category("3", Lists.newArrayList("c", "", "d")));
subscriber.onNext(new Category("4", Lists.newArrayList("e", "f", "")));
}
}).flatMap(new Func1<Category, Observable<ViewModel>>() {
#Override public Observable<ViewModel> call(Category category) {
// TODO make this block to one line
// 1. clean response data and transform to ViewModel
List<ViewModel> cleanedItems = Lists.newArrayList(
Observable.from(category.getItems()).filter(new Func1<String, Boolean>() {
#Override public Boolean call(String s) {
return s != null && !s.isEmpty();
}
}).map(new Func1<String, ViewModel>() {
#Override public ViewModel call(String item) {
return new ViewModel(ViewModel.TYPE_ITEM, null, item);
}
}).toBlocking().toIterable());
if (cleanedItems.isEmpty()) {
// 2. case : skip
return Observable.empty();
} else {
// 3. case : add header and cleaned data
return Observable.concat(
Observable.just(new ViewModel(ViewModel.TYPE_HEADER, category.getName(), null)),
Observable.from(cleanedItems));
}
}
}).subscribe(new Action1<ViewModel>() {
#Override public void call(ViewModel viewModel) {
// render data
System.out.println(viewModel.toString());
}
});
output
ViewModel(type=0, category=1, itemName=null)
ViewModel(type=1, category=null, itemName=a)
ViewModel(type=1, category=null, itemName=b)
ViewModel(type=0, category=3, itemName=null)
ViewModel(type=1, category=null, itemName=c)
ViewModel(type=1, category=null, itemName=d)
ViewModel(type=0, category=4, itemName=null)
ViewModel(type=1, category=null, itemName=e)
ViewModel(type=1, category=null, itemName=f)
I try to write 1, 2, 3 (in comment) statement to 1 line (or more readable way), but I had no idea.
defaultIfEmpty operator seems not to use in this case.
Do anyone have any idea ?
How about this:
public static <T, R> Func1<? super T, ? extends Observable<? extends R>> ternary(
Func1<T, Boolean> predicate,
Func1<? super T, ? extends Observable<? extends R>> ifTrue,
Func1<? super T, ? extends Observable<? extends R>> ifFalse) {
return (item) -> predicate.call(item)
? ifTrue.call(item)
: ifFalse.call(item);
}
and you use it like this:
.map(CharSequence::toString)
.distinctUntilChanged()
.flatMap(ternary(Strings::notNullOrEmpty,
(kw) -> userRelationshipApi.searchFollowing(kw, null),
(kw) -> Observable.just(selectedUserListAdapter.getUsers())))
.subscribe(...)
AFAIK you cannot fullfil it using one line.
You have to determine if stream is empty and if not emit the items again. You can use cache to avoid double computations.
Observable.create(new Observable.OnSubscribe<Category>() {
#Override
public void call(Subscriber<? super Category> subscriber) {
subscriber.onNext(new Category("1", Lists.newArrayList("", "a", "b")));
subscriber.onNext(new Category("2", Lists.newArrayList("")));// this data does not output
subscriber.onNext(new Category("3", Lists.newArrayList("c", "", "d")));
subscriber.onNext(new Category("4", Lists.newArrayList("e", "f", "")));
subscriber.onCompleted();
}
}).flatMap(new Func1<Category, Observable<ViewModel>>() {
#Override
public Observable<ViewModel> call(Category category) {
final Observable<ViewModel> cleanedItems = Observable.from(category.getItems()).filter(new Func1<String, Boolean>() {
#Override
public Boolean call(String s) {
return s != null && !s.isEmpty();
}
}).map(new Func1<String, ViewModel>() {
#Override
public ViewModel call(String item) {
return new ViewModel(ViewModel.TYPE_ITEM, null, item);
}
}).cache();
return cleanedItems.isEmpty().flatMap(new Func1<Boolean, Observable<ViewModel>>() {
#Override
public Observable<ViewModel> call(Boolean aBoolean) {
if (aBoolean) {
return Observable.empty();
} else {
return Observable.concat(
Observable.just(new ViewModel(ViewModel.TYPE_HEADER, category.getName(), null)),
cleanedItems);
}
}
});
}
}).subscribe(new Action1<ViewModel>() {
#Override
public void call(ViewModel viewModel) {
// render data
System.out.println(viewModel.toString());
}
});
But instead of using rxjava whenever possible, sometimes you can use other api to make code simpler. In you case, you can for example use stream api:
.flatMap(new Func1<Category, Observable<ViewModel>>() {
#Override
public Observable<ViewModel> call(Category category) {
List<ViewModel> items = category.getItems().stream().filter(s -> s != null && !s.isEmpty()).map(s -> new ViewModel(ViewModel.TYPE_ITEM, null, s)).collect(Collectors.toList());
if (items.isEmpty()) {
return Observable.empty();
} else {
items.add(0, new ViewModel(ViewModel.TYPE_HEADER, category.getName(), null));
return Observable.from(items);
}
}
})
You can use operator filter() with switchIfEmpty(...)
The official documents only describe how to use it in scala. http://www.playframework.com/documentation/2.1.0/ThreadPools.
Future {
// Some blocking or expensive code here
}(Contexts.myExecutionContext)
I can get the excutionContext like:
ExecutionContext myExecutionContext = Akka.system().dispatchers().lookup("my-context");
But how to add it in the code blow?
return async(
future(new Callable<String>() {
public String call() {
return doSth();
}).map(new F.Function<String,Result>() {
public Result apply(String i) {
return ok(i);
}
})
I think the answer should be:
ExecutionContext myExecutionContext = Akka.system().dispatchers().lookup("my-context");
return async(
Akka.asPromise(Futures.future(new Callable<String>() {
public String call() {
return doSth();
}
}, myExecutionContext)).map(new F.Function<String,Result>() {
public Result apply(String i) {
return ok(i);
}
})
);