RxJava : How to send some additional info to subscriber - java

I have created a simple method, which will send message ( as Object ) to given subject. At other end subscriber will consume the object.
Am going to use same subject for multiple consumers, hence while sending message want to send some additional info i.e. 200, 300. So consumer can filter out un-relevant messages. But somehow not able to figure out how to do that.
Additional info (int parameter) can be handled at postToSubject method and at action1 but not sure how to place it here subject.onNext(object)
Please suggest if you can think any solution.
public void postToSubject(Object object, Subject subject) {
Observable<Object> observable = Observable.create(new Observable.OnSubscribe<Object>() {
#Override
public void call(Subscriber<? super Object> subscriber) {
subscriber.onNext(object);
subscriber.onCompleted();
}
});
observable.subscribe(new Action1() {
#Override
public void call(Object object) {
subject.onNext(object);
}
});
}
Above method is called as below ( from an Android Activity, objective is to send item to subject. Subject will be returned by second argument in the method )
At another Android Component Class ( inherited from Linear Layout ) , this message will be consumed as below. If I can get additional info i.e. 200, 300 , I will filter out and put onNext() inside it. This is the way I can think of a solution, however a RxJava expert may give a good solution.
Answer :
Additional Info passed through onNext() as below, subscriber will check using if (object instanceof Integer) and put the logic to fetch the next value returned by next onNext()

First, I would not use Object as parameter/message type because it is too generic. Make some base class, say Item, carrying basic information.
Second, create subclasses of the Item, say MenuItem, ButtonItem, ParametrizedItem, and so on. Each subtype can carry additional information.
Third, this is what postToSubject() should look like:
public void postToSubject(Item object, Subject<Item> subject) {
subject.onNext(object);
}
(no need for Observable.create...)
Fourth, on consumer side use filter() operator:
subject
.filter(i -> i instanceof MenuItem)
.subscribe(menuItemSubscriber);
subject
.filter(i -> i instanceof ParametrizedItem)
.subscribe(paramertizedItemSubscriber);
Now that each particular subscriber knows what subtype items it is going to receive, it can safely cast to that subtype, and extract additional information pertaining to that subtype.

Related

how to convert asynchronous promise code to rxjava

I have the following synchronous code that I would like to model as async code in RXJava.
void executeActions(List<Action> action) {
if (action == null || action.size() == 0) return;
for (Action action: actions) {
executeActions(action.handle());
}
}
class Action {
//implementation of handle
// return List<Action> or null.
List<Action> handle() {
}
}
Now in JS I can model this interaction with Promises like so. (Pseudo code below - my JS is weak)
executeActionsAsync(actions) {
var p = Promise.resolve();
action.forEach(function(action) {
p = p.then(function() {
action.handle();
})
}
return p;
}
class Action() {
function handle() {
actions = [];// some array of actions.
executeAsync(actions);
}
}
I would like to model the same in RXJava2. Any help is appreciated.
First of all, Sorry for my bad English.
I edited entire answer because I did not catch what his question is.
I don't know how implement of your Action class's handle function, However this function return value should change to RxJava2's async classes. In this case, Maybe class.
You wants how to implements recursion of async.
Handle List or null.
Use Maybe if you want to handle something or null. in RxJava2
class Action {
Maybe<List<Action>> handle() {}
}
This is what your Action class's handle returns.
void executeActions(Maybe<List<Action>> rxactions) {
// add null check.
// List<Action> handles as stream, but you can use for or iterator or whatever you want.
rxactions.subscribe(actions -> actions.stream().map(action -> executeActions(action.handle())));
}
Important thing is, handle() function returns properly.
Additional
In RxJava2, There are multiple classes to handle async.
Single, Flowable, Observable, Completable. And each classes instance method, subscribe.
Simply say,
1.Single => returns single class.
2.Flowable, Observable => returns multiple classes. (Flowable is more complex than Observable, which added back pressure.)
3.Completable => returns nothing, just succeed or not.
4.Maybe is returns * or null.
5.subscribe is execute this async.
:: Each classes can convert easily.
:: And There are so many ways to solve one problem. so it is just reference.
ex) Single<List<Foo>> <=> Flowable<Foo> // This is not same. but treat as similar.
PS.
I had this experience too. I think you need to learn more about RxJava2 to use properly everywhere.
Promise can devide into Single, Flowable, Observable, Completable. As describe above. This is the KEY to start understanding RxJava2.

