Subscribe based on other emissions - java

I have an observable that emits values. Based on these values I need to subscribe/unsubscribe to/from another Observable.
Is there a handy way of doing so? A convenient way instead creating a field for the subscription and handling it manually?
Example:
Observable A emits Booleans. If it emits true then a subscription should be made to Observable B - if false this subscription should be unsubscribed.

I'm not sure if we're 100% on the same page but I think you're missing one point. Maybe you'll think I'm nitpicking, but I think it will be good to get our terms straight.
Observable starts emitting values when a Subscriber subscribes to it. So unless you're thinking about two separate Subscribers you can't react to an emitted value with a subscription because the Observer won't emit anything.
That said... what (I think) you wanna do could be done this way:
Observable<Boolean> observableA = /* observable A initialization */;
final Observable<SomeObject> observableB = /* observable B initialization */;
observableA
.flatMap(new Func1<Boolean, Observable<SomeObject>>() {
#Override
public Observable<SomeObject> call(Boolean aBoolean) {
if (!aBoolean) {
throw new IllegalStateException("A dummy exception that is here just to cause the subscription to finish with error.");
}
return observableB;
}
})
.subscribe(
new Action1<SomeObject>() {
#Override
public void call(SomeObject someObject) {
// THIS IS A PART OF THE SUBSCRIBER TO OBSERVABLE B.
// THIS METHOD WILL BE CALLED ONLY IF THE OBSERVABLE A RETURNED TRUE
}
},
new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
// A dummy Action1 so the subscription does not crash on the Exception
}
});

If all of observables has the same type or you can combine whatever you want based on values.
Observable.from(new int[]{1,2,3,4,5})
.filter(i -> i < 5) // filter out something
.flatMap(i -> {
if (i < 2) { // subscribe on some observable, based on item value
return Observable.just(i);
} else {
return Observable.just(3);
}
})

Related

How does the concurrency differ between .flatMap() and .delayUntil()?

I was trying to find a flux operator which returns the original publisher after the new one finishes, and .delayUntil() was the key.
how to avoid .flatMap(x-> reactiveAction(x).thenReturn(x))
However, when it comes to flux, unlike flatMap, delayUntil seemed not to work concurrently.
public class BasicTest {
// flatMap
#Test
public void testFlatMap() {
Flux.range(1, 10)
.flatMap(num -> save(num).thenReturn(num))
.doOnNext(System.out::println)
.as(StepVerifier::create)
.thenConsumeWhile(p -> true)
.verifyComplete();
}
// delayUntil
#Test
public void testDelayUntil() {
Flux.range(1, 10)
.delayUntil(this::save)
.doOnNext(System.out::println)
.as(StepVerifier::create)
.thenConsumeWhile(p -> true)
.verifyComplete();
}
private Mono<Integer> save(Integer num) {
return Mono.just(num).log().delayElement(Duration.ofMillis(100));
}
}
I can confirm you that testDelayUntil takes twice more time than testFlatMap.
And I looked through the inner implementation of delayUntil, and it used .concatMap() inside.
I want delayUntil using flatMap, so that it does not wait for the new publisher made from the former signal to be completed, but keep subscribing to new publishers when the formal signals arrive.
Is there anything similar as .flatMap(v -> reactiveOperation(v).thenReturn(v)) instead of .delayUntil()? or should I request for new feature?
Thank you!

Transpose from Consumer to CompletableFuture

I'm currently using an API which I unfortunately cannot change easily. This API has some methods in the style of this:
public void getOffers(Consumer<List<Offer>> offersConsumer) {
final Call<List<Offer>> offers = auctionService.getOffers();
handleGetOffers(offersConsumer, offers);
}
It's a web api using retrofit, and it enables me to process the response in a consumer, but I much rather want to work with CompletableFutures.
I'm using the data I receive from this endpoint to compose an interface in a game, and therefore compose an inventory, that basically acts as a frontend to the api. What I want to do, is to have my composing method to wait for the consumer to finish, and then provide the processed results. This is what I have so far, but I don't know how to do the step from the consumer to the CompletableFuture:
#Override
public CompletableFuture<Inventory> get(Player player) {
return CompletableFuture.supplyAsync(() -> {
auctionAPI.getOffers(offers -> {
//process the offers, then return the result of the processing, in form of an "Inventory"-Object.
}
});
});
}
I now need to return the result of the processing after all the Items have been received and then processed. How can I achieve this?
Something along the lines should work:
#Override
public CompletableFuture<Inventory> get(Player player) {
CompletableFuture<Inventory> result = new CompletableFuture<>();
CompletableFuture.supplyAsync(() -> {
auctionAPI.getOffers(offers -> {
//process the offers, then return the result of the processing, in form of an "Inventory"-Object.
result.complete(inventory);
}
});
return null;
});
return result;
}

