So i have a scenario in which i want to combine the newest results of two flowables and do something with it.
Flowable.combineLatest(
info,
list,
BiFunction { ... }
)
In certain conditions, i need to be able to get the the results again, and do some different stuff from before. So i could manually store the results of combinelatest somewhere, and then just reuse them but i was thinking, maybe there is a way to add a third flowable, and trigger onNext manually so the results are propagated again. Is this possible?
There are two approaches to keeping the computed value around for later use. You can create a BehaviorSubject that acts as an intermediate variable, that when defined will have the computed value, or you can publish() the observable so that newer subscribers will get the most recent results.
BehaviorSubject intermediateResult = BehaviorSubject.create();
Flowable.combineLatest(info, list, ...)
.subscribe( intermediateResult );
Alternatively,
Observable<Type> intermediateResult = Flowable.combineLatest(info, list, ...)
.replay(1)
.publish();
In either case, subscribing to intermediateResult will get the most recent computed value, if it is present.
Edit: make the function selectable on the fly:
Observable<FunctionSelector> fnSelector;
Observable<Type> intermediateResult =
Flowable.combineLatest(info, list, fnSelector,
(information, listToUse, selector) ->
getFunction(selector).apply(information, listToUse))
.replay(1)
.publish(1);
Related
My observable looks like following:
obs
.doOnNext { logger("Items changed (${it.size})") }
.distinctUntilChanged()
.doOnNext { logger("Items changed (${it.size})- EMIITTED") }
Log looks like following:
Items changed (7)
Items changed (7)- EMIITTED
Items changed (8)
// => missing EMIITTED message although it.size has changed => WHY?
Using the default comparator with a list of comparable items seems to fail here. Why? If the observables emitted list item size changed, the data is different, so distinctUntilChanged should not filter out the new list. But it seems like this happens here. Why?
Do I really need to provide my own comparator for distinctUntilChanged if I emit a list of items that compares the list size and the items one by one?
Edit
My obs basically looks like following:
obs = Observable.combineLatest(
RxDBDataManager.appsManager.observeList(),
RxDBDataManager.widgetsManager.observeList(),
RxDBDataManager.shortcutsManager.observeList(),
RxDBDataManager.customItemsManager.observeList(),
RxDBDataManager.foldersManager.observeList(),
Function5<List<IDBApp>, List<IDBWidget>, List<IDBShortcut>, List<IDBCustomItem>, List<IDBFolder>, List<IFolderOrSidebarItem>> { t1, t2, t3, t4, t5 ->
val list = ArrayList<IFolderOrSidebarItem>()
list.addAll(t1)
list.addAll(t2)
list.addAll(t3)
list.addAll(t4)
list.addAll(t5)
list
}
.flatMapSingle {
Observable.fromIterable(it)
.filter { it.parentType == parentType && it.parentId == parentId }
.cast(T::class.java)
.toList()
}
.flatMapSingle {
Observable.fromIterable(it)
.sorted(comparator)
.toList()
}
Additionally I apply some sorting and filtering on this data with
Based on the exchange in the comments:
RxJava users are encouraged to use immutable data types in its flows which prevents concurrency issues such as modifying the same object at different stages from different threads resulting in broken operator behavior and seemingly impossible business logic failures.
In this case, distinctUntilChanged didn't work as expected because mutable items were changed in a way that two subsequent onNext signals basically had the same content and the operator filtered them out as being non-distinct.
A way to detect if the items involved are in fact the same unintentionally is to use the bi-predicate version of the operator and then placing a breakpoint in the custom lambda. This lets one inspect the previous and current values and see if they are truly equal even if they shouldn't be:
source.distinctUntilChanged((prev, curr) -> {
// breakpoint the next line
return prev.equals(curr);
});
As in this case, broken behavior was due to a mutable item changed somewhere and thus evaluating as the same as the current/previous. With Lists, it is often not practical to breakpoint all mutation methods (such as add, addAll, set, remove etc.) but one can turn a mutable list into an immutable one and send it along the sequence. The built-in way is to convert it via the Collections::unmodifiableList:
source
.toList()
.map(Collections::unmodifiableList)
;
This will crash whenever a mutation is attempted on the now unmodifiable list instance, pointing to the logic that should be investigated further.
I'm making an Android application. I've to zip results from two places: One from SharedPreferences as a Maybe and other from the Room Persistence library(basically SQLite) as a Flowable.
I'm using the following code:
repository.getMaybe()
.subscribeOn(Schedulers.io)
.toSingle()
.zipWith(repository.getFlowable().single(DEFAULT VALUE), BiFunction { t1: DataType1, t2: DataType2 -> Pair(t1, t2) }
.subscribe()
repository.getMaybe() is the Maybe source mentioned in the first paragraph. Likewise, repository.getFlowable() is the Flowable source.
I've tried using doOnEvent(to Log statements) on the Maybe source, the Flowable source and the zipped source. Only the Maybe source emits successfully. Others don't do anything at all.
The Flowable source is used in various other parts of my application and it is not at all an issue.
What am I doing wrong?
If your Maybe does not return a value then calling toSingle would result in an Single.error. This would mean it would reach the zipWith as an arrow and it would never bother evaluating the value, since there is no value to zip with.
Thanks to Akarnokd, I tried removing the single(DEFAULT ITEM) part. It worked. On looking up the documentation at http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/Flowable.html, I came across this:
single(T defaultItem)
Returns a Single that emits the single item
emitted by the source Publisher, if that Publisher emits only a single
item, or a default item if the source Publisher emits no items.
Basically, the Flowable should emit only once. So, I'm using firstOrError() in it's place.
I have two Flowable list
and I need to combine them ,apply some functions and get Flowable list
the initial Flowable lists represent data from DB
and the idea is that when in the DB could be changes the combined list could be changed as well as the inputs are changed.
I guess zip isn’t the right approach as it once works complete it’s job and doesn’t continue to emit changes.
My question what could be alternative of zip where I could combine 2 lists ,apply some functions and continue to listen updates
Flowable.zip(shoppingListsRepository.loadCommonArticles(), shoppingListsRepository.loadShoppingListItems(shoppingListId),
BiFunction<List<CommonArticle>, List<ShoppingListItem>, List<CommonArticle>> {
commonArticles, shoppingListItems ->
//apply some filters on these two list and return result
items
});
fun loadCommonArticles(): Flowable<List<CommonArticle>> {
return shoppingListDao.loadCommonArticles()
}
fun loadShoppingListItems(shoppingListId:
Int):Flowable<List<ShoppingListItem>> {
return shoppingListDao.loadShoppingListItems(shoppingListId)}
Consider Flowable.combineLatest(stream1, stream2, combineBiFunc).
If stream1 ends, stream2 will still generate emissions with the last known list from stream1 (and vice versa). In the combineBiFunc, you can decide what to do with the updated data.
Given the following input:
Observable<Class1> obs = {{super complex long method}}
List<Class2> inputList = {{simple list}}
I'd like to be able to create the following:
Observable<Class3> output
that emits the result of applying method input.transform(Class1 c) to each of the inputs in the inputList.
What I've come up with so far is a combination of zip & repeat:
Observable<Class3> output = Observable.zip(obs.repeat(), Observable.from(inputList),
(class1, class2) -> class2.transform(class1));
However, the repeat is way too extreme, it emits multiple repeated items before the zip kicks in.
Another thing I tried was using combineLatest, but since my List emits first, I ended up with only the last item of the list being combined with the class1 instance.
What other combination of operators might make sense?
You can just change the parameter order, like zip(Observable.from(inputList), obs.repeat(), ...).
zip will subscribe the first Observable and then the second Observable. In your example, the first Observable is infinite (e.g., obs.repeat()), RxJava will request 128 items at first. That's why you saw obs.repeat() emitted a lot of items before subscribing the second Observable.
If changing the parameter order to Observable.from(inputList), Observable.from(inputList), RxJava will subscribe Observable.from(inputList) at first, since it's a synchronous Observable, RxJava will consume it and know its length at once (assume its length is less than 128), then RxJava will request items from the second obs.repeat() using this length. So it won't require more than the necessary items.
It sounds like what you want to do is to take each input from obs, apply a set number of functions defined in a list to each of the items in obs, then flatten that output back into an Observable of type Class3. In that case I think flatMap is a good choice, because it clearly signals the intent: that you are applying many functions per item of input, then flattening the stream.
Here's an example (pardon the Java6-ness of it):
Observable<Class3> output = obs.flatMap(new Func1<Class1, Observable<Class3>>() {
#Override
public Observable<Class3> call(final Class1 class1) {
return Observable.from(inputList).map(new Func1<Class2, Class3>() {
#Override
public Class3 call(Class2 class2) {
return class2.transform(class1);
}
});
}
});
I need an Observable that never ends, and just process some data and chain another observable when there are items on a list. Is there any way of accomplish that, and what would be the best approach=?
My closest idea was to create a timer observable and check every x seconds if there are items on the list. This idea is not ideal, because i need to process the data as soon as there are values on that list, which i modify outside the observable chain.
return Observable.timer(2, TimeUnit.SECONDS)
.flatMap(integer -> captureList.getLatestCaptureCut())
.flatMap(vp::processVideo)
.observeOn(AndroidSchedulers.mainThread())
.repeat()
I think you can use Subject, and push your next items there.
PublishSubject<Integer> subject = PublishSubject.<Integer>create();
subject.flatMap(integer -> captureList.getLatestCaptureCut())
.flatMap(vp::processVideo)
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
//push new items
subject.onNext(0);
subject.onNext(1);
I would suggest a PublishSubject in your CaptureList class. Instead of providing a pull method getLatestCaptureCut(), you could provide a push method, with a Subject:
PublishSubject<VP> captured = PublishSubject.create();
You could then .subscribe() to the PublishSubject and process the data when they come in.
In your CaptureList you would call
captured.onNext(vp);
every time new data is available. For instance, in your setLatestCaptureCut(). I'm assuming you already have some kind of routine that generates the CaptureCut and store it, to make it available in getLatestCaptureCut().