How does this asynchronous call work in my example

I learn Java and wonder if the item in this code line:
useResult(result, item);
Will be overrwritten by the next call coming from the
doItem(item);
Here´s the eaxmple:
public void doSomeStuff() {
// List with 100 items
for (Item item : list) {
doItem(item);
}
}
private void doItem(final Item item) {
someAsyncCall(item, new SomeCallback() {
#Override
public void onSuccess(final Result result) {
useResult(result, item);
}
});
}
the SomeCallback() happens some time in the future and it´s another thread
I mean will the useResult(result, item); item be the same when callback return?
Please advice what happens here?
I mean will the useResult(result, item); item be the same when callback return?
Of course it will, what would the utility of that be otherwise?
What you are doing is creating 100 different SomeCallback classes, that will process a different Item object.
A skeleton for your someAsyncCall may look like this:
public static void someAsyncCall(Item i, Callback callback) {
CompletableFuture.runAsync( () -> { // new thread
Result result = computeResult(i);
callback.onSuccess(result, i);
});
}
The point is: Callback, at the moment of instantiation, doesn't know anything about the Item he will get as parameter. He will only know it, when Callback::onSuccess is executed in the future.
So, will Item i change (be assigned a new object) ?
No, because it is effectively final within someAsyncCall (the object value is not explicitly changed).
You can't even assign i = new Item(), as the compiler will complain about the anonymous function accessing a non-final variable.
You could of course create a new Item and pass it to the callback
Item i2 = new Item();
callback.onSuccess(result, i2);
but then it would become one hell of a nasty library...
Nobody forbids you to do i.setText("bla") though, unless your Result class is immutable (the member fields are final themselves).
EDIT
If your questions is how java handles object in method parameters, then the answer is: yes, they are a just copy of the original instances.
You could try with a simple swap method void swap(Item i1, Item 12); and you'll notice the references are effectively swapped only within function, but as soon as you return the objects will point respectively to their original instances.
But it's a copy that reflects the original instance.
Coming back to your example. Imagine your someAsyncCall waits 10000 ms before executing the callback.
in your for loop, after you call doItem, you also do: item.setText("bla");.
When you print item.getName() within useResult you will get bla. Even though the text was changed after the async function was called.

Issue with using 'map' in RxJava 2 / RxAndroid 2

