RxJava form validation in Android with Example

Also,

RxJava/RxAndroid is the hottest thing in Android right now. Everyone wants to use it, Jake Wharton is promoting it and it is all over in the job requirements.

And rightly so. RxJava has numerous benefits:

  • It makes Threading in Android easy

Managing threads in Android is a cumbersome task. You have to deal with all the handlers, loopers, schedulers and what not. You have to remember when to start the thread, when to destroy it.

You have to handle it manually for the configuration changes, and almost every outside event in android is a configuration change. Even rotating the phone is a configuration change.

It is a lot of hard work.

But this is all taken care of in RxJava. Using subsribeOn(), observeOn() we can define the default thread that the rxjava chain would work on.

  • It makes error handling easy

I just hate all those try catch blocks. They make the code look ugly. You have to manually catch all the exceptions and handle that yourself.

But RxJava handles them beautifully. For example: If during a network request, an exception occurs, like OOM error, you can handle the exception in onError of the subscriber.

Your apps won’t blow up in the user’s face, instead RxJava would handle the exception for you and if you chose not to do anything with it, the app would continue running without crashing (although may not produce the results you want).

  • Easy Android Lifecycle handling

As I already mentioned before, handling lifecycle is a pain if we do it the imperative way. We have little control over it.

Android OS could terminate it as needed. Hence, if our app gets killed and we are still performing some background task like downloading, buffering etc. We want to stop it immediately otherwise it would lead to a memory leak.

RxJava handles it beautifully with subscriptions. We have to unsubscribe in our onDestroy() method and that thread would be disposed.

  • Easier networking with Retrofit 2

RxJava provides an elegant way to make HTTP requests in android. Retrofit library is the industry standard for making HTTP calls on android, but we can make it much better by coupling it with RxJava.

That’s the reason retrofit has a separate adapter for RxJava for setting base urls, interceptors etc.

An Operator for everything

There is a saying in RxJava world that there is an operator for everything. There is an operator for mapping a result into something completely different. An operator to make your search look and work like google search (google debounce operator), a filter operator to allow only specific values to pass and lots more.

It’s up to your imagination as to what use case can you think of and there is probably an operator for that, or if not, you can chain several operators to get your result.

 

How to use RxJava in Android

In this tutorial, I am going to illustrate how you can use RxJava in android applications and build apps with much less code.

I am going to build a login application which would take a username and a password and match it with already initialized values to check whether to allow the login or not.

Steps

  1. Create a basic app with two input fields and one submit button
  2. Add dependencies for RxJava and RxAndroid
  3. Create observables for those input fields
  4. Apply validations
  5. Create an observer to listen to those observables.

 

Create a basic app with two input fields

Here is the code for activity_main.xml layout file:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    tools:context="com.ayusch.blogexamples.view.MainActivity">

    <EditText
        android:id="@+id/et_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:hint="Enter Username" />

    <EditText
        android:id="@+id/et_password"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:hint="Enter Password" />

    <Button
        android:id="@+id/btn_login"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="16dp"
        android:enabled="false"
        android:text="Login" />

    <TextView
        android:id="@+id/tv_status"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="48dp"
        android:text="Not Logged In" />

</LinearLayout>

 

Here is the MainActivity.java file. (I haven’t followed any architecture or something as the goal of this tutorial is to teach how to apply RxJava in a practical android application. Feel free to apply best practices to this code).

package com.ayusch.blogexamples.view;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import com.ayusch.blogexamples.model.AppPrefs;
import com.ayusch.blogexamples.R;
import com.jakewharton.rxbinding2.widget.RxTextView;

import io.reactivex.Observable;
import io.reactivex.functions.BiFunction;
import io.reactivex.functions.Function;
import io.reactivex.observers.DisposableObserver;

public class MainActivity extends AppCompatActivity {
    EditText et_name, et_password;
    TextView tv_status;
    Button btn_login;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        AppPrefs.init(this);
        AppPrefs.setUserName("ayusch");
        AppPrefs.setPassword("pass12");

