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?