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
- Create a basic app with two input fields and one submit button
- Add dependencies for RxJava and RxAndroid
- Create observables for those input fields
- Apply validations
- 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:
- updateButton(Boolean valid): This will update the state of the based upon the argument passed.
- 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.