i don´t get the right way. The Problem is, that i have two Retrofit API´s and Services and need two wait for response and make a decision and based on this other calls.
Example:
APIx:
#GET("xyz/{id}/exists")
Observable<Exists> checkObjectExists(#Path("id") String id);
#POST("xyz/")
Observable<Object> addObjectA(#Body Object a);
APIy:
#POST("abc/{id}/blabla")
Observable<Object> addObjectB(#Path("id") String id, #Body Object b);
Now the Use case:
I need to do a Request if some Object exists like:
serviceA.exists(id).flatMap(exists -> if(exists) ...
if Exists is true then i need to call
serviceB.addObjectB(b)
Then the first flow is finish.
if Exists is false i need to call
serviceA.addObject(a)
and then when the i get a Success in onNext i need to call
ServiceB.addObject(b)
again. But i really dont get the Chain with RxJava and Retrofit. I can handle this stuff with a lot lines of Code with something like this:
private void _checkExists() {
ServiceA serviceA= ServiceA.create();
serviceA.checkObjectExists(id)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<Exists>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
Timber.d("Something get Wrong");
}
#Override
public void onNext(Exists exists) {
if(exists) {
_addObjectB(b);
} else {
addobjectA(a);
}
}
});
}
private void addObjectA(Object a) {
ServiceA serviceA= ServiceA.create();
serviceA.addObjectA(a)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<Object>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
Timber.d("Something get Wrong");
}
#Override
public void onNext(Object a) {
addObjectB();
}
});
}
private void addObjectB() {
ServiceB serviceB= ServiceB .create();
serviceB.addObjectB(id, b)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Object>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
Timber.d("Something get Wrong");
}
#Override
public void onNext(Object b) {
Timber.d("Everything is ok");
}
});
}
I tried to chain everything together with flatMap and so on, but it works not correctly in the Chain and also the ErrorHandling was strange, because when i get some Error from the Backend my app will Crash, this is why i add every call the Action3 with OnComplete,OnError,OnNext. Is there a possibility to do this with less code?
Thanks
What have you try with flatMap ?
regarding your code, it seams that you call checkObjectExists then, depending of the result addObjectA then addObjectB or only addObjectB.
So, it can be achieve like this :
ServiceA serviceA= ServiceA.create();
ServiceA serviceB= ServiceB.create();
serviceA.checkObjectExists(id)
.flatMap(exists -> {
if(exists) {
return serviceB.addObjectB(id, b)
} else {
return serviceA.addObjectA(id, a).flatMap(newA -> serviceB.addObject(id, newA);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe();
(as it miss some part of your code, it's hard to undertand from where a, b and other variables come from.
As you can see, it only rely on flatMap and may be closed to what you already try.
Related
I'm new to Android development and am currently trying to make a simple MVC app that works with Rest API.
API calls are made without using Retrofit, although this is not so important. The main catch is that using Observable with debounce and SwitchMap I still get too many API calls (and the extra ones should be discarded). The function is called when text is entered (EditText Listener with TextWatcher). And when administered continuously without delay word, every symbol processed by the server and should only be administered when not within 600 milliseconds. Please help me.
public Observable<String> getObservable(final String s){
return Observable
.create(new ObservableOnSubscribe<String>() {
#Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
emitter.onNext(model.translateText(s));
}
});
}
public Observer<String> observer = new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String s) {
mainView.hideProgress();
mainView.showResult(s);
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
};
public void onEditTextChange(String textForTranslate){
mainView.showProgress();
getObservable(textForTranslate)
.debounce(600,TimeUnit.MILLISECONDS)
.switchMap(new Function<String, ObservableSource<String>>() {
#Override
public ObservableSource<String> apply(String s) throws Exception {
return Observable.just(s);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}
You are creating a new observable every time a character is typed. So multiple observers are created with each having separate debounce (time throttle) and switch but they are not reused. Instead you create a new observable whenever text changes and start rx chain on it.
You need to create a single PublishSubject
private final PublishSubject<String> querySubject = PublishSubject.create();
that emits entered text/query whenever text is changed. Use it in your callback:
public void onEditTextChange(String textForTranslate) {
querySubject.onNext(textForTranslate);
}
And in your main function, subscribe to observable:
querySubject
.debounce(600, TimeUnit.MILLISECONDS)
.distinctUntilChanged()
.switchMap(new Function<String, ObservableSource<String>>() {
#Override
public ObservableSource<String> apply(String s) throws Exception {
// perform api call or any other operation here
return Observable.just(s);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
Debounce operator emits single item only after given time (600 ms) has passed. It ignores items if current item is being processed and given time has not passed.
distinctUntilChanged helps in reducing processing of same query.
I'm dealing with the following api call https://pokeapi.co/api/v2/pokemon/, the problem is that it seems quite slow and each entry has itself several endpoints. Is it possible to update ListView asynchronously while making the api calls? I feel that it will probably be something like using the endpoint's next and previous keys and triggering listener events that update the Listview from the AsyncTask's doInBackground or onProgressUpdate methods. I'd appreciate any help, I feel that I have the beginning of an idea, but I need help finishing the thought.
You can definitely implement it through AsyncTask but I would rather suggest a solution using RxJava.
You can implement RxJava Chaining.
Sharing a code snippet how you can make a chaining call using RxJava.
private void fetchHackerNewsStoriesChaining() {
StoriesApiInterface storiesApiInterface = HackerNewsApiClient.getStoriesApiInterface();
storiesApiInterface.getTopStories()
.flatMapIterable(new Function<JsonArray, Iterable<?>>() {
#Override
public Iterable<?> apply(JsonArray jsonArray) throws Exception {
Log.d("Count", ""+jsonArray.size());
return jsonArray;
}
})
.flatMap(new Function<Object, ObservableSource<SingleStoryModelResponse>>() {
#Override
public ObservableSource<SingleStoryModelResponse> apply(Object newsId) throws Exception {
return HackerNewsApiClient.getStoryDetailsApiInterface().getNewsStoryDetail(((JsonElement) newsId).getAsLong())
.subscribeOn(Schedulers.io());
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Observer<SingleStoryModelResponse>() {
#Override
public void onSubscribe(Disposable d) {
progressBar.setVisibility(View.VISIBLE);
}
#Override
public void onNext(SingleStoryModelResponse singleStoryModelResponse) {
adapterNewsList.addNewsItem(singleStoryModelResponse);
}
#Override
public void onError(Throwable e) {
Log.d("Hacker News", e.getMessage());
}
#Override
public void onComplete() {
progressBar.setVisibility(View.GONE);
}
});
}
In my Presenter i have a method which gets some list from DataHolder:
disposable.add(dataHolder.getMonthOfAttractions(monthInAdvance)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSingleObserver<Map<String, List<Attraction>>>() {
#Override
public void onSuccess(Map<String, List<Attraction>> stringListMap) {
}
#Override
public void onError(Throwable e) {
}
}));
Then, in my DataHolder I'm checking if my list isn't null. If true, returns my list, if false it downloads this list from server :
public Single<Map<String, List<Attraction>>> getMonthOfAttractions(int monthInAdvance) {
Map<String, List<Attraction>> monthOfAttractions = monthlyAttractionsMap.get(monthInAdvance);
if (monthOfAttractions != null)
return Single.fromCallable(() -> monthOfAttractions);
else
return apiGetMonthOfAttractions(monthInAdvance);
The problem is with apiGetMonthOfAttractions method. I dont know how to correctly implement this method to return value to my Presenter.
I've tried something like:
private Single<Map<String, List<Attraction>>> apiGetMonthOfAttractions(int monthInAdvance) {
cnkRetrofitProvider.getApiInterface().getAttractions(monthInAdvance)
.subscribeWith(new CnkApiObserver<AttractionListResponse>() {
#Override
public void onSucceeded(AttractionListResponse result) {
result.getMonthOfAttractions();
}
#Override
public void onFailed(Error error) {
}
});
}
But in this case i have "missing return statement" and I'm out of ideas how to implement it. I'm begging to learn RxJava, so be understanding.
Please help :)
EDIT:
This is what how my Retrofit getAttractions() method looks like:
public interface CnkApiInterface {
#GET("pl/front-api/{dateFrom}/{dateTo}")
Single<AttractionListResponse> getAttractions(#Path("dateFrom") String dateFrom, #Path("dateTo") String dateTo);}
This is what you are after:
private Single<Map<String, List<Attraction>>> apiGetMonthOfAttractions(int monthInAdvance) {
return cnkRetrofitProvider.getApiInterface()
.getAttractions(monthInAdvance)
.flatMap(attractionListResponse -> Single.just(attractionListResponse.getMonthOfAttractions()));
}
I thing just try to do something like (it only depends what does your cnkRetrofitProvider.getApiInterface().getAttractions(monthInAdvance) returns)
private Single<Map<String, List<Attraction>>> apiGetMonthOfAttractions(int monthInAdvance) {
return cnkRetrofitProvider.getApiInterface().getAttractions(monthInAdvance)
}
should do the trick
You can always just map the result to List<Attraction> so #wojech_maciejewski s answer still holds, you jast need to add a mapping function.
private Single<Map<String, List<Attraction>>> apiGetMonthOfAttractions(int monthInAdvance) {
return cnkRetrofitProvider.getApiInterface().getAttractions(monthInAdvance)
.map(atractions -> /* convert to List<Attraction> here */)
}
I'm having a cast problem inside the subscribe method, i don't know why the new Observer is giving this issue.
Observable<GradeModel> getGrade = retrofit
.create(GradeService.class)
.getGrade()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.map(model -> {
// transform model
DecimalFormat grades = (DecimalFormat) model.getGrades();
return grades;
})
.subscribe(new Subscriber<DecimalFormat>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
Log.i(TAG, "onError method of observer");
}
#Override
public void onNext(DecimalFormat grades) {
mainPresenter.setListGrades(grades);
}
});
Required:
rx.Observable
Found:
rx.Subscription
Up until .subscribe(...) it is an Observable. However after subscribing it returns a Subscription instance which can be used to cancel the subscription.
The exception occurs because you are casting this Subscription back into an Observable although they are completely unrelated.
Without knowing your intentions with that variable it is hard to say what the correct code would be.
I'm using Retrofit and RxJava to perform some background tasks. Code looks like this:
public class MyLoader{
public Observable<MyData> getMyData(){
return setupHelper().flatMap(new Func1<MyHelper, Observable<MyData>>() {
#Override
public Observable<MyData> call(MyHelper myHelper) {
return queryData(myHelper);
}
});
}
private Observable<MyData> queryData(MyHelper myHelper){
...
}
private Observable<MyHelper> setupHelper(){
return Observable.create(new Observable.OnSubscribe<MyHelper>() {
#Override
public void call(final Subscriber<? super MyHelper> subscriber) {
try{
MyHelper helper = makeRetrofitCall();//Using Retrofit blocking call to get some data
subscriber.onNext(helper);
subscriber.onCompleted();
}catch(RetrofitError e){
subscriber.onError(e)
}
}
}
}
}
This fails with RetrofitError, due to NetworkOnMainThread Exception at this line:
MyHelper helper = makeRetrofitCall();//Using Retrofit blocking call to get some data
Subscribing to my Observable:
myLoader.getMyData()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<MyData>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(MyData inventory) {
}
});
According to Rx documentation flatMap doesn't operate on any background thread. My question is how do I ensure that the whole getMyData() method runs in background.
I just add observeOn(Schedulers.newThread()) before flatMap and it works!
This moves just one step in the pipeline to the background thread:
Observable<Integer> vals = Observable.range(1,10);
vals.flatMap(val -> Observable.just(val)
.subscribeOn(Schedulers.computation())
.map(i -> intenseCalculation(i))
).subscribe(val -> System.out.println(val));
Originally answered here:
https://stackoverflow.com/a/35429084/2908525
There is a good chance when you create the MyLoader object in the main thread the Observable.create be executed as well (or maybe somewhere else before in your code (?) ). If it's so, the .subscribeOn(Schedulers.io()) will have no effect on changing the thread.
You can try wrap the .create() with a .defer() to make sure the Observable is created only when it's subscribed.
e.g. defer(() -> create(....))