I have the following piece of code
Single.just(settings.toString())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<String>() {
private Disposable disposable;
#Override public void onSubscribe(Disposable d) {
this.disposable = d;
}
#Override public void onSuccess(String s) {
webViewFragment.onInjectMessage(s, null);
this.disposable.dispose();
}
#Override public void onError(Throwable e) {
this.disposable.dispose();
}
});
I am in a background thread and need to inject a string into a WebView, which can only be done on the main thread, which is why i'm calling .observeOn(AndroidSchedulers.mainThread()
Yet when I read through the sample code of SingleObserver on GitHub, I see the following:
return new SingleObserver<String>() {
#Override public void onSubscribe(Disposable d) {
Log.d(TAG, " onSubscribe : " + d.isDisposed());
}
#Override public void onSuccess(String value) {
textView.append(" onNext : value : " + value);
textView.append(AppConstant.LINE_SEPARATOR);
Log.d(TAG, " onNext value : " + value);
}
#Override public void onError(Throwable e) {
textView.append(" onError : " + e.getMessage());
textView.append(AppConstant.LINE_SEPARATOR);
Log.d(TAG, " onError : " + e.getMessage());
}
};
They are not disposing SingleObserver.
Is it required for me to call this.disposable.dispose(); in onSuccess and/or onError (both? or just in onSuccess?), or will this class dispose by itself, as shown in the GitHub sample?
I'm asking this in the context of memory leaks.
This question has its roots in RxAndroid `Observable...subscribe` highlighted in Android Studio
You don't need to dispose the disposable at there : onSuccess, onError.
Most operators have their own Observer. Each Observer is created and connected during Operator Chaining. After then, when you call subscribe(), disposable is created in the top-level DataSource Operator(e.g, Single.just()) and connected to the subscriber through the ChildObserver.onSubscribe() callback of each operator. Then, start with DataSource, check Disposable.isDisposed(), and call ChildObserver.onSuccessful() or onError() callback.
Anyway, Disposable is an interface to control the stream and does not hold resources statically. If your own logic works on the background and the result is applied to your view on the main thread, View components can be invalid if the Activity's life cycle goes through onDestroy(). So, it is general to call dispose() at there for this reason.
Related
I am new in retrofit/rxjava-android
Someone told me that, it is best practice if I will cancel the request if the call is not yet finished and the user leaves the activity page.
I am having problem where/how to cancel it.
Here's my code, it is working properly.
Observable<List<MyObject>> call;
public void getStaticMessages() {
call = restInterface.loginURL();
call.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<List<MyObject>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
Log.d("LOGGER", "error");
}
#Override
public void onNext(List<MyObject> myObjects) {
Log.d("LOGGER", "succcess");
}
});
}
One of the best practice is to create subscription/disposable when onStart() method of activity/fragment is called and unsubscribe /dispose when onStop called.
You can create one disposable Disposable disposable = call.subscribeOn and dispose it via disposable.dispose() or use CompositeDisposable.
I used the same approach with CompositeDisposable in one of my previous pet projects - link
I am new in RxJava and trying to update my asyncTask works to RxJava. As a first try I have done the following codes:
public class MainActivity extends AppCompatActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
doSomeWork();
}
private String funcCallServerGet()
{
//Some code to call a HttpClient Get method & return a response string
//this is the method which previously i used to call inside asynctask doInbackground method
}
private void doSomeWork() {
getSingleObservable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getSingleObserver()) ;
}
private Single<String> getSingleObservable()
{
return Single.create(new SingleOnSubscribe<String>() {
#Override
public void subscribe(SingleEmitter<String> emitter) throws Exception {
if(!emitter.isDisposed()) {
String strRxResponse = funcCallServerGet();
emitter.onSuccess(strRxResponse);
}
}
});
}
private SingleObserver<String> getSingleObserver()
{
return new SingleObserver<String>() {
#Override
public void onSubscribe(Disposable d) {
Log.d(TAG, " onSubscribe getSingleObserver: " + d.isDisposed()); }
#Override
public void onSuccess(String value) {
Log.d(TAG, " onNext : value : " + value); }
#Override
public void onError(Throwable e) {
Log.d(TAG, " onError : " + e.getMessage()); }
};
}
}
But I have some confusions:
Why am I getting false in onSubscribe() of SingleObserver getSingleObserver() .
How do I unsubscribe or cancel the observable/observer when activities onStop() is called.
Also, what really happens when screen oriantation. Does the observable get unsubscribed automatically or it continues its work ? what to do for the device rotation ?
Why am I getting false in onSubscribe() of SingleObserver getSingleObserver() .
You're currently logging whether the disposable is disposed within the onSubscribe method. At this point the disposable hasn't been disposed yet.
How do I unsubscribe or cancel the observable/observer when activities onStop() is called.
Rather than use a SingleObserver you could use the subscribe method which returns a disposable. With this you could either manage the disposable directly or use a CompositeDisposable. You would then call the dispose method on that disposable, with CompositeDisposable this is achieved by calling clear()
private final CompositeDisposable disposables = new CompositeDisposable();
#Override
protected void onStart() {
super.onStart();
disposables.add(getSingleObservable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(value -> {
Log.d(TAG, " onSuccess: " + value);
}, error -> {
Log.e(TAG, " onError", error);
}
)
);
}
#Override
protected void onStop() {
disposables.clear();
super.onStop();
}
Also, what really happens when screen oriantation. Does the observable get unsubscribed automatically or it continues its work ? what to do for the device rotation ?
By default no automatic management of the observable occurs, it's your responsibility to manage it. In your example code when the device rotates you will receive another call to onCreate, here you're scheduling the work to be executed again, work that was scheduled before rotation could still be running, so you could end up leaking the old activity and receiving a callback when the work succeeds or fails - in this case you'd see a log statement.
There are some tools that provide automatic observable management, though you should read the authors article about some of the issues that exist with this approach.
https://blog.danlew.net/2017/08/02/why-not-rxlifecycle/
https://github.com/trello/RxLifecycle
https://github.com/uber/AutoDispose
Another option for you could be to look at the new Architecture Components library, specifically ViewModel and LiveData. This will simplify what you need to do with respect to subscription management and configuration changes.
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 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.
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.