RxJava2 Android Schedulers.io() and Schedulers.newThread() - android

I'm using RxJava2 Android Networking for network call. The problem I'm facing is when I'm trying to hit the API through Schedulers.io() sometimes it does not give any response whereas when I tried hitting the API with Schedulers.newThread() it always gives a response
.subscribeOn(Schedulers.newThread()) AND .subscribeOn(Schedulers.io())

I think there are a lot of reasons if you didn't receive a response from the beck-end server.
Bellow i will post-you some example code for making network-call via rxJava2.
Note that the job is executed via Schedulers.io() and result is observerd on the main-thread. Note that getCoinList() should return some observable
service.getCoinList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<CoinList>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(CoinList coinList) {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});

Related

Android paging works not as expected

I tried to use paging in my project. Unfortunately, it works not as I expected. I expected that the liveDataObserver will work after callBack.onResult.But in fact, the liveDataObserver observes immediately when the loadInitial finished.The callBack works later, and didn't post data to the observer.
The code:
First I wrote a class extend PageKeyedDataSource and interface SingleCreator
public class MyPagingDataSource<T> extends PageKeyedDataSource<Integer, T>
public interface SingleCreator<T> {
SingleSubscribeProxy<Page<T>> createSingle(int page, int pageSize);
}
Then the constructor of MyPagingDataSource:
public MyPagingDataSource(SingleCreator<T> singleCreator) {
this.singleCreator = singleCreator;
}
And override loadInitial:
#Override
public void loadInitial(#NonNull LoadInitialParams<Integer> params, #NonNull LoadInitialCallback<Integer, T> callback) {
singleCreator.createSingle(1, params.requestedLoadSize)
.subscribe(new SingleObserver<Page<T>>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onSuccess(Page<T> ts) {
callback.onResult(ts.list, ts.pageNumber, ts.total, ts.pageNumber - 1, ts.pageNumber + 1);
Timber.d("registerLiveData" + ts.list.size());
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
});
try {
//when I add this, observer will work after callback
//And if not observer works before callback.onResult
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Then the datasourceFactory is just newInstanced in viewModel:
public LiveData<PagedList<MyData>> page = loadPageData();
public LiveData<PagedList<MyData>> loadPageData() {
return new LivePagedListBuilder<>(new DataSource.Factory<Integer, MyData>() {
#Override
public DataSource<Integer, MyData> create() {
return new HBPagingDataSource<>((page, pageSize) -> loadPageSingle(page, pageSize));
}
}, 2).build();
}
the single
private SingleSubscribeProxy<Page<MyData>> loadPageSingle(int pageNum, int pageSize) {
return mModel.loadMyDates(pageNum, pageSize)
.doOnError(Throwable::printStackTrace)
.as(autoDisposable(this));
}
at fragment
mViewModel.page.observe(this, myDatas -> {
Timber.d("registerLiveData%s", myDatas.size());
myAdapter.submitList(myDatas);
});
Maybe related things:
I wrote subscribeOn and observeOn in retrofit's callAdapter
The viewModel is a scopeProvider since I'm using autoDispose
I tried some example in github. And it seems, the setValue for pageLivedata is always work after loadInitial. In this case, how can I use single?
It's seems solved.
The error is because schedule the thread using rxjava.
It makes single and datasource work in different thread.
In this case, callback onResult run after the observer.
So, I updated the callAdapter where I wrote subscribeOn and observeOn for single.
Filter by className when It's Page class, it won't do subscribeOn and observeOn.
Now the conclusion is, let paging handle the thread.

How can I send only last requst with Rx and Retrofit?

I've got an EditText view and TextWatcher for it, in onTextChanged method I have to requst server for result with query from EditText field.
In my presenter I use rx for that, but i need to delay search until user's input ends. At this moment i've got this:
service.getData(query)
.delaySubscription(REQUEST_DELAY_FROM_SERVER, TimeUnit.MILLISECONDS, Schedulers.io())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
data-> {
getViewState().showData(data);
},
error -> {
Log.e(this.getClass().getSimpleName(), error.getMessage(), error);
}
);
But delaySubscription does not work as desired. It collects all call and after delay sends every of them. I have to do same as if I had used handler.postDelayed(), when only once request will be send.
Edit 2:
The saple of a presenter in RxJava2
class Presenter {
private PublishSubject<String> queryPublishSubject = PublishSubject.create();
public Presenter() {
queryPublishSubject
.debounce(1000, TimeUnit.MILLISECONDS)
// You might want to skip empty strings
.filter(new Predicate<CharSequence>() {
#Override
public boolean test(CharSequence charSequence) {
return charSequence.length() > 0;
}
})
// Switch to IO thread for network call and flatMap text input to API request
.observeOn(Schedulers.io())
.flatMap(new Function<CharSequence, Observable<...>() {
#Override
public Observable<...> apply(final CharSequence charSequence) {
return ...; // Call API
}
})
// Receive and process response on Main thread (if you need to update UI)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(...);
}
public void onSearchTextChanged(String query) {
queryPublishSubject.onNext(query);
}
}
Edit 1:
The same code in RxJava 1:
class Presenter {
private PublishSubject<String> queryPublishSubject = PublishSubject.crate();
public Presenter() {
queryPublishSubject
.debounce(1000, TimeUnit.MILLISECONDS)
// You might want to skip empty strings
.filter(new Func1<CharSequence, Boolean>() {
#Override
public Boolean call(CharSequence charSequence) {
return charSequence.length() > 0;
}
})
// Switch to IO thread for network call and flatMap text input to API request
.observeOn(Schedulers.io())
.flatMap(new Func1<CharSequence, Observable<...>() {
#Override
public Observable<...> call(final CharSequence charSequence) {
return ... // Call API
}
})
// Receive and process response on Main thread (if you need to update UI)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(...);
}
public void onSearchTextChanged(String query) {
queryPublishSubject.onNext(query);
}
}
Initial answer (with RxBinding and RxJava 1)
The correct answer is to use Debounce, but besides that there are some other tricks you might find useful
textChangeListener = RxTextView
.textChanges(queryEditText)
// as far as I know, subscription to textChanges is allowed from Main thread only
.subscribeOn(AndroidSchedulers.mainThread())
// On subscription Observable emits current text field value. You might not need that
.skip(1)
.debounce(1000, TimeUnit.MILLISECONDS)
// You might want to skip empty strings
.filter(new Func1<CharSequence, Boolean>() {
#Override
public Boolean call(CharSequence charSequence) {
return charSequence.length() > 0;
}
})
// Switch to IO thread for network call and flatMap text input to API request
.observeOn(Schedulers.io())
.flatMap(new Func1<CharSequence, Observable<...>() {
#Override
public Observable<...> call(final CharSequence charSequence) {
return ... // Call API
}
})
// Receive and process response on Main thread (if you need to update UI)
.observeOn(AndroidSchedulers.mainThread())
I have something similar for an address research combining with RxAndroid could give something like that :
RxTextView.textChanges(searchEditText)
.debounce(100, TimeUnit.MILLISECONDS)
.subscribe(....);
The debounce operator will wait in this case that the observable stop to emit for 100ms before emitting the next value.
Try using debounce instead. For eg. code below look for changes in a TextView and do something when there is a change but with a debounce of 100 ms
RxTextView
.textChanges(queryEditText)
.debounce(100, TimeUnit.MILLISECONDS)
.doOnNext(new Action1<CharSequence>() {
#Override
public void call(CharSequence charSequence) {
}
})
.subscribe();

RxJava: Continue next iteration even if error occurs

I'm using RxSearchView to emit out the results of a search query from an API to a recyclerview. However, if one of those query fails, onError() is called(which is expected) but the subscription as a whole is also canceled. Subsequent queries are not executed at all.
How should i modify the code so that the call to onError() is prevented when a query fails and the next incoming queries are executed normally?
Here's a code snippet:
subscription = RxSearchView.queryTextChanges(searchView)
.debounce(500, MILLISECONDS)
.filter(charSequence -> !TextUtils.isEmpty(charSequence))
.map(CharSequence::toString)
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(Schedulers.io())
.switchMap(query -> apiService.getSearchResults(query))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<SearchResponse>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(SearchResponse searchResponse) {
if (searchResponse.getStatus().equals("OK")) {
//update Adapter
} else {
//update error views
}
}
});
P.S: I am using switchMap() so that the results of old queries are ignored, if the results of new query has arrived.
You have to handle this error and return an object instead. You can do it, for example, by using onErrorResumeNext operator with apiService.getSearchResults(query) call. What you are going to return - depends on you, you can even return null if you want, but better to create some wrapper which can carry both response status flag and normal response if received.
Something like:
subscription = RxSearchView.queryTextChanges(searchView)
.debounce(500, MILLISECONDS)
.filter(charSequence -> !TextUtils.isEmpty(charSequence))
.map(CharSequence::toString)
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(Schedulers.io())
.switchMap(query -> apiService
.getSearchResults(query)
.onErrorResumeNext(error -> null)
)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<SearchResponse>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(SearchResponse searchResponse) {
if (searchResponse != null && searchResponse.getStatus().equals("OK")) {
//update Adapter
} else {
//update error views
}
}
});
Of course, this is naive example with using null, in reality you need to write error handling logic. Better to return wrapper, because if using RxJava 2, then it doesn't support null.

