I am using rx-android zip operator to merge two retrofit calls.
Previously the code was like this:
affinityService.rewardsStatusChanges()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.compose(this.<RewardsStatus>bindToLifecycle())
.subscribe(new Action1<RewardsStatus>() {
#Override
public void call(RewardsStatus rewardsStatus) {
onRewardStatus(rewardsStatus);
}
});
affinityService.affinityStatusChanges()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.compose(this.<AffinityStatus>bindToLifecycle())
.subscribe(new Action1<AffinityStatus>() {
#Override
public void call(AffinityStatus affinityStatus) {
onAffinityStatus(affinityStatus);
}
});
rewardsStatusChanges() and affinityStatusChanges() are two retrofit calls.
Now I need to merge them.
What I have tried:
affinityService.rewardsStatusChanges()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.compose(this.<RewardsStatus>bindToLifecycle())
.flatMap(new Func1<RewardsStatus, Observable<RewardsStatus>>() {
#Override
public Observable<RewardsStatus> call(RewardsStatus rewardsStatus) {
return Observable.just(rewardsStatus);
}
})
.flatMap(new Func1<RewardsStatus, Observable<RewardsStatus>>() {
#Override
public Observable<RewardsStatus> call(RewardsStatus rewardsStatus) {
return Observable.zip(Observable.just(rewardsStatus),
affinityService.affinityStatusChanges(),new Func2<RewardsStatus, AffinityStatus, RewardsStatus>() {
#Override
public RewardsStatus call(RewardsStatus rewardsStatus, AffinityStatus affinityStatus) {
onAffinityAndRewardsMerged(rewardsStatus,affinityStatus);
return null;
}
});
}
});
But unfortunately the above codebase is not working.
Any idea how to do this.
I am using:
RX_ANDROID_VERSION=1.0.1
RX_JAVA_VERSION=1.0.14
posting as you wished, however with that return null in the anonymous function you will get null in your consumer, so I think returning like Pair<RewardsStatus,AffinityStatus> would be nicer, and do that result processing in the consumer.
Observable.zip(affinityService.rewardsStatusChanges(), affinityService.affinityStatusChanges(),
object : Func2<RewardsStatus, AffinityStatus, RewardsStatus>() {
fun call(rewardsStatus: RewardsStatus, affinityStatus: AffinityStatus): RewardsStatus? {
onAffinityAndRewardsMerged(rewardsStatus, affinityStatus)
return null
}
})
Related
I have the following call to retrieve some data from server and update the UI according to response.
poiAPIService.getPoiDetails(poiId!!)
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe { showProgressBar(true) }
.doFinally { showProgressBar(false) }
.subscribeOn(Schedulers.io()).subscribe(
{ poiDetails ->
bindPoiDetails(poiDetails)
},
{
(getActivity() as MainOverviewActivity).fragmentControl.hidePoiDetailsFragment()
})
}
It complains about showProgressBar that the Views are only accessable on thread that created them.
If I change the call like this, everything seems to be fine again.
showProgressBar(true)
poiAPIService.getPoiDetails(poiId!!)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io()).subscribe(
{ poiDetails ->
showProgressBar(false)
bindPoiDetails(poiDetails)
},
{
showProgressBar(false)
(getActivity() as MainOverviewActivity).fragmentControl.hidePoiDetailsFragment()
})
}
I have done by using below code, using RxJava 2.x
poiAPIService.getPoiDetails(poiId!!)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(new Consumer < Disposable >() {
#Override
public void accept(Disposable disposable) throws Exception {
showProgressBar(true);
}
})
.doFinally(new Action () {
#Override
public void run() throws Exception {
showProgressBar(false);
}
})
.subscribe(/**your subscription here**/);
Try using above code and let me know.
did you tried to do something like this...
poiAPIService.getPoiDetails(poiId!!)
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(Schedulers.io())
.doOnSubscribe { showProgressBar(true) }
.doFinally { showProgressBar(false) }
.subscribe(
{ poiDetails ->
bindPoiDetails(poiDetails)
},
{
(getActivity() as MainOverviewActivity).fragmentControl.hidePoiDetailsFragment()
})
pay attention to observeOn and subscribeOn
Looks like you use observeOn and subscribeOn not correctly...
take a look to How RXJava Scheduler/Threading works for different operator?
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.
Android Studio 3.1 RC 2
kotlin 1.2.30
Signature for the fetchMessage
Single<Response> fetchMessage(final String messageId);
The kotlin code that I am trying to convert to Java. However, I am not sure where the returns are? As I am new to kotlin and lambda.
private fun getMessage(messageId: String): Observable<State> {
return repository
.fetchMessage(messageId)
.flatMap {
Single.fromCallable<State>({
update(messageId, it, State.COMPLETED)
State.COMPLETED
})
}
.toObservable()
}
This is my attempt at trying to convert it. However, the compiler complains of a missing return.
public Observable<TranslationChatState> translate(String messageId) {
return repository
.fetchMessage(messageId)
.flatMap(new Func1 <Response, Single<State >>() {
#Override
public Single<State> call(final Response response) {
Single.fromCallable(new Callable<State>() {
#Override
public State call() {
update(messageId, response, State.COMPLETED);
return State.COMPLETED;
}
});
} /* complains about no return here */
})
.toObservable();
}
Many thanks for any suggestions,
because it is missing the return statement. It should be
public Single<State> call(final Response response) {
return Single.fromCallable(new Callable<State>() {
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();
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.