Emit single item while update UI about progress in RxJava / RxAndroid

I'm currently trying to learn RxJava in Android. I require some guides.
At the moment, I'm trying to rewrite AsyncTask below to RxJava:
public class MyAsyncTask extends AsyncTask<Void, ProgressInfo, Result> {
#Override
protected Result doInBackground(Void... void) {
//Long running task
publishProgress(progressInfo);
//Long running task
return result;
}
#Override
protected void onProgressUpdate(ProgressInfo... progressInfo) {
//Update the progress to UI using data from ProgressInfo
}
#Override
protected void onPostExecute(Result res) {
//Task is completed with a Result
}
}
In AsyncTask approach shown above, I can update the UI about the progress by making use of onProgressUpdate method, I pack every data I needed into ProgressInfo and reflect the UI in onProgressUpdate. After task ends, the Result will be passed from from doInBackground to onPostExecute.
But, when I'm trying to implement this with RxJava, I have a hard time dealing with it. Since I cannot pass any parameter to onComplete in Observer. And thus, I ended up with following implementation. I merged the pass of the ProgressInfo and Result into onNext.
Observable.create(emitter -> {
//Long running task
emitter.onNext(progressInfo);
//Long running task
emitter.onNext(result);
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object -> {
if(object instanceof ProgressInfo){
//Update the progress to UI using data from ProgressInfo
}else if(object instanceof Result){
//Task is completed with a Result
}
});
QUESTION 1: Is my implementation/concept in RxJava right or wrong?
Although it works, I personally feels the implementation above strange and wrong to me. Since the task ultimately is just trying to do some calculations and come out with a single item - Result. The emission of ProgressInfo is like a "side" thing but not "main" thing. I should implement it with Single.create(). But if I did this, I cannot think of any way to pass any ProgressInfo to my UI.
QUESTION 2:
Is there a better idea/way to emit single item while updating the UI during the process?
If yes, how would you implement this logic in RxJava? Can you show me your codes/examples?
QUESTION 1: Is my implementation/concept in RxJava right or wrong?
Surely it depends on your use-case. If you want to provide feedback on each progress-step, there is no way, which I am aware of, to do it differently. I would recommand to provide progress feedback, when the task takes quite a few time and you are able to provide meaningful progress-information.
Either use a union of ProgressInfo and Result in one type and test for null or use a marker interface, from which ProgressInfo and Result inherite from.
interface ResultT { }
final class ProgressInfo implements ResultT { }
final class Result implements ResultT { }
When the result is emitted via onNext, I would recommand to complete the observable, in order to give notice to the subscriber, that the task has been done. The subscriber will receive the result via onNext and a onComplete afterwards.
Observable.<ResultT>create(emitter -> {
emitter.onNext(progressInfo);
emitter.onNext(result);
emitter.onComplete();
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object -> {
if (object instanceof ProgressInfo) {
//Update the progress to UI using data from ProgressInfo
} else if (object instanceof Result) {
//Task is completed with a Result
}
});
If you have no meaningfull progress-information, I would recommend using a Single.
QUESTION 2: Is there a better idea/way to emit single item while updating the UI during the process?
The doOn*-Operators could be used, to update the UI on subscription and termination. This way is one of the easiest, but could cause problems, when events from other subscriptions interleave with UI changes^1
.doOnSubscribe(disposable -> {/* update ui */})
.subscribe(s -> {
// success: update ui
},
throwable -> {
// error happened: update ui
},
() -> {
// complete: update ui
});
My recommandation would be modelling all States (e.g. Success/ Error) via a class and switch-case in the the subscribe-method (see ^1). First emit an StartProgress-event, then the ProgressInformation ones and on finish the SucessResult. Catch any errors with onError*-operators and return a FailureResult, which contains a error-message and maybe the throwable.
Observable.<ResultT>create(emitter -> {
emitter.onNext(progressInfo);
emitter.onNext(result);
emitter.onComplete();
}).startWith(new StartProgress())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.onErrorReturn(throwable -> new FailureResult(throwable))
.subscribe(object -> {
// when StartProgress -> updateUI
// when ProgressInformation -> updateUI
// ...
});
^1 http://hannesdorfmann.com/android/mosby3-mvi-1
1- Create a data class called ProgressInfo
data class ProgressInfo(val progress: Float,val downloadedFile: File, val isCompleted: Boolean = false )
2- Create observable
Observable.create<ProgressInfo> { emitter ->
try {
val url = URL("mediaUrl")
val targetFile = File( "filePath")
if (targetFile.exists().not() && targetFile.createNewFile()) {
val openConnection = url.openConnection()
openConnection.connect()
val totalBytes = openConnection.contentLength
val openStream = openConnection.inputStream
var downloadedBytes = 0f
openStream.use { inStream ->
FileOutputStream(targetFile).use { outStream ->
val streamSlice = ByteArray(1024)
while (true) {
val read = inStream.read(streamSlice)
if (read == -1) {
// file download complete
val progressInfo =
ProgressInfo(
(downloadedBytes / totalBytes) * 100f,
targetFile,
true
)
emitter.onNext(progressInfo)
break
}
downloadedBytes += read
outStream.write(streamSlice)
// update progress
emitter.onNext(
ProgressInfo(
(downloadedBytes / totalBytes) * 100f,
targetFile
)
)
}
}
}
}
emitter.onComplete()
} catch (ex: Exception) {
emitter.onError(ex)
}
}.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
// update your progress here
}, {
// on error
},{
// on complete
})