RxAndroid: Is there a clean way to get a Subscriber object from an Observable?

So from my view model I call my Observable in another class:
getAuthentication.kickoff()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<RxOkHttpResponse>() {
#Override
public final void onCompleted( ) {
getAlbums();
}
#Override
public final void onError(Throwable e) {
userMsgHandler.showToast(mParent,mParent.getString(R.string.error_cannot_authenticate));
}
#Override
public final void onNext(RxOkHttpResponse response) {
mSubscription = response.subscription;
}
});
So this call obviously returns an Observable. So I'm wondering what the cleanest way to call subscriber.unsubscribe() on this Observable that is returned or if there even is a way (Right now I return it in onNext -- but I'm not happy bout that)
So is there a way to format the code to store .subscribe() in a member variable:
.observeOn(AndroidSchedulers.mainThread())
mSubscriber = .subscribe(new Subscriber<RxOkHttpResponse>() {
...
I'm using v 1.1.0
The answer depends on whether you're using RxJava 1 or 2. In case of RxJava 1 subscribe() will return Subscription instance which you can then call unsubscribe() on. However for RxJava 2 I believe you need to add onSubscribe()
This is the syntax I was looking for:
mSubscriber = ( getAuthentication.kickoff()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
).subscribe(new Subscriber<RxOkHttpResponse>() {
...
Now I can easily store mSubscriber in my view model for clean up triggered by OnDestroy() in my Activity

RxAndroid ViewObservable NetworkOnMainThreadException

I have a Button from which I create an Observable<OnClickEvent>.
When the button is clicked, I wish to fetch a file from the network, but I run into issues regarding networking and threads.
This example throws android.os.NetworkOnMainThreadException :
Observable<OnClickEvent> networkButtonObservable = ViewObservable.clicks(testNetworkButton);
networkButtonObservable
.map(new Func1<OnClickEvent, List<String>>() {
#Override
public List<String> call(OnClickEvent onClickEvent) {
return TestAPI.getTestService().fetchTestResponse();
}
}
)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Object>() {
#Override
public void call(Object o) {Log.w("Final result: " + o);
}
}
);
So I try from another thread.
The following throws rx.exceptions.OnErrorNotImplementedException: Observers must subscribe from the main UI thread, but was Thread[RxNewThreadScheduler-1,5,main] :
networkButtonObservable
.subscribeOn(Schedulers.newThread())
.map(new Func1<OnClickEvent, List<String>>() {
#Override
public List<String> call(OnClickEvent onClickEvent) {
return TestAPI.getTestService().fetchTestResponse();
}
}
)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Object>() {
#Override
public void call(Object o) {Log.w("Final result: " + o);
}
}
);
Ok.. Now I try with a .debounce() at the start :
networkButtonObservable
.debounce(10, TimeUnit.MILLISECONDS)
.map(new Func1<OnClickEvent, List<String>>() {
#Override
public List<String> call(OnClickEvent onClickEvent) {
return TestAPI.getTestService().fetchTestResponse();
}
}
)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Object>() {
#Override
public void call(Object o) {Log.w("Final result: " + o);
}
}
);
And this succeeds.
Obviously I do not like to add delays to my code, so I am trying to figure out what's going on, thread-wise. Why is the first example not also executing the code inside the .map() in a background thread?
Or what am I missing here?
--- Update
I change my TestAPI to return an Observable, and change the first call to the networkButtonObservable to .flatMap(). This also functions properly. But I still don't know why the original way using .map() should fail.
networkButtonObservable
.flatMap(new Func1<OnClickEvent, Observable<?>>() {
#Override
public Observable<?> call(OnClickEvent onClickEvent) {
return TestAPI.getTestService().fetchTestResponseObservable();
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Object>() {
#Override
public void call(Object o) {Log.w("Final result: " + o);
}
}
);
I'm not an expert in Android but based on the error messages, I think you need to bounce the value between the main thread and the background thread. Usually, Android examples show you to add a subscribeOn/observeOn pair to your stream processing:
Observable.just(1)
.map(v -> doBackgroundWork())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(v -> {});
but in these cases, the 'source' is usually a cold observable you are in control.
In your question, the source is a hot Observable with specific requirements that you need to subscribe on the main thread, yet you need to do a network call on a background thread and then show the results on the main thread.
In this case, you can use observeOn multiple times:
networkButtonObservable
.subscribeOn(AndroidSchedulers.mainThread()) // just in case
.observeOn(Schedulers.io())
.map(v -> TestAPI.getTestService().fetchTestResponse())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(v -> updateGUI(v));
I think fetchTestResponseObservable has its own subscribeOn or observeOn applied to it so it doesn't throw the network exception.
Also I'd like to mention that using multiple subscribeOn is functionally equivalent to using only one that is closest to the emitting source, but technically it will hog unused threading resources. Using multiple observeOn in a stream, however, has relevance because you can meaningfully 'pipeline' the stream processing between threads with them.

Categories

Resources