Wait for an Observable to finish before executing another Observable? - android

The Problem
I have an activity which fetches data from an API periodically and displays the data received. The API uses OAuth so I receive a temporary access token which expires after a certain period of time (1 hr). If the app tries to get data with an expired token, obviously the request will fail. In an earlier iteration of my app, I was using AsyncTasks for the network requests and essentially just executed a new AsyncTask that would get a new access token before calling the main AsyncTask that fetches the data from the server. This worked great because the main AsyncTask would wait until the other one was finished before executing.
I recently switched to RxJava and basically just replaced the AsyncTasks with Observables. The problem is that the main Observable that fetches the data doesn't wait for the Observable that refreshes the access token to finish. Here's my code, thanks for your help.
Code
LiveThreadActivity.java
private Subscription subscription;
private Observable<List<CustomComment>> fetchData;
#Override
protected void onResume() {
super.onResume();
if (tokenExpired()) {
auth.refreshToken();
}
subscription = fetchData
.compose(bindToLifecycle())
.retryWhen(new RetryWithDelay(5, 2000))
.subscribe(list -> addNewComments(list), e -> handleFetchDataError(e));
}
// This method gets called in onCreate()
private void dataCollection() {
fetchData = Observable.interval(0, REFRESH_RATE, TimeUnit.MILLISECONDS)
.map(tick -> fetchNewComments()) // Run function every time a tick is emitted
.retryWhen( new RetryWithDelay(2, 2000) ) // Retry twice with 2 second delay
.subscribeOn(Schedulers.io()) // Network stuff in background thread
.observeOn(AndroidSchedulers.mainThread()); // Other stuff on the main thread
}
Auth.java
public class Auth {
...
public void refreshToken() {
Observable.just(1)
.map(y -> refreshAccessToken())
.retryWhen( new RetryWithDelay(3, 2000) )
.subscribeOn(Schedulers.io())
.subscribe();
}
}

Using reactive libraries a new way of thinking is needed. You have to write the code as it is synchronious, but be aware that it evecutes asynchroniously.
Your code just executes synchoniously. It executes two Observable's at the same time.
The function refreshToken() should look like:
public Observable<?> refreshToken() {
return Observable.just(1)
.map(y -> refreshAccessToken())
.retryWhen( new RetryWithDelay(3, 2000) )
.subscribeOn(Schedulers.io());
}
And onResume():
#Override
protected void onResume() {
super.onResume();
Observable obs = fetchData
.compose(bindToLifecycle())
.retryWhen(new RetryWithDelay(5, 2000));
if (tokenExpired()) {
obs = obs.startWith(auth.refreshToken());
}
subscription = obs
.subscribe(list -> addNewComments(list), e -> handleFetchDataError(e));
}
Notice startWith() operator. It allows to executes one Observable (fetching list) after another (refreshing token).

.flatMap() will probably be sufficient, i.e. tokenObservable.flatMap(/* return dataObservable */)

Related

Is Observable object released automatically RxAndroid

I have a function that creates Observable:
void getData() {
Observable.create(emitter -> {
// call webservice
.......
emitter.onNext(myData);
}).subscribe(data -> {
// Process data here
});
}
I don't want to use Disposable here. I think the observable is local variable, so it will be released after the function is done.
Is the observable released automatically after I call getData() function?
Observable will automatically dispose they called onComplete() or onError()
Ex: You have a method to load exactly data from 10 files Observable<String> loadFiles() which return Observable.create(). Then every time you want to emit value you call e.onNext(), after count 10 times you will call e.onComplete() to mark that your Observable has finish it's work, then it will auto dispose.
You only need to call dispose() method to indicate that the Subscriber is no longer interested in any of the Observables it is currently subscribed to. Those Observables can then (if they have no other interested observers) choose to stop generating new items to emit.
Call dispose() when activity stopped to make sure that no more emission will come after that. So it's a good practice because it can prevent memory leaks and waste of resources, network calls.
Observables do not dispose them-selfs.
It's a good practice to dispose your observable to avoid memory leaks and crashes of your app.
you either use disposable.dispose() or compositeSubscribtion.clear().
I have made a simple test and after I exited the app(back btn) observable continued to emit data.
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Observable.create(emitter -> {
for (; ; ) {
emitter.onNext("data");
Thread.sleep(3000);
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).
subscribe(data -> {
Log.d(tag, (String) data);
});
}
});
OUTPUT :
onStart() is called
onResume() is called
data
data
onPause() is called
onStop() is called
data
data

RXJava2 "neverending" Observable