RxJava: How can I wrap a multi-step operation, returning the step complete to observers?

Lets say I have a login and user data method, representing HTTP calls:
Single<LoginResponse> login();
Single<UserData> userData();
I need to call login() then userData(). If both succeed, the user is logged in.
I know how to wrap them up in a e.g. Completable:
Completable performLogin() {
login().doOnSuccess(this::storeLoginResponse)
.flatMap(userData())
.doOnSuccess(this::storeUserData)
.doOnError(this::wipeLoginData)
.toCompletable();
}
So the UI then says
showLoading();
performLogin().subscribe(() -> {
stopLoading();
onLoginSuccess();
}, error -> {
stopLoading();
onLoginFailure();
});
What if the UI needs to show which stage of the loading is happening? As in, when the login() call completes and the userData() call starts it will change the UI?
What I thought of is something like
Observable<LoginStage> performLogin() {
return Observable.create(emitter -> {
login.doOnSuccess(response -> {
storeLoginResponse(response)
emitter.onNext(LOGIN_COMPLETE)
}).flatMap(userData())
.subscribe(userData -> {
storeUserData(userData);
emitter.onNext(USER_DATA_COMPLETE)
emitter.onComplete();
}, error -> {
wipeLoginData();
emitter.onError(error);
});
});
}
But it feels like there's a nicer or more Rx-y way to do it.
You can use hot observables and chain one observable to another Subject and pass all items form one emission to another if you need it.
#Test
public void chainObservablesWithSubject() throws InterruptedException {
Observable<Long> observable = Observable.from(Arrays.asList(1l, 2l, 3l, 4l));
Subject<Long, Long> chainObservable = ReplaySubject.create(1);
observable.subscribe(chainObservable);
chainObservable.subscribe(System.out::println, (e) -> System.err.println(e.getMessage()), System.out::println);
}
You can check more examples here https://github.com/politrons/reactive/blob/master/src/test/java/rx/observables/connectable/HotObservable.java

Observable.publish() doesn't call onCompleted() on observers that subscribe after the source Observable is done

