I'm trying to learn RxAndroi so question is like on Title.
1 Response From Multiple Observables
I want to get 1 response from this 2 requests.
Or options number 2 I also want know how to synchronize them.
Do second request after first is completed.
Example code:
Observable<List<CategoriesTreeModel>> categoriesTreeObservable = ApiManager.getInstanceApi().getCategoriesTree();
Subscription treeSubscription = categoriesTreeObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<CategoriesTreeModel>>() {
#Override
public void onCompleted() {
Log.e("CATALOG", "getCategoriesTree() complete");
}
#Override
public void onError(Throwable e) {
Log.e("CATALOG", "getCategoriesTree() error");
}
#Override
public void onNext(List<CategoriesTreeModel> categoriesTreeModels) {
Log.e("CATALOG", "getCategoriesTree() next");
}
});
Observable<ResponseModel<CategoriesResponse>> categoriesObservable = ApiManager.getInstanceApi().getCategories();
Subscription subscription = categoriesObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<ResponseModel<CategoriesResponse>>() {
#Override
public void onCompleted() {
Log.e("CATALOG", "getCategories() complete");
}
#Override
public void onError(Throwable e) {
Log.e("CATALOG", "getCategories() error");
}
#Override
public void onNext(ResponseModel<CategoriesResponse> categoriesResponseResponseModel) {
Log.e("CATALOG", "getCategories() onNext");
}
});
EDIT:
It's a correct whay ?
Observable.zip(categoriesObservable, categoriesTreeObservable, new Func2<ResponseModel<CategoriesResponse>, List<CategoriesTreeModel>, Object>() {
#Override
public Object call(ResponseModel<CategoriesResponse> categoriesResponseResponseModel, List<CategoriesTreeModel> categoriesTreeModels) {
for (int i = 0; i < categoriesResponseResponseModel.getList().size(); i++){
Log.e("RESPONSE", "CATEGORIES: " + categoriesResponseResponseModel.getList().get(i).getCategory_id() + " NAME: " + categoriesResponseResponseModel.getList().get(i).getTranslations().getPl_PL().getName());
}
for (int i = 0; i < categoriesTreeModels.size(); i++){
Log.e("RESPONSE", "TREE: ID " + categoriesTreeModels.get(i).getId() + " CHILD: " + (categoriesTreeModels.get(i).getChildren().size() > 0 ? " has children " : "no child"));
}
return null;
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
Read something about MVP pattern in order to separate data logic from view logic.
Most straightforward solution for "Do second request after first is completed." is presented below. This pattern is not the best in the world but at least it will give you some code you can reference to.
final Observable<String> firstObservable = Observable.just("A")
.delay(5, TimeUnit.SECONDS)
.doOnNext(new Action1<String>() {
#Override
public void call(String s) {
Log.d(TAG, "call: " + s);
}
});
firstObservable
.subscribe();
final Observable<String> secondObservable = firstObservable
.flatMap(new Func1<String, Observable<String>>() {
#Override
public Observable<String> call(String s) {
return Observable.just(s + "B").delay(2, TimeUnit.SECONDS); // Make some api call
}
});
secondObservable.subscribe(new Action1<String>() {
#Override
public void call(String s) {
Log.d(TAG, "call: " + s);
}
});
Related
I'm new to RxJava, using RxJava 3 to return a response that contains a dynamic number of urls that I then need to make GET requests to and then add those responses to a list that is to be observed by the ui. Since the number of urls returned is unknown, I'm unsure how to go about this. I tried populating a list with the urls and then looping through them - not the right way to go about it & did not work:
public void getSection() {
disposables.add(repository.getContent()
.subscribeOn(Schedulers.io())
.map(new Function<Content, List<Section>>() {
#Override
public List<Section> apply(Content content) throws Throwable {
return content.getContent().getSections().stream().filter(live -> live.getSectionType().equals("shows")).collect(Collectors.toList());
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableObserver<List<Section>>() {
#Override
public void onNext(#NonNull List<Section> sections) {
contentSourcesURIs = new ArrayList<>();
for (int i = 0; i < sections.get(0).getContent().get(0).getContent().size(); i++) {
contentSourcesURIs.add(sections.get(0).getContent().get(0).getContent().get(i).getSource().getUri());
}
sectionList.postValue(sections);
}
#Override
public void onError(#NonNull Throwable e) {
Log.e(TAG, "getSection: " + e.getMessage());
}
#Override
public void onComplete() {
System.out.println(Thread.currentThread());
for(int i = 0; i < contentSourcesURIs.size(); i++){
getVideoContent(Collections.singletonList(contentSourcesURIs.get(i)));
}
}
}));
}
public void getVideoContent(List<String> contentSourcesURIs) {
for (int i = 0; i < contentSourcesURIs.size(); i++) {
getVideosContent(contentSourcesURIs.get(i));
}
}
public void getVideosContent(String contentSourcesURI) {
String authHeader = URL.POLICY_KEY;
disposables.add(repository.getVideosPlaylistContent(authHeader, contentSourcesURI)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableObserver<Video>() {
#Override
public void onNext(#NonNull Video video) {
videosList.add(video);
}
#Override
public void onError(#NonNull Throwable e) {
Log.e(TAG, "getVideosContent: " + e.getMessage());
}
#Override
public void onComplete() {
videoPlaylist.postValue(videosList);
}
}));
}
Is it possible to use RxJava to achieve what I'm trying to do? Thanks in advance!
Since in Hot Observable , we must use backpressure strategy to prevent from crash but why we use backpressure for example in the following examples which are Cold type:
Example 1
// If we do not use backpressure, the program will crash
Flowable.interval(1, TimeUnit.MILLISECONDS, Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer() {
#Override
public void accept(Long aLong) throws Exception {
// do smth
}
});
Example 2
Flowable.range(0, 1000000)
.onBackpressureBuffer()
.observeOn(Schedulers.computation())
.subscribe(new FlowableSubscriber<Integer>() {
#Override
public void onSubscribe(Subscription s) {
}
#Override
public void onNext(Integer integer) {
Log.d(TAG, "onNext: " + integer);
}
#Override
public void onError(Throwable t) {
Log.e(TAG, "onError: ", t);
}
#Override
public void onComplete() {
}
});
I am using Retrofit for the network call in my android app. Now if the response if something wrong (maybe wrong data), I do not want the onComplete to be executed. Please see the code snippet,
restClient.getService().getProjectDetail(projectId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableObserver<List<Project>>() {
#Override
public void onNext(List<Project> value) {
/*Something wrong in the data and I would like to execute onError*/
}
#Override
public void onError(Throwable e) {
handleError(e, 0, "");
hideProgressDialog();
}
#Override
public void onComplete() {
hideProgressDialog();
}
});
Thanks in advance.
Since your end-consumer can crash, the straightforward way is to catch that exception and delegate to onError:
.subscribeWith(new DisposableObserver<List<Project>>() {
#Override
public void onNext(List<Project> value) {
try {
// something that can crash
} catch (Throwable ex) {
// tell the upstream we can't accept any more data
dispose();
// do the error handling
onError(ex);
}
}
#Override
public void onError(Throwable e) {
handleError(e, 0, "");
hideProgressDialog();
}
#Override
public void onComplete() {
hideProgressDialog();
}
});
On a side note, RxJava does pretty much this in its own operators when dealing with potentially failing user functions: try-catch, cancel the source and signal through onError.
You can use flatMap. ex:
restClient.getService().getProjectDetail(projectId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(new Function<Integer, Observable<List<Project>>>() {
#Override
public Observable<List<Project>> apply(List<Project> x) {
if(validate(x)){
return Observable.error(new Exception("Response is invalid"));
}else {
return Observable.just(x);
}
}
public boolean validate(List<Project> x){
return x.size()==0;
}
})
.subscribeWith(new DisposableObserver<List<Project>>() {
#Override
public void onNext(List<Project> value) {
/*Something wrong in the data and I would like to execute onError*/
}
#Override
public void onError(Throwable e) {
handleError(e, 0, "");
hideProgressDialog();
}
#Override
public void onComplete() {
hideProgressDialog();
}
});
example other:
Observable.just(1)
.flatMap(new Function<Integer, Observable<Integer>>() {
#Override
public Observable<Integer> apply(Integer x) {
if(validate(x)){
return Observable.error(new Exception("Response is invalid"));
}else {
return Observable.just(x);
}
}
public boolean validate(Integer x){
return x==1;
}
})
.subscribe(new Observer<Integer>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onComplete() {
}
#Override
public void onError(Throwable e) {
Log.e("ERROR", e.getMessage());
}
#Override
public void onNext(Integer integer) {
Log.d("flatMap", "onNext: " + integer.toString());
}
});
You are questioning the basic behaviour of rx-java. if you want call hideProgressDialog only once. you should delete that from onError. the sequence must destroy after problem.
but if you want to get other items in onNext and avoid onError you can use this method on your observable chain:onErrorResumeNext
restClient.getService().getProjectDetail(projectId).onErrorResumeNext(/*Func1*/)
consider this method will emit List<Project> in onNext instead of onError. and onComplete would not call
as long as you use retrofit the onNext only will invoke one times. so the better solution is the first one
I try to get some data from server.
Observable<List<Countries>> backendObservable = mCountriesApi.getCountries()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
backendObservable.doOnNext(list -> Log.d(TAG, "doOnNext"));
backendObservable.doAfterNext(list -> Log.d(TAG, "doAfterNext"));
backendObservable.doOnComplete(() -> Log.d(TAG, "doOnComplete"));
backendObservable.subscribe(new Observer<List<Country>>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(List<Country> value) {
Log.d(TAG, "doOnNext"));
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
It's works, I receive data into "onNext". But methods doOnNext, doAfterNext, doOnComplete doesn't call.
What i doing wrong ?
You got it right up until observeOn, what happened? You have to chain operators on Observable because they are immutable dataflow plans:
backendObservable = backendObservable.doOnNext(list -> Log.d(TAG, "doOnNext"));
backendObservable = backendObservable.doAfterNext(list -> Log.d(TAG, "doAfterNext"));
backendObservable = backendObservable.doOnComplete(() -> Log.d(TAG, "doOnComplete"));
or more concisely
Observable<List<Countries>> backendObservable = mCountriesApi.getCountries()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(list -> Log.d(TAG, "doOnNext"));
.doAfterNext(list -> Log.d(TAG, "doAfterNext"));
.doOnComplete(() -> Log.d(TAG, "doOnComplete"));
.subscribe(...);
How can I bind two subscriptions like:
1) Retrofit&RX which converts JSON into list of strings shown in recyclerView.
restClient.getCatFacts()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<CatFactsResponse>() {
#Override
public void onCompleted() {
Log.i(TAG, "onCompleted");
}
#Override
public void onError(Throwable e) {
Log.i(TAG, "onError, " + e.getMessage());
}
#Override
public void onNext(CatFactsResponse catFactsResponse) {
catFactsList = catFactsResponse.getCatFacts();
}
});
2) And Jack Wharton's RxBinding library to react to changes made in EditText widget.
subscription = RxTextView
.textChangeEvents(editText)
.debounce(400, TimeUnit.MILLISECONDS)
.observeOn(Schedulers.newThread())
.subscribe(new Observer<TextViewTextChangeEvent>() {
#Override
public void onCompleted() {
Log.i(TAG, "onCompleted");
}
#Override
public void onError(Throwable e) {
Log.i(TAG, "onError >> " + e.getMessage());
}
#Override
public void onNext(TextViewTextChangeEvent textViewTextChangeEvent) {
Log.i(TAG, textViewTextChangeEvent.text().toString());
}
});
to get list which is dynamically filtered using EditText. Am I supposed to use classes like Subject or sth? If yes, then how should it look like? Thanks for help :)
You should use flatMap operator
subscription = RxTextView
.textChangeEvents(editText)
.debounce(400, TimeUnit.MILLISECONDS)
.map(new Func1<TextViewTextChangeEvent, String>() {
#Override
public String call(TextViewTextChangeEvent textViewTextChangeEvent) {
return textViewTextChangeEvent.text();
}
})
.flatMap(new Func1<String, Observable<CatFactsResponse>>() {
#Override
public Observable<CatFactsResponse> call(String text) {
return restClient.getCatFacts(text)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
})
.subscribe(...);