I'm new to RX and got stuck with the next:
I'm using SQLBrite and when query method is called it gives me back a "neverending" Observable which listens to the db changes. When some items are emitted - I need to make some async calls in a loop and give back that items which I received. The sample code:
public Observable<List<Entity>> execute() {
return someManager.getAccount()
.take(1) // I do not need RX to trigger updates when account changed, so use take(1)
.flatMap(accountOptional -> {
if (accountOptional.isPresent()) {
SomeAccount account = accountOptional.get();
return someManager.getEntityDataSource()
.query(new EntitySpecification(account.getId()));
}
return Observable.just(new ArrayList<Entity>()); // No account - no entities
})
.flatMapIterable(entities -> entities) // Here I have a list of entities i I need to make async calls to fill entities with some iiner data (some other entities)
.flatMap(this::loadInnerData)
.toList()
.toObservable();
}
private Observable<Entity> loadInnerData(Entity entity) {
return do some work with entity;
}
The trouble comes when I use toList() - it waits till Observable ends it's job - but it will not be done, as this observable is listening to the db. How can I achieve ability to listen to not stop listening to the "neverending" Observable and loop async calls with RX (as it is done with loadInnerData())?
Maybe try with
return Observable.create(new Action1<Emitter<String>>() {
#Override
public void call(final Emitter<String> stringEmitter) {
// here u can use onNext / onError
stringEmitter.onNext(s);
stringEmitter.onError(e);
}
});
stringEmitter.setCancellation(new Cancellable() {
#Override
public void cancel() throws Exception {
subscription.unsubscribe();
}
});
}
}, Emitter.BackpressureMode.BUFFER);

How to wait first request finish before start second with Rx?

I have an async method makeRequest() with callback. It called many times from different classes of my application. I need that this calls start one by one and never simultaneously.
I want to implement this using Rx. Like this:
public void execute() { // This method called many times from another classes
Observable.just(true)
// what I need to add here?
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(o -> {
internalExecute();
return o;
})
.subscribe();
}
private void internalExecute() { // This method should called only when previous call was finished
makeRequest(this::onRequestFinished);
}
private void onRequestFinished() {
// here is I handle request finish
}
But at now all requests works at parallel. What I need to add here to run requests one by one?
According to comments, you have here separated streams and requests. each client that execute request expect a result from the request. but no requests allowed to run in parallel, in this case I think the easiest way is to limit the Scheduler to an application global background sequential thread Executor, i.e:
Schedulers.from(Executors.newSingleThreadExecutor())
provide somewhere in your app this single thread Executor, in singleton manner of course, it's important that each request stream will use the same object:
private final Scheduler singleThreadScheduler = Schedulers.from(Executors.newSingleThreadExecutor());
public void execute() { // This method called many times from another classes
Observable.just(true)
.map(o -> {
internalExecute();
return o;
})
.subscribeOn(singleThreadScheduler)
.subscribe();
}
private void internalExecute() { // This method should called only when previous call was finished
makeRequest(this::onRequestFinished);
}
private void onRequestFinished() {
//NOTE: you should make sure that the callback execute where you need it (main thread?)
// here is I handle request finish
}
besides that, you're not exposing Observable outside, to the clients, but rather using callback mechanism, you can leverage reactive approach further, by making execute() returning Observable. (and enjoy composition of Obesrvables, operators, proper use of observeOn/subscribeOn, error handling with onError, disposing/unsubscribing etc.), as you're using async api, you can use fromEmitter()/create() (in newer RxJava1 version)), read more here:
private final Scheduler singleThreadScheduler = Schedulers.from(Executors.newSingleThreadExecutor());
public Observable<Result> execute() { // This method called many times from another classes
return Observable.fromEmitter(new Action1<Emitter<? extends Object>>() {
#Override
public void call(Emitter<?> emitter) {
emitter.setCancellation(() -> {
//cancel request on unsubscribing
});
makeRequest(result -> {
emitter.onNext(result);
});
}
})
.subscribeOn(singleThreadScheduler)
}

Function not executing on background thread

