Why onSubscribe does not work in rxjava? - java

when I run below code if I don't write observeOn line, app crashes because getView().showBlockLayout(isBlock); invoke a method that try to hide or show a layout.
but I tried to change below observeOn(AndroidSchedulers.mainThread()) to subscribeOn(AndroidSchedulers.mainThread()) and app crashes again!
subscription.add(UserStore.getInstance().getBlockObservable(databaseHelper.getConference().getUserChatId())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Boolean>() {
#Override
public void call(Boolean isBlock) {
getView().showBlockLayout(isBlock);
databaseHelper.getConference().setBlock(isBlock);
mConferenceModel.setBlock(isBlock);
}
}));
I also test this:
subscription.add(UserStore.getInstance().getBlockObservable(databaseHelper.getConference().getUserChatId())
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Boolean>() {
#Override
public void call(Boolean isBlock) {
getView().showBlockLayout(isBlock);
databaseHelper.getConference().setBlock(isBlock);
mConferenceModel.setBlock(isBlock);
}
}));
and unexpectedly it worked and did not crash! I didn't use subscribeOn in getBlockObservable method(because I know we can set it once)
it's my UserStore class
PublishSubject<Pair<String,Boolean>> mObservableBlock;
private UserStore(){
mObservableBlock = PublishSubject.create();
mInstance = this;
}
public static UserStore getInstance() {
if(mInstance == null)
new UserStore();
return mInstance;
}
public Observable<Boolean> getBlockObservable(final String userId){
return mObservableBlock
.observeOn(Schedulers.computation())
.filter(new Func1<Pair<String,Boolean>, Boolean>() {
#Override
public Boolean call(Pair<String,Boolean> s) {
if(userId.equals(s.first))
return true;
return false;
}
}).map(new Func1< Pair<String, Boolean>, Boolean>() {
#Override
public Boolean call(Pair<String, Boolean> UserBlock) {
return UserBlock.second;
}
});
}
public void publishBlockedUser(String userId,boolean isBlock){
mObservableBlock.onNext(new Pair<String, Boolean>(userId,isBlock));
}
and here is how I imported rxjava dependency in gradle
compile 'io.reactivex:rxjava:1.1.5'
compile 'io.reactivex:rxandroid:1.2.0'

As mentioned in this medium artice:
One important fact is that subscribeOn does not work with Subjects.
So you can't use subscribeOn with subjects and we have to use observerOn(AndroidSchedulers.mainThread()) before subscription.
so all downstream methods are called on mainThread after that.
check this medium artice

Related

Run RxWorker on background thread

I need to run an Observable that does heavy processing on a background thread so the user doesn't feel the app freeze.
I've already tried overriding getBackgroundScheduler(), but the app keeps freezing.
#Override
protected Scheduler getBackgroundScheduler() {
return Schedulers.computation();
}
And tried to subscribe using .subscribeOn(Schedulers.computation()), but got nothing.
I'm using WorkManager with RxJava.
This is my worker class:
public class Worker extends RxWorker {
public Worker(#NonNull Context appContext, #NonNull WorkerParameters workerParams) {
super(appContext, workerParams);
}
#NonNull
#Override
public Single<Result> createWork() {
return Observables.getCounter()
.map(currentValue -> Result.success()) // map is running on a background thread.
.lastOrError();
}
}
And this is the heavy work:
public class Observables {
public static Observable<Integer> getCounter() {
return Observable.just(1, 2, 3); // This is running on the main thread.
}
}
Just create your single using Single.create(). This will make your heavy work run on the subscribing thread. It's good to override the onStopped() method to dispose the disposable.
Your rewritten class:
public class Worker extends RxWorker {
private final CompositeDisposable compositeDisposable;
public Worker(#NonNull Context appContext, #NonNull WorkerParameters workerParams) {
super(appContext, workerParams);
compositeDisposable = new CompositeDisposable();
}
#NonNull
#Override
public Single<Result> createWork() {
return Single.create(emitter -> {
compositeDisposable.add(Observables.getCounter().subscribe(
next -> emitter.onSuccess(Result.success()),
throwable -> emitter.onSuccess(Result.failure())
));
});
}
#Override
public void onStopped() {
super.onStopped();
compositeDisposable.dispose();
}
}

Observable merge() detect which observable is triggered

