36

I am learning Observer pattern, I want my observable to keep track of a certain variable when it changes it's value and do some operations, I've done something like :

public class Test extends MyChildActivity { private int VARIABLE_TO_OBSERVE = 0; Observable<Integer> mObservable = Observable.just(VARIABLE_TO_OBSERVE); protected void onCreate() {/*onCreate method*/ super(); setContentView(); method(); changeVariable(); } public void changeVariable() { VARIABLE_TO_OBSERVE = 1; } public void method() { mObservable.map(value -> { if (value == 1) doMethod2(); return String.valueOf(value); }).subScribe(string -> System.out.println(string)); } public void doMethod2() {/*Do additional operations*/} } 

But doMethod2() doesn't get called

1
  • I left Java long time ago. Here is my thought. Because VARIABLE_TO_OBSERVE is copied to Observable. Hence, it isn't observed. How about private Integer VARIABLE_TO_OBSERVE = 0;? Commented Aug 3, 2016 at 9:21

3 Answers 3

48

Nothing is magic in the life : if you update a value, your Observable won't be notified. You have to do it by yourself. For example using a PublishSubject.

public class Test extends MyChildActivity { private int VARIABLE_TO_OBSERVE = 0; Subject<Integer> mObservable = PublishSubject.create(); protected void onCreate() {/*onCreate method*/ super(); setContentView(); method(); changeVariable(); } public void changeVariable() { VARIABLE_TO_OBSERVE = 1; // notify the Observable that the value just change mObservable.onNext(VARIABLE_TO_OBSERVE); } public void method() { mObservable.map(value -> { if (value == 1) doMethod2(); return String.valueOf(value); }).subScribe(string -> System.out.println(string)); } public void doMethod2() {/*Do additional operations*/} } 
Sign up to request clarification or add additional context in comments.

4 Comments

I was under the impression that an observer would immediately notify all its subscribers every time the object it observes changes. So basically we need to keep telling all those subscribers so they listen to any changes ?
You need to keep telling all your Observable that something changes. Then all subscribers will be notified.
I was wondering what if I would like to asynchronously return something from the map inside method. I mean return String.valueOf(value); is an asynchronous call.
I think the requirement to track 'variable' changes has been interpreted too literally here... the result does not read easily at all. if we allow ourselves the use of regular Java accessors to a value, things become neater. See my answer.
46

If interested here a Kotlin version of Variable class, which lets subscribers to be updated after every variable change.

class Variable<T>(private val defaultValue: T) { var value: T = defaultValue set(value) { field = value observable.onNext(value) } val observable = BehaviorSubject.createDefault(value) } 

Usage:

val greeting = Variable("Hello!") greeting.observable.subscribe { Log.i("RxKotlin", it) } greeting.value = "Ciao!" greeting.value = "Hola!" 

This will print:

"Hello!" "Ciao!" "Hola!" 

5 Comments

How to unsubscribe from greeting.observable?
@BogdanStolyarov I usually dispose them. private val compositeDisposable = CompositeDisposable() ... greeting.subscribe {}.addTo(compositeDisposable) ... compositeDisposable.dispose() Hope this helps.
@Bogdan Stolyarov you can use library Auto Dispose from uber.
For observable.onNext(value) I'm getting the following warning: Type mismatch: type parameter with nullable bounds is used T is used where T was expected. This warning will become an error soon - do you have any idea how to fix this?
Ok simply checking null by value?.let { seems to be enough.
13

@dwursteisen Nothing is magic, no, but I think we can get it a little more magic than that... 😊

How about using an Rx BehaviourSubject in this way:

import rx.functions.Action1; import rx.subjects.BehaviorSubject; public class BehaviourSubjectExample { public BehaviourSubjectExample() { subject.skip(1).subscribe(new Action1<Integer>() { @Override public void call(Integer integer) { System.out.println("The value changed to " + integer ); } }); } public final BehaviorSubject<Integer> subject = BehaviorSubject.create(0); public int getValue() { return subject.getValue(); } public void setValue(int value) { subject.onNext(value); } } 

Remove the .skip(1) if you want the observing code to see the initial value.

The variable backing remains with the BehaviourSubject and can be accessed through conventional Java Getter/Setter. This is a toy example of course: If your use case were really this simple there'd be no excuse for not just writing:

private int value = 0; public int getValue() { return value; } public void setValue(int value) { this.value = value; System.out.println("The value changed to " + value ); } 

...but the use of BehaviourSubject lets you bridge changes to other Rx data-streams inside your class for composing more advanced behaviours.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.