I'm trying to apply RX to Android. I want when a button is clicked, to download something from web and display it.
My problem is that HttpClient.connect() executes on the mainThread instead of a background one.
The call to HttpClient.connect() executes as a function passed to Observable.map()
Observable<Integer> dayDeltas = Obs.obsToSequence(Obs.Observable(textView)); //transforms click events to observable
Observable<String> dates = dayDeltas.map(...).map(...)
dates.map(Obs.dateToWebPage()) // calls http.connect()
.map(Obs.parseEvents())
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.subscribe(updateTextView(textView));
public static Observable<Object> Observable(final TextView text) {
return Observable.create(new Observable.OnSubscribe<Object>() {
#Override
public void call(final Subscriber<? super Object> subscriber) {
final Object event = new Object();
text.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.e("click", "click");
subscriber.onNext(event);
}
});
}
});
Now my naive interpretation is that since i have .subscribeOn(Schedulers.newThread()) every function/operator on the observable should execute in a new thread, including .map(f). Clearly this is not what is happening, so what part of this chain does execute on new threads?
subscribeOn is there to trigger subscription side-effects. In your setup, it will register the callback to capture the button press on the new thread but when the press happens, the onNext emission is triggered by the main thread. The chain, including the network connect then executes on the main thread.
You have to put a new observeOn(Schedulers.io()) before the connecting method to make sure the reception of the button press event happens off the main thread.
Edit:
//transforms click events to observable
Observable<Integer> dayDeltas = Obs.obsToSequence(Obs.Observable(textView));
Observable<String> dates = dayDeltas.map(...).map(...)
dates
.observeOn(Schedulers.io()) // <------------------------------------------ add
.map(Obs.dateToWebPage()) // calls http.connect()
.map(Obs.parseEvents())
.observeOn(AndroidSchedulers.mainThread())
//.subscribeOn(Schedulers.newThread()) // <------------------------------- remove
.subscribe(updateTextView(textView));
After carefully reading the Scheduling and Threading RX doc, i have the solution:
dates
.observeOn(Schedulers.io())
.subscribeOn(AndroidSchedulers.mainThread())
.map(Obs.dateToWebPage())
In my original code, the schedulers passed to observeOn/subscribeOn were reversed and in the wrong place.

Schedulers.io() not returning to main thread

I'm using RxParse to parse query's async load but when i subscribe my observable using subscribeOn(Schedulers.io()) my onCompleted method is never called on main thread. Instead of this, my onCompleted method is called inside of worker thread pool. If i use observeOn(AndroidSchedulers.mainThread) everything will work as well, but my onNextMethod will be called on main thread too and I don't want it.
There is something wrong in my code?
Have anything wrong in my code?
ParseObservable.find(myQuery)
.map(myMapFunc())
.subscribeOn(AndroidSchedulers.handlerThread(new Handler()))
.subscribe(
new Subscriber<MyObj>() {
#Override
public void onError(Throwable e) {
Log.e("error","error",e);
}
#Override
public void onNext(T t) {
// ... worker thread (but here is ok)
}
public void onCompleted() {
// ... worker thread again instead of mainThread
}
}
)
);
First you need to understand the difference between subscribeOn() and observeOn(). These are two completely different operators that affect different parts of the Rx chain.
subscribeOn() specifies where your Observable will do its work. It will not affect where onNext(), onError(), and onComplete() execute.
observeOn() specifies where the the callbacks (e.g. onNext()) are executed. It will not affect where your Observable does its work.
All the callbacks will occur on the same thread. You cannot specify that some callbacks occur on one thread and some happen on another through any RxJava APIs. If that is the behavior you desire, you will have to implement it yourself in your callbacks.
Unfortunately the subscription is in the same thread for all methods (onNext, onError and onCompleted
But you can observe in the Schedulers.io() and inside the onNext(T t) method, create a new Observable to listen in the MainThread like this:
ParseObservable.find(myQuery)
.map(myMapFunc())
.subscribeOn(Schedulers.io())
.subscribe(
new Subscriber<MyObj>() {
#Override
public void onError(Throwable e) {
Log.e("error","error",e);
}
#Override
public void onNext(T t) {
Observable.just(t)
.observeOn(AndroidSchedulers.mainThread())
.subscribe((t) -> {
// do something in MainThread
})
}
public void onCompleted() {
// ... worker thread again instead of mainThread
}
}
)
);
I hope it help!
I would recommend using "side action" operators in this case. It seems to me like a slightly more elegant solution than using nested observables:
ParseObservable.find(myQuery)
.map(myMapFunc())
.subscribeOn(AndroidSchedulers.handlerThread(new Handler()))
.doOnCompleted(() -> onCompleteAction())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(value -> onNext(value))
.subscribe();
It is not advisable to subscribe within a subscription.
subscribeOn determines where the Observable chain will start when an observer subscribes to it.
observeOn can be used at different points (and multiple times, if need be) throughout your observable chain to pass control between threads. (You can verify this by checking whether you're on the main thread or not within each of these blocks).
ParseObservable.find(myQuery)
.map(myMapFunc())
// Added this:
.doOnNext(obj -> {
// NOTE: This will happen on your `subscribeOn` scheduler
// Do something with `obj` here while on worker thread
}
.subscribeOn(AndroidSchedulers.handlerThread(new Handler()))
// Added this:
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<>() {
next -> {
// NOTE: This will happen on the main thread
},
error -> {
Log.e("error","error",e);
// NOTE: This will happen on the main thread
},
() -> {
// NOTE: This will happen on the main thread
}
});

Categories

Resources