I have the following code based on an example provided by #a.bertucci here Emit objects for drawing in the UI in a regular interval using RxJava on Android, where I zip an Observable with a Timer. When I trigger the subscription by calling processDelayedItems(), the code [A] in the zipped Observable is executed exactly once and one item is emitted to [B]. I would have expected code [A] to run continuously once triggered and keep emitting items every 1500 msec, but it obviously only runs once here.
private static void processDelayedItems() {
Observable.zip(
Observable.create(new Observable.OnSubscribe<Object>() {
#Override public void call(Subscriber<? super Object> subscriber) {
// [A] this code is only called once
subscriber.OnNext(o)
}
}),
Observable.timer(1500, 1500, TimeUnit.MILLISECONDS), new Func2<Object, Long, Object>() {
#Override public Object call(Object entity, Long aLong) {
return entity;
}
}
)
.subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Object>() {
#Override public void call(Object entity) {
// ... and accordingly one item is emitted [B]
}
}, new Action1<Throwable>() {
#Override public void call(Throwable throwable) {
throwable.printStackTrace();
}
}, new Action0() {
#Override public void call() {
}
});
}
Can anybody see the problem which I have here? Is it that I need to reference the Observable from outside the function to keep it alive for more time? Is it collected by GC (Android)? Is it a problem that the function is static?
What are the rules for Observables in terms of their livetime? Are there any best practices how longer-running Observables should be referenced and if they can be static at all? In my tests I noticed that it doesn't really matter, but maybe it does here, when a timer is involved.
--
Corrected code [not working yet]:
added repeat()
Observable.zip(
Observable.create(new Observable.OnSubscribe<Object>() {
#Override public void call(Subscriber<? super Object> subscriber) {
// [A] this code is only called once
subscriber.OnNext(o);
subscriber.OnCompleted();
}
}).repeat(Schedulers.newThread()),
Observable.timer(1500, 1500, TimeUnit.MILLISECONDS), new Func2<Object, Long, Object>() {
#Override public Object call(Object entity, Long aLong) {
return entity;
}
}
)
.subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Object>() {
#Override public void call(Object entity) {
// ... and accordingly one item is emitted [B]
}
}, new Action1<Throwable>() {
#Override public void call(Throwable throwable) {
throwable.printStackTrace();
}
}, new Action0() {
#Override public void call() {
}
});
You need repeat to generate an infinite Observable. E.g.,
Observable.create(new Observable.OnSubscribe<Object>() {
#Override public void call(Subscriber<? super Object> subscriber) {
// [A] this code is only called once
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(o);
}
if (!subscriber.isUnsubscribed()) {
subscriber.onCompleted();
}
}
}).repeat(Schedulers.newThread());
Is it that I need to reference the Observable from outside the function to keep it alive for more time? Is it collected by GC (Android)? Is it a problem that the function is static?
Since you use Schedulers.newThread() and timer, there will be some other Threads which has a reference to your Observable. You don't need more work.
What are the rules for Observables in terms of their livetime? Are there any best practices how longer-running Observables should be referenced and if they can be static at all? In my tests I noticed that it doesn't really matter, but maybe it does here, when a timer is involved.
You're right. It doesn't matter.
In regard to your comment, for simplicity you could do this,
Observable.timer(1500, 1500, TimeUnit.MILLISECONDS)
.flatMap(new Func1<Long, Observable<Object>>() {
#Override
public Observable<Object> call(Long aLong) {
String o = "0";
return Observable.from(o);
}
})
.subscribe(new Action1<Object>() {
#Override
public void call(Object aLong) {
System.out.println(aLong);
}
});
Here you still get the benefits of the timer without the added zip / repeat on top. It's still a bit verbose but it's a bit simpler.
Related
I want to set a count down timer inside each event in recyclerview.
I tried it inside onBindViewHolder. It is working but at the same time it is also affecting the UI.
For example, click events are not working.
Is the any best approach to resolve it?
You could use Observable.interval from RxJava
Here's a util function for countdowntimer
public static Observable<Long> countdownTimer(long countdownValue, Observer<Long> observer) {
if (observer == null) return Observable.just((long) 0);
//.interval is a cold observable
// it will emit separately for every observer
Observable<Long> timerObservale = Observable
.interval(1, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.takeWhile(new Predicate<Long>() {
#Override
public boolean test(Long aLong) throws Exception {
return aLong <= countdownValue;
}
})
.doOnError(Throwable::printStackTrace)
.observeOn(AndroidSchedulers.mainThread());
timerObservale.subscribe(observer);
return timerObservale;
}
Here's how you would use it
Utils.countdownTimer(60, new Observer<Long>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(Long aLong) {
//do somethng
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onComplete() {
}
});
The utils function emits a long every second for a given period of time
I have 2 streams, the first stream is a stream which takes data from database and call onCompleted() after finish taking data. The second stream is a stream that takes live data from server and never call onCompleted(). What I want to do is to create an operator that can do an action if the first stream(upstream) is an empty stream. Here is the sample:
getItemFromDatabase()
.lift(new DoIfEmptyOperator<Item>(new Action0() {
#Override
public void call() {
//Database is empty
System.out.println("Yeay successfully do an action");
}
}))
.concatWith(getItemFromServer()) // -----> intentionally never complete
.subscribe(new Subscriber<StoryItem>() {
#Override
public void onCompleted() {
//dosomething...
}
#Override
public void onError(Throwable e) {
//dosomething...
}
#Override
public void onNext(StoryItem storyItem) {
//dosomething
}
}));
Here is the code of DoIfEmptyOperator:
import rx.Observable;
import rx.Subscriber;
import rx.functions.Action0;
public class DoIfEmptyOperator<T> implements Observable.Operator<T,T>{
private Action0 action;
private boolean isEmpty = true;
public DoIfEmptyOperator(Action0 action) {
this.action = action;
}
#Override
public Subscriber<? super T> call(final Subscriber<? super T> childSubscriber) {
Subscriber<T> parentSubscriber = new Subscriber<T>() {
#Override
public void onCompleted() {
if(isEmpty) {
action.call();
}
childSubscriber.onCompleted();
}
#Override
public void onError(Throwable e) {
childSubscriber.onError(e);
}
#Override
public void onNext(T t) {
isEmpty = false;
childSubscriber.onNext(t);
}
};
childSubscriber.add(parentSubscriber);
return parentSubscriber;
}
}
However the action is never executed because the parentSubscriber onCompleted() is not firing, because the downstream never completed. If I remove
.concatWith(getItemFromServer())
then the action is executed. Any clue about how to solve the problem? I have dived to the source code of Observable.switchIfEmpty() but still have no clue about how it works.
I would advise against creating an operator.
This could be easily done with existing operators like this:
getItemFromDatabase()
.toList()
.flatMap(list -> {
if (list.isEmpty()) {
// side effect here
}
return getItemFromServer();
});
Have you thought about switchIfEmpty()? As an example of the usage of this operator - I have created some code on GitHub at the following link:
https://github.com/rs146/rxjava-simple/blob/master/src/test/java/SwitchIfEmpty.java
switchIfEmpty() is called when no items are emitted.
However, if you want to get items from the api or the db, then you can do something like the following:
Observable.concat(getFromDatabase(), getFromApi()).first();
As long as both getFromDatabase() and getFromApi() return the same Observable Type. This is a common Rx idiom in Android apps. It basically states that if an item's is not emitted from the database, then go fetch the result from the API instead.
I'm using retrofit2 with Rx. I have two API calls. If first call returns empty response with code 400 I need to make second API call, if not then just to show result. I've implemented custom error handling how shown here. Here is my solution:
getResponse1(token)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new ObserverErrorImpl<Response1, BaseError>(BaseError.class) {
#Override
public void onNext(Response1 response) {
view.onSuccess(response);
}
#Override
public void onClientError(BaseError response) {
getResponse2(token)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new ObserverErrorImpl<Response2, BaseError>(BaseError.class) {
#Override
public void onNext(Response2 response) {
view.onSuccess(response);
view.hideProgress();
}
#Override
public void onError(Throwable throwable) {
super.onError(throwable);
view.hideProgress();
}
});
}
#Override
public void onError(Throwable throwable) {
super.onError(throwable);
view.hideProgress();
}
});
Is it possible to simplify this code that goes inside method onClientError? Is it good solution to like that?
1). To simplify it, it would be better that Response1 and Response2 will extend some base class, so in your chain, you can operate with base class, which can be casted to certain type when needed
So, let's assume, that you have BaseResponse:
public abstract class BaseResponse{
public static int TYPE_RESPONSE_1 = 1;
public static int TYPE_RESPONSE_2 = 2;
public abstract int getType(); //every class MUST override this method
}
Response1 and Response2 should override BaseResponse
2). getResponse1 and getResponse2 should return Observable<BaseResponse>
3). Your target code:
getResponse1(token)
.onErrorResumeNext(new Func1<Throwable, Observable<BaseResponse>>() {
#Override
public Observable<BaseResponse> call(Throwable throwable) {
// I use Retrofit 1.9
// And in Retrofit 1.9 I have class RetrofitError, which may provide me all info about error
// I'm absolutelly sure Retrofit 2 also have such class,
// but it may have another name
if(/* is your target error */){
//cast it tour target error
return getResponse2(token);
}
return Observable.error(throwable);
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new ObserverErrorImpl<Response1, BaseError>(BaseError.class) {
#Override
public void onNext(BaseResponse response) {
if(response.getType() == BaseResponse.TYPE_RESPONSE_1){
view.onSuccess(response);
} else {
view.onSuccess(response);
view.hideProgress();
}
}
#Override
public void onError(Throwable throwable) {
super.onError(throwable);
view.hideProgress();
}
});
Jeesh, why does everyone make it so complicated? In practice I've felt that every time I needed to subscribe to an Observable in the subscribe of another, there is an operator that will do it much more cleanly for me:
<T,E extends Throwable> Observable<T>
whenExceptionIs(Class<E> what, Func1<E,Observable<T>> result) {
return t -> {
return what.isInstance(t) ? result.call(t) : Observable.error(t);
};
}
getResponse1(token)
.onErrorResumeNext(whenExceptionIs(BaseError.class, getResponse2(token)))
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(view::onSuccess, view::hideProgress, err -> view.hideProgress());
If you have special error handling needs, create a custom Subscriber that handles that, but make sure that error handling in general is
handled in the Observable chain if it can do anything about it (ignore it, retry the calls etc)
propagated downstream.
I've got a chain of Observables and a dialog that is dismissing after everything is finished.The order is this:
1 api call get ResponseBody
2 take response body process (not ui thread)
3 other process (not ui thread)
During the first call the dialog is okay, when it comes to the second and I receive the body of the first call the dialog is blocked and it remain as is for the rest of the time.
At the end after everything is done, but I receive a warning says that "The app is doing to much work on the main thread".
I'm not doing anything on the main thread, so I don't really understand how i can unblock the dialog and keep everything on a separate thread.
showLoadingDialog();
mZappAppApis.downloadDatabase(Token.getToken(AppConfig.TOKEN_SYNC_DOWNLOAD_DATABASE))
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.concatMap(new Func1<ResponseBody, Observable<String>>() {
#Override
public Observable<String> call(ResponseBody responseBody) {
return mDatabaseFileHelper.writeDatabaseToFile(responseBody);
}
})
.concatMap(new Func1<String, Observable<String>>() {
#Override
public Observable<String> call(String s) {
return mDatabaseFileHelper.copyDatabaseIntoZappApp();
}
})
.subscribe(new Subscriber<String>() {
#Override
public void onCompleted() {
dismissLoadingDialog();
saveLocalTimestamp(timestamp);
flowContinueInterface.onContinueFlow();
}
#Override
public void onError(Throwable e) {
Logger.e(e, "Error during processing new database");
dismissLoadingDialog();
flowContinueInterface.onStopFlow();
}
#Override
public void onNext(String result) {
Logger.d(result);
}
});
The concatMap work is happening on the main thread. You need to move the observeOn call to just above the subscribe call.
I would also move significant processing out of the subscriber into doOnCompleted and doOnError calls that are also placed before the observeOn.
Move your .observeOn(AndroidSchedulers.mainThread()) above the subscribe(… call.
Everything after your observeOn(… is executed on this thread.
You can see this in by printing out the current thread you are on:
.subscribeOn(Schedulers.newThread())
.concatMap(new Func1<String, Observable<String>>() {
#Override
public Observable<String> call(final String string) {
Log.i("Before", Thread.currentThread().toString());
return Observable.just(string);
}
})
.observeOn(AndroidSchedulers.mainThread())
.concatMap(new Func1<String, Observable<String>>() {
#Override
public Observable<String> call(final String string) {
Log.i("After", Thread.currentThread().toString());
return Observable.just(string);
}
})
...
I have to use a legacy library that using AsyncTask for a background job. How I can wrap an AsyncTask by an Observable object which I'm using on my current project.
The AsyncTask is encapsulated so I cannot access the synchronous call inside AsyncTask.
say you have an object asynchronousCall executing some async work with call() method which takes callback as a param, you can wrap it like that :
Observable.create(new Observable.OnSubscribe<Object>() {
#Override
public void call(final Subscriber<? super Object> subscriber) {
asynchronousCall.call(new CallBack() {
#Override
public void success(Object o) {
subscriber.onNext(o);
subscriber.onCompleted();
}
#Override
public void error(Throwable t) {
subscriber.onError(t);
}
});
}
});
Another approach to the case when you cannot or don't want to wrap execution in an Observable is to use Subjects:
public static void main(String[] args) {
Subject<Object, Object> subject = PublishSubject.create();
Listener listener = new Listener() {
#Override
public void onCallback(Object object) {
subject.onNext(object);
subject.onCompleted();
}
};
subject.subscribe(object -> yourReaction(object));
someMethodWithCallback(listener);
}
public interface Listener {
void onCallback(Object object);
}
Subject being an Observer allows you to send items into it, while it being an Observable allows you to subscribe to it and receive these events.