Keep stream alive even after onComplete() - java

There are two issues which I am currently facing.
1) As soon as the line RetrofitProvider.getInstance().getCurrentWeather(.....) is called the network call is being done. How can it be deferred till the observer is connected to it.
2) Once weatherInfoPublisher.onComplete() is called, the next time I call onComplete on this object the new observer's onNext is not getting called.
public Observable<LinkedList<WeatherInfo>> getWeatherData(final String payload, final TempUnit tempUnit) {
PublishSubject weatherInfoPublisher = PublishSubject.create();
RetrofitProvider.getInstance().getCurrentWeather(payload + ",us", translateTempUnit(tempUnit))
.flatMap(new Function<String, ObservableSource<String>>() {
#Override
public ObservableSource<String> apply(String todayResponse) throws Exception {
Log.d(TAG, "Received today weather: " + todayResponse);
parseTodayData(todayResponse, weatherDataList);
return RetrofitProvider.getInstance().getForecastWeather(
payload + ",us", translateTempUnit(tempUnit), FORECAST_DAYS);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableObserver<String>() {
#Override
public void onNext(String futureResponse) {
Log.d(TAG, "Received future weather: " + futureResponse);
parseFutureData(futureResponse, weatherDataList);
weatherInfoPublisher.onNext(weatherDataList);
weatherInfoPublisher.onComplete();
}
#Override
public void onError(Throwable e) {
Log.e(TAG, "The error is, " + e.getMessage());
}
#Override
public void onComplete() {
}
});
return weatherInfoPublisher;
}
This is a singleton class and the entire implementation has been provided in here in Github Link.

How can it be deferred till the observer is connected to it.
Do not subscribe to that observable in this method. Instead return that observable to the client. As soon as the observable is subscribed - a request would be performed.
the next time I call onComplete on this object the new observer's onNext is not getting called.
See reactive stream specs: if a stream completes - it can never be continued, that's a terminal event.

Related

RXJava retrofit waiting having the UI wait for data to be fetched from API

I'm learning android development I'm using RXJava with retrofit to interact with an API. So i've successfully managed to GET Data from my API. The problem now is that the program continues before the data has been fetched. How can I wait for the data to be downloaded from the API and then run some function?
I have a retrofit client as shown
public class RetrofitClient {
private static Retrofit retrofit = null;
private RetrofitClient() {
}
public static Retrofit getClient(String baseUrl) {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(SimpleXmlConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
}
return retrofit;
}
}
I do an API call as such.
public void getData(MyFragment Fragment) {
mAPIService.getData()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Data>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
Log.i("ERROR IN GET DATA", e.toString());
}
#Override
public void onNext(Data response) {
Log.i("MY DATA", response.toString());
fragment.downloadData(response);
}
});
}
the problem is my android application does not wait for fragment.downloadData(response) to finish but instead continues executing code and then crashes because response is null.
I have a listener on a button that when clicked gets data from the API
button.setOnClickListener(v ->{
APICaller.getData(this);
Log.i("TEST", data.ToString()); //THIS IS NULL
});
This is the downloadData function that I run from the APICaller
public void downloadData(Data data) {
this.data = data;
}
You need to be waiting for your RxJava stream to emit a value (either error or response).
Firstly, for this, if you're expecting a "single" emission, success or failure, I would use a Single. At the moment it looks your mAPIService.getData() method is returning an Observable. These are meant for streams that are going to emit multiple values which in your cause I am assuming is not what is going to happen. You only have one item that is going to be emitted so I would look at returning a Single. Not part of your question but FYI.
What I like to do is to tell my UI that whatever I'm doing is "loading", normally in the doOnSubscribe. Then the UI knows to show a loading icon or to not allow user interactions or something. Something like this (nb. notice how its after the observeOn(AndroidSchedulers.mainThread). Any time you interact with UI elements, do it on the main thread. I think this will do it):
mAPIService.getData()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(new Consumer<Disposable>() {
#Override
public void accept(Disposable disposable) throws Exception {
fragment.loading();
}
})
Then when either the onError or onSuccess returns in your subscription, that is where you tell the UI it's ready to proceed. You then either have a valid response or can show the error to the user.
EDIT: Reactive Programming
After your comments it looks like you do not understand reactive programming. It has a bit of a steep learning curve and I still struggle with it today.
Your APICaller class, whatever it is, should return the Observable itself. You shouldn't be passing in a Fragment to this and handling it within there as you're opening yourself up to memory leaks and its a bit of a code smell. A better option is to just return the Observable returned by mAPIService.getData(). That's it. At the moment you are pushing the Observable to another thread using Schedulers.io() which says to your main thread, carry on and don't wait for me. You then come back to this thread when a response is emitted using the code .observeOn(AndroidSchedulers.mainThread())
In your fragment is then where you handle the emission of a value or an error. Your fragment code then becomes:
button.setOnClickListener(v ->{
APICaller.getData()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Data>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
Log.i("ERROR IN GET DATA", e.toString());
}
#Override
public void onNext(Data response) {
Log.i("MY DATA", response.toString());
this.data = response
Log.i("TEST: "+data.toString());
}
});
});
I would like to recommend you watch some tutorials, do some reading and I'd also like to refer you back to my point about Singles at the start