        init();

    }

    private void init() {
        et_name = findViewById(R.id.et_name);
        et_password = findViewById(R.id.et_password);
        btn_login = findViewById(R.id.btn_login);
        tv_status = findViewById(R.id.tv_status);
        
    }

}

 

Add two dependencies for RxJava and RxAndroid

RxJava is available for all java applications and not just android. Hence, for stuff such as AndroidSchedulers etc. we have to include RxAndroid that contains android bindings from RxJava.

implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
implementation 'io.reactivex.rxjava2:rxjava:2.2.0'

 

There is one more dependency which I want you guys to add, RxBinding by Jake Wharton.

implementation 'com.jakewharton.rxbinding2:rxbinding-appcompat-v7:2.1.1'

These are the latest versions at the time of writing this article. Feel free to update to latest versions. It consists of RxJava binding APIs for Android UI widgets from the platform and support libraries. It makes tasks such as converting an edittext to an observable, really easy.

And that’s it with all the dependencies. Let’s move on to the next step and start using RxJava in our project.

Create observables for those input fields

Now that we have created our basic app with two input fields and a button and added rxjava dependencies, we need to create observables out of the edittexts.

(Since you are here, I assume that you have some level of understanding of the terms, Observer, Observable and Subscribers. If not, I would suggest you google these terms as explaining them will take a complete dedicated post by itself).

Firstly, create observables from et_name and et_password.

Modify your MainActivity.java to look something like this:

package com.ayusch.blogexamples.view;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import com.ayusch.blogexamples.model.AppPrefs;
import com.ayusch.blogexamples.R;
import com.jakewharton.rxbinding2.widget.RxTextView;

import io.reactivex.Observable;
import io.reactivex.functions.BiFunction;
import io.reactivex.functions.Function;
import io.reactivex.observers.DisposableObserver;

public class MainActivity extends AppCompatActivity {
    EditText et_name, et_password;
    TextView tv_status;
    Button btn_login;

    Observable<Boolean> observable;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        AppPrefs.init(this);
        AppPrefs.setUserName("ayusch");
        AppPrefs.setPassword("pass12");

        init();

    }

    private void init() {
        et_name = findViewById(R.id.et_name);
        et_password = findViewById(R.id.et_password);
        btn_login = findViewById(R.id.btn_login);
        tv_status = findViewById(R.id.tv_status);

        Observable<String> nameObservable = RxTextView.textChanges(et_name).skip(1).map(new Function<CharSequence, String>() {
            @Override
            public String apply(CharSequence charSequence) throws Exception {
                return charSequence.toString();
            }
        });
        Observable<String> passwordObservable = RxTextView.textChanges(et_password).skip(1).map(new Function<CharSequence, String>() {
            @Override
            public String apply(CharSequence charSequence) throws Exception {
                return charSequence.toString();
            }
        });
        
    }

    


}

 

Notice that I have added skip(1) after RxTextView.textChanges(…), skip(1) is an operator in RxJava which allows us to skip the first value that we receive. When we subscribe to this observable an empty data is thrown which would be considered invalid and the error would pop up. But we don’t want this behavior so skip(1) would do exactly that and skip the first emission.

Now here’s the best part!

We will combine these observables into a single observable which will emit the data from both the input fields at once. But remember that the emission starts only when the last Observable starts emitting data. You can modify this behavior but it is appropriate for our use.

Add the following lines of code after you have created nameObservable and passwordObservable inside init() method.

observable = Observable.combineLatest(nameObservable, passwordObservable, new BiFunction<String, String, Boolean>() {
    @Override
    public Boolean apply(String s, String s2) throws Exception {
        return isValidForm(s, s2);
    }
});

 

Add a field named observable in your class as well:

Observable<Boolean> observable;

 

Apply validations

