I wrote a method to print the output from flatMap (Pseudo code):
Observable.just(...).repeat()
.flatMap( return Observable.just([double]))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Double>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
tvConfidence.setText(e.getMessage());
}
#Override
public void onNext(Double aDouble) {
tvConfidence.setText("Confidence :" + aDouble);
}
});
When I run these code, it works a few seconds but after a few seconds, it would not run onto the onNext method again. I don't know why, because I debug the code, it will run the Observable.just(double), and the value always changed but it would not execute the code setText to refresh the textView.
My guess is that due to that particular flatMap overload, you eventually start to accumulate a lot of just because flatMap is unbounded-in. Try with flatMap(f, 1) to limit the concurrency level.
Related
I am new to RxAndroid and tried the same with Room database. But the problem is that when inserting data into a database, then the progress bar that I am showing getting blocked and being sluggish. I referred this blog for the project
public void insertBillPayments(final CallBackParams params,
BillPaymentsOffline... payments) {
Completable.fromAction(() -> db.daoAccess().insertBillPayments(payments))
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new CompletableObserver() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onComplete() {
params.getCallback().onOrderAdded(params);
}
#Override
public void onError(Throwable e) {
params.getCallback().onDataNotAvailable(params);
}
});
}
Please correct me if I am doing anything wrong.
This code seems fine, but I would recommend that you switch your observeOn and subscribeOn calls for clarity as subscribeOn applies to the stream and observeOn applies to the emitted values and functions applied after it.
http://reactivex.io/documentation/operators/observeon.html
I am researching into RxJava/RxAndroid. I decided to create Observable via Observable.fromCallable method because of it gives us two important things:
The code for creating the emitted value is not run until someone subscribes to the Observerable
The creation code can be run on a different thread.
I have wrote the sample as below:
private void testObservableFromCallable() {
Observable<String> stringObservable = Observable.fromCallable(new Callable<String>() {
#Override
public String call() throws Exception {
return "Hello";
}
});
Subscription subscription = stringObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String s) {
Log.d("LOG_TAG", "Thread name: " + Thread.currentThread().getName());
textView.setText(s);
}
});
}
Every things are ok. It work fine and log tag show thread name is main. It means onNext() method be called on mainThread() and textView be accessed properly.
Now what am I confusing is when I remove this line observeOn(AndroidSchedulers.mainThread()) or just change it to observeOn(Schedulers.io()). Both of two cases the log tag show thread name is RxIoScheduler. That is not main thread, but the app compile with no error and textView show the text "Hello" on the screen.
Anyone help me explain why am I able to access to the view outside main thread?.
Thanks so much!
The framework usually does not do anything to check that you're really on the main thread. As a caller, it's your responsibility.
Some but not all UI widget access on non-main thread lead to a crash. Here you're being "lucky" that the thread problem goes undetected with your method call.
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 to chain up three network calls with RxJavaand Retrofit. The first call (retrieves the session token) has to be the first, the other two depend on this call and if the first call isn't finished before, the other two calls will result in an error.
For the other two calls, they should retrieve some information and update the UI. What would be the best way to proceed?
I first thought about using the zip Operator, but I'm not sure if it respects the order of the requests and as it returns a value, it felt like abusing it to just use it to bundle up the requests without any further processing.
My second approach would be to flatmap the requests and use doOnNext to update the UI once, but I'm not certain if this is the correct way.
private void setUpInitialUIState() {
restClient.requestSessionToken()
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
.flatMap(new Func1<SessionTokenResponse, Observable<CurrentPlmnResponse>>() {
#Override
public Observable<CurrentPlmnResponse> call(SessionTokenResponse sessionTokenResponse) {
return restClient.requestCurrentPlmn();
}
})
.doOnNext(new Action1<CurrentPlmnResponse>() {
#Override
public void call(CurrentPlmnResponse currentPlmnResponse) {
if (!currentPlmnResponse.isError()) {
tvProvider.setText(currentPlmnResponse.getData().getFullName());
}
}
})
.flatMap(new Func1<CurrentPlmnResponse, Observable<MonitoringStatusResponse>>() {
#Override
public Observable<MonitoringStatusResponse> call(CurrentPlmnResponse currentPlmnResponse) {
return restClient.requestMonitoringStatus();
}
})
.subscribe(new Subscriber<MonitoringStatusResponse>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable throwable) {
Log.d("onError", throwable.toString());
}
#Override
public void onNext(MonitoringStatusResponse monitoringStatusResponse) {
if (monitoringStatusResponse != null && !monitoringStatusResponse.isError() && monitoringStatusResponse.getData().getSignalIcon() >= 0 && monitoringStatusResponse.getData().getSignalIcon() <= 5) {
ivSignalStrength.setImageResource(getResources().getIdentifier("ic_signal_" + monitoringStatusResponse.getData().getSignalIcon(), "drawable", getPackageName()));
tvNetworkType.setText(getNetworkTypeTitle(monitoringStatusResponse.getData().getCurrentNetworkType()));
}
}
});
}
Depends if you want your 2nd and 3rd calls to be executed in parallel or one after another. If in parallel go for the .zip and don't feel bad about it :)
3 tips on your (current) code (maybe you are aware already or slightly different in your app, so apologies):
Catch the subscription returned from the .subscribe and kill (unsubscribe) at onDestroy the latest. If the app closes the network calls will continue to live.
If .requestCurrentPlmn() is in a thread then the .setText will complain from a touching view from not ui thread exception.
You miss a .onError in your .subscribe. If a request fails, the app will crash.
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.