Now I am trying to understand how to use RxJava2 library with Retrofit2.
My Api service:
public interface ApiService {
#GET
Observable<String> getObservable(#Url String url);
#GET
Flowable<String> getFlowable(#Url String url);
}
and main activity:
mApiService.getObservable("https://google.com")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableObserver<String>() {
#Override
public void onNext(#NonNull String s) {
Log.d(TAG,"onNextObservable");
}
#Override
public void onError(#NonNull Throwable e) {
Log.d(TAG,"onErrorObservable");
}
#Override
public void onComplete() {
Log.d(TAG,"onCompleteObservable");
}
});
mApiService.getFlowable("https://google.com")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new FlowableSubscriber<String>() {
#Override
public void onSubscribe(#NonNull Subscription s) {
Log.d(TAG,"onSubscribeFlowable");
}
#Override
public void onNext(String s) {
Log.d(TAG,"onNextFlowable");
}
#Override
public void onError(Throwable t) {
Log.d(TAG,"onErrorFlowable");
}
#Override
public void onComplete() {
Log.d(TAG,"onCompleteFlowable");
}
});
In my log I see:
onNextObservable
onSubscribeFlowable
onCompleteObservable
Why I don't see onNextFlowable? I can't get a response from Flowable. Maybe there is a more compact method of writing code?
Calling a URL is something that return only one result or failed (more like Single or Maybe). Observable work too. But Flowable doesn't make sens here.
Anyway, try to add s.request(1) in onSubscribe(...) of the Flowable.
Flowable has the notion of "backpressure". You have to ask for data.
Related
While inflating Android view I load a bunch of stuff from the background thread and inflate some views based on network responses. So I am trying to defer some of that tasks using RxJava like this
Single.fromCallable(() -> savedInstanceState)
.delay(50,TimeUnit.MICROSECONDS,AndroidSchedulers.mainThread())
.flatMapCompletable(this::loadVideos)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CompletableObserver() {
#Override
public void onSubscribe(Disposable d) {
Timber.d("on Subscribe");
}
#Override
public void onComplete() {
Timber.d("on onComplete");
}
#Override
public void onError(Throwable e) {
Timber.d("on onError");
}
});
And the loadVideos method is like this:
private Completable loadVideos(Bundle savedInstanceState) {
return Completable.fromAction(() -> {
videoPresenter.loadVideos(savedInstance);
});
}
What I am finding is onSubscribe() certainly gets called, but method videoPresenter.loadVideos never gets called. Would appreciate if anyone can point out what I am doing wrong.
For my testing, I implemented following test that seems to work...
public class DelayTest {
public static void main(String[] args) throws InterruptedException {
Single.fromCallable(() -> "hello")
.delay(50, TimeUnit.MICROSECONDS)
.flatMapCompletable(new Function<String, CompletableSource>() {
#Override
public CompletableSource apply(String s) throws Exception {
return getFlatMapCompletable();
}
})
.subscribe(new CompletableObserver() {
#Override
public void onSubscribe(Disposable d) {
System.out.println("In onSubscribe");
}
#Override
public void onComplete() {
System.out.println("In onComplete");
}
#Override
public void onError(Throwable e) {
System.out.println("In onError");
}
});
Thread.sleep(200L);
}
private static Completable getFlatMapCompletable() {
return Completable.fromAction(new Action() {
#Override
public void run() throws Exception {
System.out.println("In flatmapCompletable");
}
});
}
}
Delay operator in RxJava is executed in another thread. So the rest of the execution does not wait for this one to be finished.
Take a look to some examples https://github.com/politrons/reactive/blob/master/src/test/java/rx/observables/utils/ObservableDelay.java
I want to send multiple requests over the network and this tutorial
helped but i'm stuck at the latter part .
seems i'm expected to return a value(OrderValues) from onSubscribe,onNext,....
since apply function returns a value. But ....,onNext returns void by default.
Any help?Here is my piece of code
Observable<Restaurant> orderRestaurant= IdentityClient.getAPIService()
.getRestaurantById(restaurantId)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
Observable<Menu> orderMenu= IdentityClient.getAPIService()
.getMenuById(menuId)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
Observable<User> orderUser= IdentityClient.getAPIService()
.getUserById(userId)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
Observable<OrderValues> combineValues=Observable.zip(orderRestaurant,
orderMenu, orderUser,
new Function3<Restaurant, Menu, User, OrderValues>() {
#Override
public OrderValues apply(Restaurant restaurant, Menu menu, User user)
throws Exception {
return new OrderValues(restaurant,menu,user);
}
I get an error here "cannot resolve method 'subscribe anonymous
org.reactivestreams.Subscriber(....OrderValues)
}).subscribe(new Subscriber<OrderValues>() {
#Override
public void onSubscribe(Subscription s) {
}
#Override
public void onNext(OrderValues orderValues) {
}
#Override
public void onError(Throwable t) {
}
#Override
public void onComplete() {
}
});
I'm assuming that you are using RxJava 2.
Use Observer instead of Subscriber. And also do not assign the result to a new Observable (you called it combineValues).
private void myMethod() {
Observable.zip(orderRestaurant, orderMenu, orderUser, new Function3<Restaurant, Menu, User, OrderValues>() {
#Override
public OrderValues apply(#NonNull Restaurant restaurant, #NonNull Menu menu, #NonNull User user) throws Exception {
return new OrderValues(restaurant, menu, user);
}
}).subscribe(new Observer<OrderValues>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(OrderValues orderValues) {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
}
}
I am new to Rx world and try to implement my AutoCompleteTextView with RxJava, RxBinding and Retrofit 2.
Here's what I come up with which is troublesome: (Maybe I'm not doing it in the right way.)
I have an AutoCompleteTextView and here I created my subscribtion and observables:
subcription = RxTextView.textChangeEvents(clearableEditText)
.skip(1)
.debounce(400, TimeUnit.MILLISECONDS)
.map(new Func1<TextViewTextChangeEvent, String>() {
#Override
public String call(TextViewTextChangeEvent textViewTextChangeEvent) {
return textViewTextChangeEvent.text().toString();
}
})
.filter(new Func1<String, Boolean>() {
#Override
public Boolean call(String s) {
return s.length() > 2;
}
})
.flatMap(new Func1<String, Observable<List<String>>>() {
#Override
public Observable<List<String>> call(String text) {
return searchService.getAutoCompleteTermsObservable(text)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<String>>() {
#Override
public void onCompleted() {
Log.d("rx", "oncomplete");
}
#Override
public void onError(Throwable e) {
Log.e("rx", e.toString());
}
#Override
public void onNext(List<String> strings) {
Log.d("rx", strings.size()+"");
autoAdapter = new ArrayAdapter<>(MainActivity.this,
android.R.layout.simple_dropdown_item_1line, strings);
clearableEditText.setAdapter(autoAdapter);
clearableEditText.showDropDown();
}
});
My issue is when I set my EditText with setText() method, it triggers dropdown. For example it does that when I set the word from AutoCompleteTextView's dropdown and when I set it with voice input. Is there a way to avoid triggering onTextChanged when I set it manually? Or how can I fix that?
You could indeed use unsubscribe() but depending on how you set the value, you also use skipWhile. Here is an example:
public void handleTextChanges() {
final String textFromSource = "an";
Observable.fromArray("a", "an", "ancestor")
.skipWhile(new Predicate<String>() {
#Override
public boolean test(String value) throws Exception {
return textFromSource.contains(value);
}
})
.subscribe(new Consumer<String>() {
#Override
public void accept(String value) throws Exception {
Log.d("Rx", value);
}
});
}
This will only consume ancestor (example is RxJava2, but the same methods exist). Any subsequent values, even if they match an, will be consumed. You could use filter if you always want to do the check like this
I've an Observable something like this:
#GET("endpoint")
Observable<Something> getSomething();
and Subscriber like this
Subscriber<Something> somethingSubscriber = new Subscriber<Something>() {
public void onCompleted() {
}
public void onError(Throwable e) {
//handle exceptions
}
public void onNext() {
//do something
}
In my OnClickListener associated with a button, i make a subscription
getSomething()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(somethingSubscriber);
If i don't have an internet connection, onError is called and i do some exception handling. when I press the button again (assume i want to retry), the callback methods do not get called.
I want that onNext / onError callbacks get called everytime I press the button.
There is extention for RxJava. It has a lot of "cool tools", but for handling retrofit errors you can use ResponseOrError class.
So in you case it would looks like:
final PublishSubject<Object> clickSubject = PublishSubject.create();
final Observable<ResponseOrError<Something>> responseOrErrorObservable = clickSubject
.flatMap(new Func1<Object, Observable<ResponseOrError<Something>>>() {
#Override
public Observable<ResponseOrError<Something>> call(Object o) {
return getSomething()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.compose(ResponseOrError.<Something>toResponseOrErrorObservable());
}
})
.replay(1)
.refCount();
final Observable<Throwable> error = responseOrErrorObservable
.compose(ResponseOrError.<Something>onlyError())
.subscribe(new Action1<Segment>() {
#Override
public void call(Throwable throwable) {
// what to do on error, some toast or what ever yu need
}
});
final Observable<UserInfoResponse> success = responseOrErrorObservable
.compose(ResponseOrError.<Something>onlySuccess())
.subscribe(new Action1<Something>() {
#Override
public void call(Something some) {
// code what to do on success
}
});
And now, into onClick you just need to put clickSubject.onNext(null)
.replay(1).refCount(); needed because there are 2 Observables that uses responseOrErrorObservable, so without it retrofit request will "happens" two times.
You are reusing the same Subscriber. Once you get the onError or a result (so it completes) the subscriber is unsubscribed. Try to pass every time a new subscriber.
use this code
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
getSomething()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Something>() {
#Override
public void call(Something something) {
//do something
}
},
new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
//handle exceptions
}
},
new Action0() {
#Override
public void call() {
}
});
}
});
Addition
or
replace this
Subscriber<Something> somethingSubscriber = new Subscriber<Something>() {
public void onCompleted() {
}
public void onError(Throwable e) {
//handle exceptions
}
public void onNext() {
//do something
}
};
to
Subscriber<String> somethingSubscriber = new Subscriber<String>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String s) {
}
};
In my Case onNext() and onError() methods are not getting called because of my model class wrong parsing, I was taking a double object as Integer so NumberFormatException was thrown and nothing was happening after getting the result from retrofit.
Have the following snippet:
Log.d("#######", Thread.currentThread().getName());
RxSearchView.queryTextChangeEvents(searchView)
.debounce(400, TimeUnit.MILLISECONDS,Schedulers.newThread())
.flatMap(new Func1<SearchViewQueryTextEvent, Observable<GifsData>>() {
#Override
public Observable<GifsData> call(SearchViewQueryTextEvent txtChangeEvt) {
return RestWebClient.get().getSearchedGifs(txtChangeEvt.queryText().toString(),"dcJmzC");
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<GifsData>() {
#Override
public void onCompleted() {
Log.d("#######","onCompleted searchGifs");
}
#Override
public void onError(Throwable e) {
Log.d("#######",e.toString());
}
#Override
public void onNext(GifsData gifsData) {
mainFragmentPresenterInterface.displaySearchedGifsList(gifsData);
}
});
}
No matter what i try i keep getting the following error:
java.lang.IllegalStateException: Must be called from the main thread. Was: Thread[RxNewThreadScheduler-2,5,main]
Probably have spend close to an hour on this..Haven't been able to figure out what is the issue. Even tried matching my snippet to the following link:
Combine RxTextView Observable and Retrofit Observable
No luck. Can someone point out what is wrong here?
Thanks.
Reason of error: You are subscribing result on background thread and you are accessing View in stream on background thread. Here I have invoked RestWebClient.get().getSearchedGifs(txtChangeEvt.queryText().toString(),"dcJmzC").subscribeOn(Schedulers.newThread());on background scheduler .Please try this it will work for you:
RxSearchView.queryTextChangeEvents(mSearchView)
.debounce(400, TimeUnit.MILLISECONDS)
.flatMap(new Func1<SearchViewQueryTextEvent, Observable<String>>() {
#Override
public Observable<String> call(SearchViewQueryTextEvent txtChangeEvt) {
return Observable.just(txtChangeEvt.queryText().toString()).subscribeOn(AndroidSchedulers.mainThread());
}
})
.flatMap(new Func1<GifsData, Observable<String>>() {
#Override
public Observable<GifsData> call(String txtChangeEvt) {
return RestWebClient.get().getSearchedGifs(txtChangeEvt,"dcJmzC").subscribeOn(Schedulers.newThread());
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<GifsData>() {
#Override
public void onCompleted() {
Log.d("#######","onCompleted searchGifs");
}
#Override
public void onError(Throwable e) {
Log.d("#######",e.toString());
}
#Override
public void onNext(GifsData gifsData) {
Log.d("#######", gifsData);
}
});
Let me know if it helps
Operator debounce by default uses computation scheduler, you need to change it to main thread (because you work with UI only on main).
Next thing is to schedule network request to be executed on io scheduler.
(we are using only one subscribeOn now).
And again observing results on main thread to inreact with UI.
RxSearchView.queryTextChangeEvents(searchView)
.debounce(400, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
.flatMap(new Func1<SearchViewQueryTextEvent, Observable<GifsData>>() {
#Override
public Observable<GifsData> call(SearchViewQueryTextEvent txtChangeEvt) {
return RestWebClient.get()
.getSearchedGifs(txtChangeEvt.queryText().toString(),"dcJmzC")
.subscribeOn(Schedulers.io());
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<GifsData>() {
#Override
public void onCompleted() {
Log.d("#######","onCompleted searchGifs");
}
#Override
public void onError(Throwable e) {
Log.d("#######",e.toString());
}
#Override
public void onNext(GifsData gifsData) {
mainFragmentPresenterInterface.displaySearchedGifsList(gifsData);
}
});