I'm trying to learn RXJAVA for Android. Parts make sense and I'm still confused about a lot of the other bits but, given some time I hope it will all make a lot more sense.
At present I'm having trouble with the 'map' functionality. I'm receiving an error but cannot quite figure out how to resolve it.
Before I share my code, I'll explain my understanding..
At a simple level..
Observable - Code that emits data.
Observer - Code that processes the emitted data.
Map - Code that takes in data of type A and returns it processed or as type B.
So, with this in mind:
In gradle I have:
compile 'io.reactivex.rxjava2:rxjava:2.0.1'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
If I have:
//declaration at top of file
private Observable<Integer> myIntObservable;
private Observer<Integer> myIntObserver;
private Observer<String> myStringObserver;
private Observable<String> myStringObservable;
//usage in a function
myIntObserver = new Observer<Integer>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(Integer value) {
Toast.makeText(getApplicationContext(), "" + value, Toast.LENGTH_SHORT).show();
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
Toast.makeText(getApplicationContext(), "Int Observer Async Complete", Toast.LENGTH_SHORT).show();
}
};
//Connect my Observable to the observer.
myIntObservable.observeOn(Schedulers.io());
myIntObservable.subscribeOn(AndroidSchedulers.mainThread());
myIntObservable.subscribe(myIntObserver);
This all works fine... my map usage is similar..
What I would like to do is use this same observable that returns an int, then use the map code to instead return a string...
Therefore:
myStringObservable
.observeOn(Schedulers.io())
.subscribeOn(AndroidSchedulers.mainThread())
.map(new Function<Integer, String>() {
#Override
public String apply(Integer query){
return "String Observable result == " + query;
}
});
Now, I have two issues:
a) The build error I receive is:
Error:(179, 17) error: method map in class Observable cannot be applied to given types;
required: Function
found: >
reason: cannot infer type-variable(s) R
(argument mismatch; > cannot be converted to Function)
where R,T are type-variables:
R extends Object declared in method map(Function)
T extends Object declared in class Observable
I believe that this is essentially telling me that the types are not correct for my usage but, I can't clearly see... how to resolve this.
b) The map code that I have posted above doesn't connect the observable to what it needs to observe... hence, should I add the subscribe line before the map command?
Therefore, I tried this..
public void setupAsyncSubscription(){
myIntObservable
.observeOn(Schedulers.io())
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(myIntObserver)
.map(new Function<Integer, String>() {
#Override
public String apply(Integer query){
return "String Observable result == " + query;
}
});
}
Whilst this removes the error detailed in 'a' it instead provides me with the following error:
Error:(180, 17) error: void cannot be dereferenced (this points to the 'map' call)
Finally, I can also see that what I 'return' back from the 'map' function isn't being processed at all... I'm not clear how to process that. I feel that I should be using the .subscribe call in that case??
I 'think' that I am slowly on the right path to resolving the issue(s), but I'm not quite there and I don't want to just try and happen upon the answer without understanding what the problem is.
As always, any help is appreciated.
You have multiple issues here. There are - one by one:
//Connect my Observable to the observer.
myIntObservable.observeOn(Schedulers.io());
myIntObservable.subscribeOn(AndroidSchedulers.mainThread());
myIntObservable.subscribe(myIntObserver);
Code above would not work as you probably think.
Operators observeOn, subscribeOn are not designed to change internal observable state. They are returning new observable with desired behaviour.
To accomplish observing on io() thread and subscribing your observable on mainThread() you need to change the code:
//Connect my Observable to the observer.
myIntObservable = myIntObservable.observeOn(Schedulers.io());
myIntObservable = myIntObservable.subscribeOn(AndroidSchedulers.mainThread());
myIntObservable.subscribe(myIntObserver);
Or use (preferred) chaining:
//Connect my Observable to the observer.
myIntObservable
.observeOn(Schedulers.io());
.subscribeOn(AndroidSchedulers.mainThread());
.subscribe(myIntObserver);
For the code same as yours, calling .subscribe() on not changed Observable will result in subscribing and observing on the same thread from which you call .subscribe() (most likely from main thread).
Keep in mind you need to dispose observable once the work is finished.
When it comes to mapping problem - map() operator changes one type of Observable<A> into another type of observable Observable<B>.
If you'd like to end up with String objects converted from Integer objects you need to use - as a source of data - your original myIntObservable:
myStringObservable = myIntObservable
(...)
.map(new Function<Integer, String>() {
#Override
public String apply(Integer query){
return "String Observable result == " + query;
}
});
In above example myIntObservable will emit Integer objects (as expected in .apply(Integer query) method. Then .map() operator will create another Observable of type Observable<String> you can assign to myStringObservable (or do whatever you want from here).
Then, using myStringObservable you can subscribe to its events:
myStringObservable.subscribe(myStringObserver)
Again, please remember to dispose Observable when work is done.
Please also note that you should:
.observeOn() as soon as possible for current piece of work,
.subscribeOn() as late as possible (you don't want to continue io() or computation() operations on your main thread, right?).
Hint at the end: consider using lambdas with RxJava. Using all these anonymous classes (new Function() etc.) will make your code hard to read in the nearest future.
The first problem is that myStringObservable emits object of class String, but you're trying to map function which expects arguments of class Integer. In order to achieve what you want you should use myIntObservable.
The second problem is that subscribe call returns a Disposable object for you and you can't do map after that. Remove that subscribe call and this should be fine. Please also note that your method signature tells that it returns String but it can't return String, it can return Observable<String>.

Java 8 Stateful consumer

Is conditional composition of Consumers possible in Java 8? Basically I'm looking to create a custom Lambda interface similar to Consumer but that only works with one type of object. Let's call it, Stateful and it contains multiple statuses (we'll say two for the purpose of this example):
public class Stateful {
private int status1;
private int status2;
}
We have a lot of areas in our code where we do an operation on a Stateful and, if the status has changed, we would do another operation. I was wondering if we could use composition to handle this in a more compact and elegant manner. Right now we would do something like:
SimpleEntry<Integer, Integer> oldStates = new SimpleEntry(stateful.getStatus1(), stateful.getStatus2());
applyLogicOnStateful(stateful); //do some operation that may change state values
if(isStatusChanged(oldStates, stateful) { //compare oldStates integers to status integers
doSomethingElse(stateful);
}
where I think something like this would look better:
statefulConsumer
.accept((stateful)->applyLogicOnStateful(stateful))
.ifStatusChanged((stateful)->doSomethingElse(stateful));
but I don't know if we would be able to track the change in status from before the first consumer to after. Maybe I need to create a lambda that takes two consumers as input?
I'm definitely looking to do this without the assistance of a 3rd party library, although you're welcome to promote one here if it is helpful.
Here is a function that will return a Consumer<Stateful> that will extract the former state, do the change, compare results, and conditionally operate on the changed object.
public static Consumer<Stateful> getStatefulConsumer(
Function<Stateful,SimpleEntry<Integer,Integer>> getStatus, // extract status from Stateful
Consumer<Stateful> applyLogic, // apply business logic
BiPredicate<SimpleEntry<Integer,Integer>,SimpleEntry<Integer,Integer>> compareState, // test statuses for change
Consumer<Stateful> onChange) // doSomethingElse
{
return stateful -> {
SimpleEntry<Integer,Integer> oldStatus = getStatus.apply(stateful);
applyLogic.accept(stateful);
if(!compareState.test(oldStatus, getStatus.apply(stateful))){
onChange.accept(stateful);
}
};
}
You might use it like this:
Consumer<Stateful> ifChanged = getStatefulConsumer(s -> new SimpleEntry<> ( s.status1, s.status2 ),
s -> changeSomething(s), Objects::equals, s->doSomething(s));
You could generify the extracted status so that different stateful types could have different extracted status types, or even use Stateful::clone to copy the status.
The solution I am working with right now is to create a Lambda interface that takes the Stateful instance and two Consumers as input:
public interface StatefulConsumer {
void accept(Stateful stateful, Consumer<Stateful> consumer, Consumer<Stateful> ifStateChangedConsumer);
}
and an implementation:
final StatefulConsumer IfStateChanges = new StatefulConsumer() {
#Override
public void accept(Stateful stateful, Consumer<Stateful> consumer, Consumer<Stateful> ifStateChangedConsumer) {
SimpleEntry<Integer, Integer> oldStates = new SimpleEntry(stateful.getStatus1(), stateful.getStatus2());
consumer.accept(stateful); //do some operation that may change state values
if(isStatusChanged(oldStates, stateful) { //compare oldStates integers to status integers
ifStateChangedConsumer.accept(stateful);
}
}
};
which could be called like this:
IfStateChanges.accept(stateful,
(Stateful s)->applyLogicOnStateful(stateful),
(Stateful s)->doSomethingElse(stateful))
It could also be implemented as a Predicate or a Function that takes a stateful and a consumer as input and returns a boolean for use in an if Statement

RxJava: Find out if BehaviorSubject was a repeated value or not

I'm making an Android interface that shows some data fetched from the network. I want to have it show the latest available data, and to never be empty (unless no data has been fetched at all yet) so I'm using a BehaviorSubject to give subscribers (my UI) the latest available info, while refreshing it in the background to update it.
This works, but due to another requirement in my UI, I now have to know whether or not the published result was gotten fresh from the network or not. (In other words, I need to know if the published result was BehaviorSubject's saved item or not.)
How can I achieve this? If I need to split it up into multiple Observables, that's fine, as long as I'm able to get the caching behavior of BehaviorSubject (getting the last available result) while also being able to tell if the result returned was from the cache or not. A hacky way I can think of to do it would be to check if the timestamp of the response was relatively soon, but that'd be really sloppy and I'd rather figure out a way to do it with RxJava.
As you mentioned in the question, this can be accomplished with multiple Observables. In essence, you have two Observables: "the fresh response can be observed", and "the cached response can be observed". If something can be "observed", you can express it as an Observable. Let's name the first one original and the second replayed.
See this JSBin (JavaScript but the concepts can be directly translated to Java. There isn't a JavaBin as far as I know, for these purposes).
var original = Rx.Observable.interval(1000)
.map(function (x) { return {value: x, from: 'original'}; })
.take(4)
.publish().refCount();
var replayed = original
.map(function (x) { return {value: x.value, from: 'replayed'}; })
.replay(null, 1).refCount();
var merged = Rx.Observable.merge(original, replayed)
.replay(null, 1).refCount()
.distinctUntilChanged(function (obj) { return obj.value; });
console.log('subscribe 1st');
merged.subscribe(function (x) {
console.log('subscriber1: value ' + x.value + ', from: ' + x.from);
});
setTimeout(function () {
console.log(' subscribe 2nd');
merged.subscribe(function (x) {
console.log(' subscriber2: value ' + x.value + ', from: ' + x.from);
});
}, 2500);
The overall idea here is: annotate the event with a field from indicating its origin. If it's original, it's a fresh response. If it's replayed, it's a cached response. Observable original will only emit from: 'original' and Observable replayed will only emit from: 'replayed'. In Java we would require a bit more boilerplate because you need to make a class to represent these annotated events. Otherwise the same operators in RxJS can be found in RxJava.
The original Observable is publish().refCount() because we want only one instance of this stream, to be shared with all observers. In fact in RxJS and Rx.NET, share() is an alias for publish().refCount().
The replayed Observable is replay(1).refCount() because it is also shared just like the original one is, but replay(1) gives us the caching behavior.
merged Observable contains both original and replayed, and this is what you should expose to all subscribers. Since replayed will immediately emit whenever original does, we use distinctUntilChanged on the event's value to ignore immediate consecutives. The reason we replay(1).refCount() also the merged is because we want the merge of original and replay also to be one single shared instance of a stream shared among all observers. We would have used publish().refCount() for this purpose, but we cannot lose the replay effect that replayed contains, hence it's replay(1).refCount(), not publish().refCount().
Doesn't Distinct cover your case? BehaviorSubject only repeats the latest element after subscription.
I believe what you want is something like this:
private final BehaviorSubject<T> fetched = BehaviorSubject.create();
private final Observable<FirstTime<T>> _fetched = fetched.lift(new Observable.Operator<FirstTime<T>, T>() {
private AtomicReference<T> last = new AtomicReference<>();
#Override
public Subscriber<? super T> call(Subscriber<? super FirstTime<T>> child) {
return new Subscriber<T>(child) {
#Override
public void onCompleted() {
child.onCompleted();
}
#Override
public void onError(Throwable e) {
child.onError(e);
}
#Override
public void onNext(T t) {
if (!Objects.equals(t, last.getAndSet(t))) {
child.onNext(FirstTime.yes(t));
} else {
child.onNext(FirstTime.no(t));
}
}
};
}
});
public Observable<FirstTime<T>> getObservable() {
return _fetched;
}
public static class FirstTime<T> {
final boolean isItTheFirstTime;
final T value;
public FirstTime(boolean isItTheFirstTime, T value) {
this.isItTheFirstTime = isItTheFirstTime;
this.value = value;
}
public boolean isItTheFirstTime() {
return isItTheFirstTime;
}
public T getValue() {
return value;
}
public static <T> FirstTime<T> yes(T value) {
return new FirstTime<>(true, value);
}
public static <T> FirstTime<T> no(T value) {
return new FirstTime<>(false, value);
}
}
The wrapper class FirstTime has a boolean which can be used to see if any subscriber to the Observable has seen it before.
Hope that helps.
Store the information of BehaviorSubject objects in a data structure with a good lookup such as a Dictionnary. Each value would be a key and the value would be the number of iteration.
There so, when you look at a particulary key, if your dictionnary contains it already and its value is already at one, then you know that a value is a repeated value.
I'm not really sure what you want to achieve. Probably you'd just like to have a smart source for the "latest" data and a second source which tells you when the data was refreshed?
BehaviorSubject<Integer> dataSubject = BehaviorSubject.create(42); // initial value, "never empty"
Observable<String> refreshedIndicator = dataSubject.map(data -> "Refreshed!");
refreshedIndicator.subscribe(System.out::println);
Observable<Integer> latestActualData = dataSubject.distinctUntilChanged();
latestActualData.subscribe( data -> System.out.println( "Got new data: " + data));
// simulation of background activity:
Observable.interval(1, TimeUnit.SECONDS)
.limit(100)
.toBlocking()
.subscribe(aLong -> dataSubject.onNext(ThreadLocalRandom.current().nextInt(2)));
Output:
Refreshed!
Got new data: 42
Refreshed!
Got new data: 0
Refreshed!
Refreshed!
Refreshed!
Got new data: 1
Refreshed!
Got new data: 0
Refreshed!
Got new data: 1

Categories

Resources