RxJava on the Sign In Screen
Before I dive into an example of using RxJava, I’d like to talk a little bit about my journey trying to grok RxJava. What exactly is this thing called RxJava?
RxJava is a Java VM implementation of ReactiveX (Reactive Extensions): a library for composing asynchronous and event-based programs by using observable sequences.
When I first read this it didn’t make a whole lot of sense. So I decided to read blog after blog about the topic and look at the reference material https://github.com/ReactiveX/RxJava/wiki . I realized there are so many different ways that RxJava can be used that the only way I could truly learn was by going over examples. Along the way I created a list of bullet points, that I picked out from all the material that I read, that helped to define RxJava. I highly recommend you do the same. Here is my list of notes on the topic : https://gist.github.com/lawloretienne/40959a8d95c8d0bf4675999d07df3c0c
Once I started doing this, I started becoming more and more comfortable with using RxJava. I am by no means an expert on this subject matter. However, I would like to share a recent problem I was trying to solve and how RxJava helped me solve it.
A common screen that appears in almost every app that requires you to have an account is the Sign In screen. I wanted to set up form validation so that only when all fields are valid then a Sign In button would be enabled. That way the client takes care of this responsibility and doesn’t put the onus on the server.

I set up three checks. A check for the validity of the email input field, a check for the validity of the password input field, and a check for the validity of all fields.
Below are the business rules for the checks.
Check #1
- Do not display errors for an empty email input field.
- If there currently is an error immediately clear it as the email input field changes.
- If the user starts typing, don’t immediately check for validity but instead wait for 400 ms to pass after the last change was made to the email input field.
- If the validity check returns true clear the error view from the email input field, otherwise display the error view on the email input field.
Check #2
- Do not display errors for an empty password input field.
- If there currently is an error immediately clear it as the password input field changes.
- If the user starts typing, don’t immediately check for validity but instead wait for 400 ms to pass after the last change was made to the password input field.
- If the validity check returns true clear the error view from the password input field, otherwise display the error view on the password input field.
Check #3
- Do not enable the SignIn button until both the email input field and password input field are valid.
Here is what those checks look like.
Check #1
Subscription emailSubscription = emailChangeObservable
.doOnNext(new Action1<CharSequence>() {
@Override
public void call(CharSequence charSequence) {
hideEmailError();
}
})
.debounce(400, TimeUnit.MILLISECONDS)
.filter(new Func1<CharSequence, Boolean>() {
@Override
public Boolean call(CharSequence charSequence) {
return !TextUtils.isEmpty(charSequence);
}
})
.observeOn(AndroidSchedulers.mainThread()) // UI Thread
.subscribe(new Subscriber<CharSequence>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onNext(CharSequence charSequence) {
boolean isEmailValid = validateEmail(charSequence.toString());
if (!isEmailValid) {
showEmailError();
} else {
hideEmailError();
}
}
});
Check #2
Subscription passwordSubscription = passwordChangeObservable
.doOnNext(new Action1<CharSequence>() {
@Override
public void call(CharSequence charSequence) {
hidePasswordError();
}
})
.debounce(400, TimeUnit.MILLISECONDS)
.filter(new Func1<CharSequence, Boolean>() {
@Override
public Boolean call(CharSequence charSequence) {
return !TextUtils.isEmpty(charSequence);
}
})
.observeOn(AndroidSchedulers.mainThread()) // UI Thread
.subscribe(new Subscriber<CharSequence>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onNext(CharSequence charSequence) {
boolean isPasswordValid = validatePassword(charSequence.toString());
if (!isPasswordValid) {
showPasswordError();
} else {
hidePasswordError();
}
}
});
Check #3
Subscription signInFieldsSubscription = Observable.combineLatest(emailChangeObservable, passwordChangeObservable, new Func2<CharSequence, CharSequence, Boolean>() {
@Override
public Boolean call(CharSequence email, CharSequence password) {
boolean isEmailValid = validateEmail(email.toString());
boolean isPasswordValid = validatePassword(password.toString());
return isEmailValid && isPasswordValid;
}
}).observeOn(AndroidSchedulers.mainThread()) // UI Thread
.subscribe(new Observer<Boolean>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onNext(Boolean validFields) {
if (validFields) {
enableSignIn();
} else {
disableSignIn();
}
}
});The complete example is here : https://github.com/lawloretienne/RxSignIn . It’s never too late to start learning the Rx approach. You just have to be willing to dive in, make mistakes, and learn by example.
RxJava Resources
Official Docs
https://github.com/ReactiveX/RxJava/wiki
Videos
Helpful Tools
Blog Posts
http://fernandocejas.com/2015/01/11/rxjava-observable-tranformation-concatmap-vs-flatmap/
https://www.bignerdranch.com/blog/what-is-functional-reactive-programming/
http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/