I'm trying to get an Observable to share its emissions with all the subscribers, so that it would be subscribe()d to exactly once.
I tried using Observable.publish(), but it appears that subscribers to the published Observable don't receive any termination messages( onCompleted() and possibly onError()) if they subscribe after the source Observable is done. Here is a piece of code to demonstrate that:
static <T> Observer<T> printObserver(String name) {
return new Observer<T>() {
#Override public void onCompleted() {
System.out.println(name + ": onCompleted()");
}
#Override public void onError(Throwable e) {
System.out.println(name + ": onError( " + e + " )");
}
#Override public void onNext(T value) {
System.out.println(name + ": onNext( " + value + " )");
}
};
}
public void testRxPublishConnect() throws Exception {
Observable<Integer> sourceObservable = Observable.range(1, 5);
ConnectableObservable<Integer> sharedObservable = sourceObservable.publish();
sharedObservable.subscribe(printObserver("Observer #1"));
sharedObservable.connect();
sharedObservable.subscribe(printObserver("Observer #2"));
}
This is what gets printed:
Observer #1: onNext( 1 )
Observer #1: onNext( 2 )
Observer #1: onNext( 3 )
Observer #1: onNext( 4 )
Observer #1: onNext( 5 )
Observer #1: onCompleted()
Note that Observer #2 doesn't receive onCompleted().
I don't think this is the desired behavior. Am I missing something?
I tried it in RxJava versions 1.0.8 and 1.0.14 with the same result.
Try .share() which is .publish().refCount().
This is by design. If you call connect() in this case, your subscriber will receive all events from the start. If a terminated publish would terminate its child subscribers immediately, you likely couldn't observe values because once connected, publish ticks away its source slowly if there are no subscribers to it.
I'm 99% sure this is the expected behavior. I'm not sure about RxJava, but in most of the implementations of the publish&subscribe pattern that I know of, the default behavior for an observable is to publish events to subscribers and forget about them. This means that notifications are not 'retro-active' (i.e. subscribers don't get to know anything about the events emitted in the past).
Also, from the Observable Contract (section 'multiple observers') of the RxJava documentation :
If a second observer subscribes to an Observable that is already emitting items to a first observer, it is up to the Observable whether it will thenceforth emit the same items to each observer ... There is no general guarantee that two observers of the same Observable will see the same sequence of items.
Publish works by building a list of all subscribers then once connect() is called it starts producing data to all subscribers in it's subscriber list. This means all the subscribers have to be known before calling connect. Here's how you would use publish() or possibly more preferable the publish(Func1<Observable<T>, Observable<R>>) overload.
Known number of subscribers: Publish
Func closing over all subscriptions.
observableStream.publish(new Func1<Observable<Integer>, Observable<Integer>>() {
#Override
public Observable<Integer> call(Observable<Integer> subject) {
Observable<Integer> o1 = subject.doOnNext(somework1());
Observable<Integer> o2 = subject.doOnNext(somework2());
return Observable.merge(o1, o2);
}
});
Manual call to connect and subscribe:
ConnectableObservable<Integer> subject = observableStream.publish();
subject.subscribe(somework1());
subject.subscribe(somework2());
subject.connect();
If you don't know how many subscribers you'll have then you can window the inputs to manageable chunks and then publish your inputs over your collection of Transformers.
Unknown number of subscribers: Window
final Set<Transformer<Integer, String>> transformers = new HashSet<>();
observableStream
.window(100, TimeUnit.MILLISECONDS, 1000)
.flatMap(new Func1<Observable<Integer>, Observable<String>>(){
#Override
public Observable<String> call(Observable<Integer> window) {
return window.publish(new Func1<Observable<Integer>, Observable<String>>() {
#Override
public Observable<String> call(Observable<Integer> publish) {
Observable<Observable<String>> workObservables = Observable.from(transformers)
.map(new Func1<Transformer<Integer, String>, Observable<String>>(){
#Override
public Observable<String> call(Transformer<Integer, String> transformer) {
return publish.compose(transformer);
}});
return Observable.merge(workObservables);
}});
}})
.subscribe();
There is a third option. You could use observable.cache() but this will hold all input data from that observable stream in memory so you want to be careful with how you use that. In that case you'll probably end up windowing anyway to control the bounds of your cached subject.

Categories