I'm creating a list of Observable using a list of values, foreach value a custom Observable. I run them all using merge, but I can't detect which one triggers onNext() or onError()
Like in the code below:
List<Observable<MyHttpRsObj>> observables = new ArrayList<>();
for (String param : paramsList) {
Observable<MyHttpRsObj> objObservable = MyRestClient.get().doHttpRequest(param);
observables.add(fileUploadObservable);
}
Observable<BaseRs> combinedObservables = Observable.merge(observables);
combinedObservables.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<MyHttpRsObj>() {
#Override
public void onCompleted() {
//called only once when all Observables finished
}
#Override
public void onError(Throwable throwable) {
//how to know which Observable has error (which param)
}
#Override
public void onNext(MyHttpRsObj myHttpRsObj) {
//how to know which Observable has sccess (which param)
}
});
It is impossible to know which obsevable triggered the error since you merge all observables into single one.
your best bet is to use one observer for each observable. And a last one for merged Observable.
Like this:
List<Observable<MyHttpRsObj>> observables = new ArrayList<>();
for (String param : paramsList) {
//change to connectable Observable
ConnectableObservable<MyHttpRsObj> objObservable = MyRestClient.get()
.doHttpRequest(param)
.publish();
//don't forget to connect
observable.connect();
observables.add(fileUploadObservable);
//subscribe for each observable
objObservable.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<MyHttpRsObj>() {
#Override
public void onCompleted() {
//just partial completed
}
#Override
public void onError(Throwable throwable) {
//you can access param from here
}
#Override
public void onNext(MyHttpRsObj myHttpRsObj) {
//access onNext here
//you can access param from here
}
});
}
Observable<BaseRs> combinedObservables = Observable.merge(observables);
combinedObservables.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<MyHttpRsObj>() {
#Override
public void onCompleted() {
//called only once when all Observables finished
}
#Override
public void onError(Throwable throwable) {
//don't handle error here
}
#Override
public void onNext(MyHttpRsObj myHttpRsObj) {
}
});
PS: use ConnectableObservable to avoid emitting twice

How to unsubscribe from the rxJava request

I have the following class, I want to return Subscription object or something else so I can cancel the request from where I have referenced subscribe() method , but subscribe(observer) returns void!
How can I do that?
public abstract class MainPresenter<T> {
protected <T> Disposable subscribe(Observable<T> observable, Observer<T> observer) {
observable.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}
[New Update]
I used this way temporary, I am waiting for better solutions:
protected <T> DisposableMaybeObserver subscribe(final Maybe<T> observable,
final Observer<T> observer) {
return observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableMaybeObserver<T>() {
#Override
public void onSuccess(T t) {
observer.onNext(t);
}
#Override
public void onError(Throwable e) {
observer.onError(e);
}
#Override
public void onComplete() {
observer.onComplete();
}
});
}
[New Update 2]
[![Screenshot][https://i.stack.imgur.com/mioth.jpg]]
[New Update 3]
[]1
You should maybe use use subscribeWith :
private Disposable mDisposable;
public abstract class MainPresenter<T> {
protected Disposable subscribe(Observable<T> observable, DisposableObserver<T> observer) {
mDisposable = observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(observer);
return mDisposable;
}
Then when you need :
if (mDisposable != null && !mDisposable.isDisposed()) {
mDisposable.dispose();
}
Hope this helps.

Get values of an Array from an Observable

I have an Observable<MoviesResponse>. My MovieResponse class contains a getResults() methods returning a List<Result>. This Result class has a getTitle() methods returning a String. I want to call the getTitle() methods of all my Result objects to get all the titles of my movies.
I achieved this with the code below using a foreach loop but I think there is a better way to do this by chaining RxJava operators, I just can't figure it out...
Subscription :
Observable<MoviesResponse> moviesResponseObservable = apiService.getTopRatedMoviesObservable(API_KEY);
subscription = moviesResponseObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<MoviesResponse>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(MoviesResponse moviesResponse) {
List<Result> results = moviesResponse.getResults();
for (Result r:results) {
Log.d(LOG_TAG,r.getTitle());
}
}
});
Interface :
public interface ApiService {
#GET("movie/top_rated")
Observable<MoviesResponse> getTopRatedMoviesObservable(#Query("api_key") String apiKey);
}
You can use a flatmap to transform your observable into an Observable<Result> and then use map to turn that into Observable<String>, which you can then subscribe to.
moviesReponseObservable
.subscribeOn(Schedulers.io())
.flatMapIterable(new Function<MoviesResponse, Iterable<Result>>() {
#Override
public Iterable<Result> apply(#NonNull MoviesResponse moviesResponse) throws Exception {
return moviesResponse.getResults();
}
})
.map(new Function<Result, String>() {
#Override
public String apply(#NonNull Result result) throws Exception {
return result.getTitle();
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
#Override
public void onNext(String s) {
Log.d("TAG", s);
}
/* ... */
});
I got the following error with #zsmb13 answer :
new Function : map (rx.functions.Func1) in
Observable cannot be applied to (anonymous
java.util.function.Function)reaso‌​n:
no instance(s) of type variable(s) R exist so that Function conforms to Func1
Anyway this answer was very helpul I just replaced Function with Func1 and used call method.
subscription = moviesResponseObservable
.subscribeOn(Schedulers.io())
.flatMapIterable(new Func1<MoviesResponse, Iterable<Result>>() {
#Override
public Iterable<Result> call(MoviesResponse moviesResponse) {
return moviesResponse.getResults();
}
})
.map(new Func1<Result, String>() {
#Override
public String call(Result result) {
return result.getTitle();
}
})
.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, s);
}
});