Consumer.accept() not called for Subject in doOnNext()

I have two Subjects, one subscribes to the other for updates.
Subject<Integer> subject = new Subject<>() {
#Override
public boolean hasObservers() {
return false;
}
#Override
public boolean hasThrowable() {
return false;
}
#Override
public boolean hasComplete() {
return false;
}
#Override
public Throwable getThrowable() {
return null;
}
#Override
protected void subscribeActual(Observer<? super InitialAPIResponse> observer) {
}
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(Integer result) {
Log.d(TAG, "onNext: " + apiResponse);
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
};
subject.doOnNext(result -> Log.d("Subject", "accept: " + result));
observableSubject
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(subject);
observableSubject.onNext(1);
observableSubject.onComplete();
When the onNext() is called, the Consumer's accept() provided in doOnNext() is not. Even though according to the documentation
Observable.doOnNext()
Modifies the source ObservableSource so that it invokes an action when it calls onNext.
Scheduler:
doOnNext does not operate by default on a particular Scheduler
onNext
the action to invoke when the source ObservableSource calls onNext
return the source ObservableSource with the side-effecting behavior applied
From what I understand from the documentation the observable should call the Consumer in doOnNext().
I'm learning RxJava so maybe I'm doing something wrong here...
There are two problems:
1.
subject.doOnNext(result -> Log.d("Subject", "accept: " + result));
In above code, the result of doOnNext is not subscribed. doOnNext does not subscribe to upstream on its own, just as many other operators. Change to this, for example:
subject.doOnNext(result -> Log.d("Subject", "accept: " + result)).subscribe();
2.
observableSubject
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(subject);
observableSubject.onNext(1);
observableSubject.onComplete();
In above code, onComplete is called immediately after .onNext. This can cause timing issues when emitting the values.
Change above code to either
observableSubject
.subscribe(subject); // subscribe on the same thread so that everything happens sequentially.
observableSubject.onNext(1);
observableSubject.onComplete();
or
Subject<Integer> observableSubject = BehaviorSubject.create();
observableSubject
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(subject);
observableSubject.onNext(1);
// observableSubject.onComplete(); // don't call onComplete/

rxjava onStart event with delaySubscription

Observable.create(new Observable.OnSubscribe<Integer>() {
#Override
public void call(Subscriber<? super Integer> subscriber) {
subscriber.onStart();
subscriber.onNext(1);
subscriber.onCompleted();
}
}).delaySubscription(5, TimeUnit.SECONDS).subscribe(new Subscriber<Integer>() {
#Override
public void onCompleted() {
Log.e("TAG", String.format("(%s) - onCompleted", System.currentTimeMillis()));
}
#Override
public void onError(Throwable e) {
Log.e("TAG", String.format("(%s) - onError", System.currentTimeMillis()), e);
}
#Override
public void onNext(Integer integer) {
Log.e("TAG", String.format("(%s) - onNext: %s", System.currentTimeMillis(), integer));
}
#Override
public void onStart() {
super.onStart();
Log.e("TAG", String.format("(%s) - onStart", System.currentTimeMillis()));
}
});
output:
(1485004553817) - onStart
(1485004558818) - onNext: 1
(1485004558819) - onCompleted
why onStart event not waiting to delaySubscription and calling soon ?
i want aware when call method called
Documentation says -
onStart -
This method is invoked when the Subscriber and Observable have been connected but the Observable has not yet begun to emit items or send notifications to the Subscriber.
delaySubscription:
Returns an Observable that delays the subscription to the source Observable by a given amount of time.
onNext is invoked only when the subscription is achieved. onStart is called the moment a connection is established. Thus, it works as expected according to the definition.
You can try commenting the code subscriber.onStart(); and execute the same again to notice that onStart is still called at the beginning. The intentional execution did not really invoke the said method because this was executed not on the real subscriber we created, but the one which was a result of delaySubscription (of type OnSubscribeDelaySubscription).
Below is a snippet which can probably help you achieve what you're looking for:
public static void main(String[] args) throws UnsupportedEncodingException, IOException {
Observable.timer(5, TimeUnit.SECONDS).flatMap(val -> {
System.out.println("Initialize");
return Observable.create(subscriber -> {
System.out.println("onsubscribe");
doMyAsyncStuff(subscriber);
});
}).subscribe(val -> System.out.println(val));
Observable.timer(10, TimeUnit.SECONDS).toBlocking().first();
}
We initialize a timer, once timer is executed, we perform some task in flatMap which should be the same as what you earlier did with onStart. Once that task is executed, we emit a Observable which emits all the elements that you could have consumed earlier with onNext calls.

How to make RxJava interval to perform action instantly

Hello I am making observable to ask my server about its online/offline status every 15 seconds:
public Observable<Response> repeatCheckServerStatus(int intervalSec, final String path) {
return Observable.interval(intervalSec, TimeUnit.SECONDS)
.flatMap(new Func1<Long, Observable<Response>>() {
#Override
public Observable<Response> call(Long aLong) {
return Observable.create(new Observable.OnSubscribe<Response>() {
#Override
public void call(Subscriber<? super Response> subscriber) {
try {
Response response = client.newCall(new Request.Builder()
.url(path + API_ACTION_CHECK_ONLINE_STATUS)
.header("Content-Type", "application/x-www-form-urlencoded")
.get()
.build()).execute();
subscriber.onNext(response);
subscriber.onCompleted();
if (!response.isSuccessful())
subscriber.onError(new Exception());
} catch (Exception e) {
subscriber.onError(e);
}
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
});
}
After I call this method, first execution of code will be after intervalSec time (15sec in my case). Looking at rxJava docummentation of interval method:
http://reactivex.io/documentation/operators/interval.html
This is how it should be.
Question: is there any way to execute code instantly and then repeat in intervals?
You can execute it immediately also like this:
Observable.interval(0, 1000, TimeUnit.MILLISECONDS).subscribe();
What you are looking for is startWith
Observable.interval(15, SECONDS).startWith(1);
This will get the updates from the interval, but emit one item immediately after subscribing.
you can use `
Observable.interval(1, TimeUnit.SECONDS).startWith(0)
`
It is duplicate value "0" in subscribe.

Firebase transaction never calls onComplete()

Working with Firebase in Java, I have a situation where it seems my Transaction.Handler.onComplete() is never called.
I'm trying to wait for completion on a particular transaction, so I keep the calling thread from advancing until onComplete() is called. This is done by an anonymous decorator around the existing Transaction.Handler txn.
Transaction.Handler txn = ...
CountDownLatch latch = new CountDownLatch(1);
ref.runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(MutableData data) {
return txn.doTransaction(data);
}
#Override
public void onComplete(FirebaseError e, boolean wasCommitted, DataSnapshot data) {
System.out.println("never printed");
txn.onComplete(e, wasCommitted, data);
if(e != null || !wasCommitted) {
System.out.println("error: " + e + ", committed: " + wasCommitted);
}
latch.countDown();
}
});
latch.await();
System.out.println("never printed");
doTransaction() definitely completes at least once - and I can see the data update in the web interface - but onComplete() is never called and we just hang.
I can't see anything wrong with this wrapper pattern: I ran the same transaction alone in a test main() method on a different Firebase, and onComplete() was called.
What is causing this hang? Is there something about a Firebase's configuration (e.g. auth? rules? ) that's introducing a bug here?
EDIT
I'm having the same problem without the wrapper pattern. Here's that code:
CountDownLatch latch = new CountDownLatch(1);
ref.runTransaction(new Transaction.Handler() {
#Override
public void onComplete(FirebaseError e, boolean b, DataSnapshot data) {
if(e != null || !b) {
System.out.println("error: " + e + ", committed: " + b);
}
System.out.println("transaction finished!");
latch.countDown();
}
#Override
public Result doTransaction(MutableData data) {
System.out.println("doing transaction");
data.child(...).setValue(...);
return Transaction.success(data);
}
});
latch.await();
System.out.println("never printed");
Which results in printing doing transaction and the data changing in the Firebase (which I see via web UI), but not printing transaction finished. It just hangs.
On Android all Firebase callbacks are run on the main thread. If you call latch.await() on the main thread, onComplete will never have the chance to run, because the main thread is blocked, resulting in a deadlock.

Categories

Resources