Now it’s the time to write our validations. We are going to create two helper methods:

  1. updateButton(Boolean valid): This will update the state of the based upon the argument passed.
  2. isValidForm(String name, String password): This will take the name and password as the argument and check to see if the input password is valid or not.

 

Here is the updateButton method:

public void updateButton(boolean valid) {
    if (valid)
        btn_login.setEnabled(true);
}

 

Here is the isValidForm method:

public boolean isValidForm(String name, String password) {
    boolean validName = !name.isEmpty();

    if (!validName) {
        et_name.setError("Please enter valid name");
    }

    boolean validPass = !password.isEmpty() && password.equals(AppPrefs.getPassword());
    if (!validPass) {
        et_password.setError("Incorrect password");
    }
    return validName && validPass;
}

 

Create an observer to listen to those observables

So, now that we have our observables set up, read to emit data, we need someone to listen to that data and do something with it. This is what Observers are for.

We are going to subscribe the Observable to an Observer which would then update the button state according to the input received.

Add the following code after you create your observables:

observable.subscribe(new DisposableObserver<Boolean>() {
    @Override
    public void onNext(Boolean aBoolean) {
        updateButton(aBoolean);
    }

    @Override
    public void onError(Throwable e) {

    }

    @Override
    public void onComplete() {

    }
});

 

And we are done!!

Here is the complete MainActivity.java file:

package com.ayusch.blogexamples.view;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import com.ayusch.blogexamples.model.AppPrefs;
import com.ayusch.blogexamples.R;
import com.jakewharton.rxbinding2.widget.RxTextView;

import io.reactivex.Observable;
import io.reactivex.functions.BiFunction;
import io.reactivex.functions.Function;
import io.reactivex.observers.DisposableObserver;

public class MainActivity extends AppCompatActivity {
    EditText et_name, et_password;
    TextView tv_status;
    Button btn_login;

    Observable<Boolean> observable;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        AppPrefs.init(this);
        AppPrefs.setUserName("ayusch");
        AppPrefs.setPassword("pass12");

        init();

    }

    private void init() {
        et_name = findViewById(R.id.et_name);
        et_password = findViewById(R.id.et_password);
        btn_login = findViewById(R.id.btn_login);
        tv_status = findViewById(R.id.tv_status);

        Observable<String> nameObservable = RxTextView.textChanges(et_name).skip(1).map(new Function<CharSequence, String>() {
            @Override
            public String apply(CharSequence charSequence) throws Exception {
                return charSequence.toString();
            }
        });
        Observable<String> passwordObservable = RxTextView.textChanges(et_password).skip(1).map(new Function<CharSequence, String>() {
            @Override
            public String apply(CharSequence charSequence) throws Exception {
                return charSequence.toString();
            }
        });

        observable = Observable.combineLatest(nameObservable, passwordObservable, new BiFunction<String, String, Boolean>() {
            @Override
            public Boolean apply(String s, String s2) throws Exception {
                return isValidForm(s, s2);
            }
        });

        observable.subscribe(new DisposableObserver<Boolean>() {
            @Override
            public void onNext(Boolean aBoolean) {
                updateButton(aBoolean);
            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onComplete() {

            }
        });
    }

    public void updateButton(boolean valid) {
        if (valid)
            btn_login.setEnabled(true);
    }

    public boolean isValidForm(String name, String password) {
        boolean validName = !name.isEmpty();

        if (!validName) {
            et_name.setError("Please enter valid name");
        }

        boolean validPass = !password.isEmpty() && password.equals(AppPrefs.getPassword());
        if (!validPass) {
            et_password.setError("Incorrect password");
        }
        return validName && validPass;
    }


}

 

This was a basic example on how to perform form validation with RxJava in Android. Feel free to connect with me on LinkedIn and suggest me topics which I should explain in my posts.

 

Like what you read ? Don’t forget to share this post on Facebook, Whatsapp and LinkedIn. You can follow me on LinkedIn, Quora and GitHub.