Obtaining reference to original Scheduler

I have the following RxJava Observable:
final class MapBitmapObservable {
static Observable<Bitmap> create(#NonNull final MapView mapView) {
return Observable.create(new Observable.OnSubscribe<Bitmap>() {
#Override
public void call(final Subscriber<? super Bitmap> subscriber) {
mapView.getMapAsync(new OnMapReadyCallback() {
#Override
public void onMapReady(#NonNull final GoogleMap googleMap) {
googleMap.snapshot(new GoogleMap.SnapshotReadyCallback() {
#Override
public void onSnapshotReady(#Nullable final Bitmap bitmap) {
if (bitmap != null) {
subscriber.onNext(bitmap);
subscriber.onCompleted();
} else {
subscriber.onError(new MapSnapshotFailedException());
}
}
});
}
});
}
});
}
private MapBitmapObservable() {
}
}
The MapView method getMapAsync must be called on the main thread to avoid this exception:
java.lang.IllegalStateException: getMapAsync() must be called on the main thread
at com.google.android.gms.common.internal.zzx.zzcD(Unknown Source)
at com.google.android.gms.maps.MapView.getMapAsync(Unknown Source)
at com.github.stkent.bugshaker.email.screenshot.maps.MapBitmapObservable$1.call(MapBitmapObservable.java:42)
at com.github.stkent.bugshaker.email.screenshot.maps.MapBitmapObservable$1.call(MapBitmapObservable.java:37)
at rx.Observable.unsafeSubscribe(Observable.java:8098)
...
Assume the MapBitmapObservable is used as part of an Observable chain in which previous and subsequent operations are potentially long-running and should be executed off the main thread. A simplified example could look like this:
Observable.just(activity)
.flatmap(new Func1<Activity, Observable<MapView>>() {
#Override
public Observable<Bitmap> call(#NonNull final Activity activity) {
return ExpensiveToCreateObservable.create(activity);
}
})
.flatmap(new Func1<MapView, Observable<Bitmap>>() {
#Override
public Observable<Bitmap> call(#NonNull final MapView mapView) {
return MapBitmapObservable.create(mapView);
}
})
.flatmap(new Func1<Bitmap, Observable<Uri>>() {
#Override
public Observable<Uri> call(#NonNull final Bitmap bitmap) {
return SomeOtherExpensiveToCreateObservable.create(bitmap);
}
})
.subscribeOn(Schedulers.io())
.subscribe();
(although it should be noted that in my actual application, the chaining is spread across several different methods). I would like to:
make sure that MapView.getMapAsync is called on the main thread;
allow the second long-running operation to execute on the original Scheduler, whatever that may have been (Schedulers.io(), Schedulers.computation(), etc.)
In my mind, pseudocode to achieve this would look something like:
Observable.just(activity)
.flatmap(new Func1<Activity, Observable<MapView>>() {
#Override
public Observable<Bitmap> call(#NonNull final Activity activity) {
return ExpensiveToCreateObservable.create(activity);
}
})
.observeOn(AndroidSchedulers.mainThread()) // This is real, and resolves bullet 1.
.flatmap(new Func1<MapView, Observable<Bitmap>>() {
#Override
public Observable<Bitmap> call(#NonNull final MapView mapView) {
return MapBitmapObservable.create(mapView);
}
})
.observeOn(/* Some way of referencing the thread on which I originally subscribed, to resolve bullet 2. */)
.flatmap(new Func1<Bitmap, Observable<Uri>>() {
#Override
public Observable<Uri> call(#NonNull final Bitmap bitmap) {
return SomeOtherExpensiveToCreateObservable.create(bitmap);
}
})
.subscribeOn(Schedulers.io()) // I do not want to rely on knowledge of the Scheduler type used at this call-site.
.subscribe();
Is this possible?
From the observeOn() documentation:
ObserveOn, on the other hand, affects the thread that the Observable will use below where that operator appears. For this reason, you may call ObserveOn multiple times at various points during the chain of Observable operators in order to change on which threads certain of those operators operate.
So as mentioned by Aaron He, you could keep some reference to the Scheduler you are using use it on the latter "observeOn".
Another approach to do this that I sometimes use, is to remove both "observeOn" function, and make sure View items are being handled on UI thread by Activity.runOnUiThread. Something like -
static Observable<Bitmap> create(#NonNull final Activity activity,#NonNull final SomeObject someObject) {
return Observable.create(new Observable.OnSubscribe<Pair<Activity,SomeObject>>() {
#Override
public void call(final Subscriber<? super Pair<Activity,SomeObject>> subscriber) {
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
someObject.doStuff();
}
});
}
});
}

Categories

Resources