just like this sample
Observable.fromCallable(() -> okHttp.getDataFromNet(page))
.subscribeOn(Schedulers.io())
.doOnSubscribe(()->{adapter.loadmore})
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<String>() {
#Override
public void onCompleted() {
adapter.stopLoad();
}
#Override
public void onError(Throwable throwable) {
}
#Override
public void onNext(String s) {
Log.e("fail","fail");
adapter.add();
}
});
I find onNextwill be called two times in this case.If i add Log.e() in
onCompleted(),it also will called two times.What is wrong about my code.What is the reason that if a method will be called in Subscriber ,then I add doOnSubscribe() it will be called two times。
//progressbarAdapter extends Recyclerview.Adapter
static final int LOADING_TYPE = -1;
private boolean isLoading = false;
public void loadingMore(){
isLoading = true;
notifyItemInserted(getItemCount());
// size = index +1
}
public void loadingEnd(){
notifyItemRemoved(getItemCount());
isLoading = false;
}
#Override
public int getItemCount() {
return getDataCount() + (isLoading ? 1: 0);
}
#Override
public int getItemViewType(int position) {
if (position == getDataCount() && !isError){
return LOADING_TYPE;
}else {
return getExtItemViewType(position);
}
}
the real reason
when adapter.notify it will trigger scroll methods,so it will be called two times. I am sorry for that I asked a bad question.
please have a look how to use observeOn and subscribeOn. There can only be one subscribeOn for an observable. If you use multiple, the last one wins. Use observeOn to switch threads between pipelines and subscribeOn to specify the thread which will create the observable.
I transformed your given code in order to use it in a test. I only get one onNext-call.
Problem: getting called two times.
This may be because of wrong usage of the observable somewhere else in the code by flatMap or multiple subscriptions to observable. Please provide more context.
Testenvironment: IntelliJ Idea 2017 EAP, io.reactivex:rxjava:1.2.1
#Test
public void name() throws Exception {
Observable<String> doOnSub = Observable.just("1")
.subscribeOn(Schedulers.io())
.doOnSubscribe(() -> {
System.out.println("doOnSub");
})
.subscribeOn(Schedulers.computation())
.observeOn(Schedulers.computation());
TestSubscriber<String> objectTestSubscriber = TestSubscriber.create();
doOnSub.subscribe(objectTestSubscriber);
objectTestSubscriber.awaitTerminalEvent();
objectTestSubscriber.assertValues("1");
}
Related
After onError, my observable stops working. How can I avoid that?
Here is my autocomplete observable and subscription code:
public void subscribeAutoComplete() {
autoSubscription = RxTextView.textChangeEvents(clearableEditText)
.skip(1)
.map(textViewTextChangeEvent -> textViewTextChangeEvent.text().toString())
.filter(s -> s.length() > 2)
.debounce(400, TimeUnit.MILLISECONDS)
.flatMap(text -> autoCompleteService.getAutoCompleteTerms(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 t) {
Log.e("rx", t.toString());
}
#Override
public void onNext(List<String> strings) {
autoAdapter = new ArrayAdapter<>(MainActivity.this,
android.R.layout.simple_dropdown_item_1line, strings);
clearableEditText.setAdapter(autoAdapter);
clearableEditText.showDropDown();
}
});
compositeSubscriptions.add(autoSubscription);
}
It's simple, just ignore the errors:
autoCompleteService.getAutoCompleteTerms(text).onErrorResumeNext(Observable.empty())
Note that this is potentially dangerous, as you'll ignore all errors; in this case it's probably OK, but be careful of overusing this.
Using tryOnError works for me and it will call error inside subscribe() as well without getting UndeliverableException, app stop running or need of RxJavaPlugins.setErrorHandler which will make UI related more difficult to handle.
I want following behaviour:
observableMain should buffers all items until observableResumed emits a value. Then observableMain should emit all buffered and all feature values...
What I do in my activity's onCreate:
PublishSubject<T> subject = ...; // I create a subject to emit items to and to subscribe to
// 1) I create a main observable from my subject
final Observable<T> observableMain = subject
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
// 2) I use a custom base class in which I can register listeners
// for the onResume event and which I can query the isResumed state!
// I call the object the pauseResumeProvider!
// 2.1) I create an observable, it emits a value ONLY if my activity is resumed
final Observable<Boolean> obsIsResumed = Observable
.defer(() -> Observable.just(pauseResumeProvider.isActivityResumed()))
.skipWhile(aBoolean -> aBoolean != true);
// 2.2) I create a second observable, it emits a value as soon as my activity is resumed
final Observable<Boolean> obsOnResumed = Observable.create(new Observable.OnSubscribe<Boolean>()
{
#Override
public void call(final Subscriber<? super Boolean> subscriber)
{
pauseResumeProvider.addPauseResumeListener(new IPauseResumeListener() {
#Override
public void onResume() {
pauseResumeProvider.removePauseResumeListener(this);
subscriber.onNext(true);
subscriber.onCompleted();
}
#Override
public void onPause() {
}
});
}
});
// 2.3) I combine the resumed observables and only emit the FIRST value I can get
final Observable<Boolean> observableResumed = Observable
.concat(obsIsResumed, obsOnResumed)
.first();
// 3) here I'm stuck
// 3 - Variant 1:
Observable<T> observable = observableMain
.buffer(observableResumed)
.concatMap(values -> Observable.from(values));
// 3 - Variant 2:
// Observable<T> observable = observableMain.delay(t -> observableResumed);
// 4) I emit events to my my subject...
// this event is LOST!
subject.onNext("Test in onCreate");
Problem
All items that are send to the subject after the activity is resumed are working, all items before are lost (at least with the delay solution). I can't get my desired behaviour to work. How would I correctly solve that?
Have a source replayed and use delaySubscription to trigger the real subscription.
PublishSubject<Integer> emitNow = PublishSubject.create();
ConnectableObservable<T> co = source.replay();
co.subscribe();
co.connect();
co.delaySubscription(emitNow).subscribe(...);
emitNow.onNext(1);
Edit:
Here is a gist with an operator you can lift into a sequence that can pause and resume emissions from upstream.
Another way to do it is to delay emissions of source (hot) observable until "valve" opens
source.delay(new Func1<Integer, Observable<Boolean>>() {
#Override
public Observable<Boolean> call(Integer integer) {
return valve.filter(new Func1<Boolean, Boolean>() {
#Override
public Boolean call(Boolean isOpen) {
//emits only when isOpen is true
return isOpen;
}
});
}
})
.subscribe(new Action1<Integer>() {
#Override
public void call(Integer integer) {
System.out.println("out: " + integer);
}
});
Gist: Rx Valve
I have an Observable that does something without the need to emit a value. Also I have a list of objects I want the Observable to work with. So for all elements in this list: doSomething()
Observable.from(uris)
.flatMap(new Func1<Uri, Observable<Void>>() {
#Override
public Observable<Void> call(Uri uri) {
return createDoSomethingObservable(uri);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Observer<Void>() {
#Override
public void onCompleted() {
Log.d(TAG, "completed");
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Void aVoid) {
Log.d(TAG, "next");
}
});
And the method that creates the Observable:
Observable<Void> createDoSomethingObservable(final Uri uri) {
return Observable.create(new Observable.OnSubscribe<Void>() {
#Override
public void call(Subscriber<? super Void> subscriber) {
//doSomething
subscriber.onNext(null);
subscriber.onCompleted();
}
});
}
Now when I run this with a List with 3 elements I get:
next
next
next
completed
which is good, because that is what I wanted, but I don't know why it's working. First I started to just call onComplete, because in the end the observable does its job and completes. But then of course onNext is never called on the subscriber. The same goes for the other way round.
So my questions are:
Why is onComplete only called for the last list element?
Is there a better way to solve this?
onComplete is called for the last element because that's when the earliest observable in the chain (from(uris)) has finished.
It's expected that your observables emitted from flatMap will call onComplete. Once that's done (and call has returned), then the next emission from from can be worked on. Once from has finished emitting observables, it calls onComplete and the chain is finished, effectively.
I think, that small code helps you to understand behavior of onNext( ) and onComplete().
Suppose, you have an List<Uri>. Let's transform it to Observable<Uri> manually.
public static Observable<Uri> getUries(List<Uri> uriList){
return Observable.create(new Observable.OnSubscribe<Uri>() {
#Override
public void call(Subscriber<? super Uri> subscriber) {
for(Uri uri : uriList){
subscriber.onNext(uri);
}
subscriber.onCompleted();
}
});
}
Or using Lambda expressions:
public static Observable<Uri> getUries(List<Uri> uriList){
return Observable.create(subscriber -> {
for(Uri uri : uriList){
subscriber.onNext(uri);
}
subscriber.onCompleted();
});
}
As you can see, we are iterating input list, and call onNext( ) for every element, and when we finished transforming our List to Observable, we called onComplete()
P.S.
This code just a demonstration, please, never use it to transfor List to Observable. Use operator Observable.from() for it.
UPDATE:
Operator from( ) implementation:
...
while (true) {
if (o.isUnsubscribed()) {
return;
} else if (it.hasNext()) {
o.onNext(it.next());
} else if (!o.isUnsubscribed()) {
o.onCompleted();
return;
} else {
// is unsubscribed
return;
}
}
...
link:https://github.com/ReactiveX/RxJava/blob/1.x/src/main/java/rx/internal/operators/OnSubscribeFromIterable.java#L75-L87
onComplete (same as onError) is called only once during observable chain, it is the way that rxjava is implemented
I think that your approach is correct, so better way is not needed.
So a few weeks ago i asked this question: recyclerview periodic ui child updates.
And today i want to refactor that funcionality using Rxjava. It's actually pretty simple, i accomplish the following way:
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (friend.getGameStatus().equals(GameStatus.INGAME)) {
holderOnline.startRepeatingTask();
} else {
holderOnline.stopRepeatingTask();
}
}
class MyViewHolderOnline extends RecyclerView.ViewHolder {
private Subscription subscribe;
public MyViewHolderOnline(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
public void startRepeatingTask() {
subscribe = Observable.interval(updateInterval, TimeUnit.MILLISECONDS)
.map(aLong -> current.getGameStatusToPrint())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<String>() {
#Override public void onCompleted() { }
#Override public void onError(Throwable e) { }
#Override
public void onNext(String gameStatusToPrint) {
gameStatus.setText(gameStatusToPrint);
}
});
}
void stopRepeatingTask() {
if(subscribe != null && !subscribe.isUnsubscribed())
subscribe.unsubscribe();
}
}
The problem however is different. I have a navigationDrawer implemented with Activities that are Paused and not Destroyed when switched. So, after i switch to the activity that don't contains this adapter, the observable keeps on sending stuff because its a periodic interval Observable. So my question is, what should i do? I need to unsubscribe when the activity is paused, but i have no idea how since to, and also how to subscribe back. Any help or ideas?
So, if I understand you correctly, one way to solve your problem is to answer the question: In an Activity that contains a RecyclerView, how do I get references to all the ViewHolders that are currently displayed/bound?
For example, to stop all the updates you could then do the following in the onPause() of your Activity:
// assuming you are using a LinearLayoutManager:
final int firstVisibleIndex = mLinearLayoutManager.findFirstVisibleItemPosition();
final int lastVisibleIndex = mLinearLayoutManager.findLastVisibleItemPosition();
for (int i = firstVisibleIndex; i <= lastVisibleIndex; i++) {
YourViewHolder viewHolder = (YourViewHolder) findViewHolderForAdapterPosition (i);
viewHolder.stopRepeatingTask();
}
And likewise, you could restart the tasks in the onResume of your Activity.
BUT: Now that I wrote this I am not sure whether there may be ViewHolders beyond the visible area of the RecyclerView (i. e. before the first or after the last visible item) that are already bound but that would not be reached with this method. If that turns out to be the case you can still iterate over all indices of the items in your adapter and just discard any null return values.
I'm almost sold to RxJava, which is a perfect companion to Retrofit, but I'm struggling into a common pattern while migrating my code: to save bandwidth, I'd like to lazily fetch (paginated) objects from my webservice as needed, while my listview (or recyclerview) is scrolling using reactive programming.
My previous code was doing the job perfectly, but reactive programming seems worth the try.
Listening to listview/recyclerview scrolling (and other boring stuffs) isn't the concern and getting an Observable is easy using Retrofit:
#GET("/api/messages")
Observable<List<Message>> getMessages(#Path("offset") int offset, #Path("limit") int limit);
I just can't figure out the pattern to use in reactive programming.
The Concat operator seems a good starting point, along with ConnectableObservable at some point to defer emission and maybe flatMap, but how ?
EDIT:
Here's my current (naive) solution:
public interface Paged<T> {
boolean isLoading();
void cancel();
void next(int count);
void next(int count, Scheduler scheduler);
Observable<List<T>> asObservable();
boolean hasCompleted();
int position();
}
And my implementation using a subject:
public abstract class SimplePaged<T> implements Paged<T> {
final PublishSubject<List<T>> subject = PublishSubject.create();
private volatile boolean loading;
private volatile int offset;
private Subscription subscription;
#Override
public boolean isLoading() {
return loading;
}
#Override
public synchronized void cancel() {
if(subscription != null && !subscription.isUnsubscribed())
subscription.unsubscribe();
if(!hasCompleted())
subject.onCompleted();
subscription = null;
loading = false;
}
#Override
public void next(int count) {
next(count, null);
}
#Override
public synchronized void next(int count, Scheduler scheduler) {
if (isLoading())
throw new IllegalStateException("you can't call next() before onNext()");
if(hasCompleted())
throw new IllegalStateException("you can't call next() after onCompleted()");
loading = true;
Observable<List<T>> obs = onNextPage(offset, count).single();
if(scheduler != null)
obs = obs.subscribeOn(scheduler); // BEWARE! onNext/onError/onComplete will happens on that scheduler!
subscription = obs.subscribe(this::onNext, this::onError, this::onComplete);
}
#Override
public Observable<List<T>> asObservable() {
return subject.asObservable();
}
#Override
public boolean hasCompleted() {
return subject.hasCompleted();
}
#Override
public int position() {
return offset;
}
/* Warning: functions below may be called from another thread */
protected synchronized void onNext(List<T> items) {
if (items != null)
offset += items.size();
loading = false;
if (items == null || items.size() == 0)
subject.onCompleted();
else
subject.onNext(items);
}
protected synchronized void onError(Throwable t) {
loading = false;
subject.onError(t);
}
protected synchronized void onComplete() {
loading = false;
}
abstract protected Observable<List<T>> onNextPage(int offset, int count);
}
Here's one out of a few potential ways to handle reactive paging. Let's assume we have a method getNextPageTrigger which returns an Observable emits some event object when the scroll listener (or whatever input) wants a new page to be loaded. In real life it would probably have the debounce operator, but in addition to that we'll make sure we only trigger it after the latest page has loaded.
We also define a method to unwrap the messages from their list:
Observable<Message> getPage(final int page) {
return service.getMessages(page * PAGE_SIZE, PAGE_SIZE)
.flatMap(messageList -> Observable.from(messageList));
}
Then we can make the actual fetching logic:
// Start with the first page.
getPage(0)
// Add on each incremental future page.
.concatWith(Observable.range(1, Integer.MAX_VALUE)
// Uses a little trick to get the next page to wait for a signal to load.
// By ignoring all actual elements emitted and casting, the trigger must
// complete before the actual page request will be made.
.concatMap(page -> getNextPageTrigger().limit(1)
.ignoreElements()
.cast(Message.class)
.concatWith(getPage(page))) // Then subscribe, etc..
This is still missing a couple potentially important things:
1 - This obviously doesn't know when to stop fetching additional pages, which means once it hits the end, depending on what the server returns, it could either keep hitting errors or empty results every time scroll is triggered. Approaches to solving this depend on how you signal to the client that there are no more pages to load.
2 - If you need error retries, I would suggest looking into the retryWhen operator. Otherwise, common network errors could cause an error in a page load to propagate.