Mastering Reactive Android programming with RxJava is no easy feat. At first, the way the code is structured and written may seem strange and alien. However, with some practice, we will quickly see that it is an easy and understandable way to write concurrent and parallel code.
One of the best ways to learn is by example. That's why we will build a simple application to learn RxJava. It will be a very straightforward financial stock quote monitoring app. We will tap into the streams of Yahoo Finance data to display the quotes while using the basics of RxJava.
In the later stages, we will plug in an endless stream of Twitter data and we will start using more advanced techniques to transform and handle data.First of all, we will start with a simple Hello World application that will provide a solid foundation to the all the things that we will learn.By the end of this chapter, we will learn and do the following things:
- Create an initial barebone for the application
- Set up RxJava and UI dependencies
- Create an initial UI where we will show the stock quote updates
- Initialize UI with mock data using RxJava to get a Hello World screen
We will start our journey by creating a skeleton application to base all our further development upon. This initial code base will have RxJava dependencies and other libraries included, which we will use throughout this book.
Furthermore, it will have simple UI elements and some very basic examples of RxJava, on which we will continue building our code. This will be a super simple Android Hello World application with a twist of RxJava.
This app will also have elements, such us RecyclerView
, which will be used in the later stages of the app but for now, they will be filled with mock data (but in a Reactive way!).
Finally, let's create our initial application using Android Studio. This can be done by starting up Android Studio (we will assume that you have a development environment set up and fully working) and using the initial Android Screen with an option of Start a new Android Studio project
 (or if you have a project already open, you can use a menu File->New->New Project
)Â and select Empty Activity
:

Â
Before we can start the development, we need to prepare dependencies so that libraries, such as React, can be used. All the dependencies will be added in the app/build.gradle
file.
Let's start by adding the RxJava 2.0 dependencies with a line:
compile 'io.reactivex.rxjava2:rxjava:2.0.3'
Also, we will want to use Android-specific schedulers, so let's add the following line:
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
To interact with UI elements, we will use two great libraries: RxBinding
(which can be found at https://github.com/JakeWharton/RxBinding ) and Butterknife
(which can be found at https://github.com/JakeWharton/butterknife ).
ButterKnife
will let us easily import UI elements into Java code (no more pesky findViewById()
!) and RxBinding
will let us make all UI interactions RxJava friendly.
To include ButterKnife
, let's add these lines:
compile 'com.jakewharton:butterknife:8.4.0' annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
To include RxBinding
, put in the following:
compile 'com.jakewharton.rxbinding2:rxbinding:2.0.0' compile 'com.jakewharton.rxbinding2:rxbinding-recyclerview-v7:2.0.0'
Finally, we will be using the RecyclerView
and CardView
elements from Android Support Repository
, so these will be needed as well:
compile 'com.android.support:cardview-v7:25.1.0' compile 'com.android.support:recyclerview-v7:25.1.0'
In the future, we might need to mix different versions of RxJava, so this line (under android
block) will help when both the libraries will be included at the same time:
packagingOptions { exclude 'META-INF/rxjava.properties' }
In the end, the top of the build.gradle
file will look like this:
plugins { id "me.tatarka.retrolambda" version "3.4.0" } apply plugin: 'com.android.application'
Also, the android
block will be as follows:
android { compileSdkVersion 25 buildToolsVersion "25.0.2" defaultConfig { applicationId "packt.reactivestocks" minSdkVersion 15 targetSdkVersion 25 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } packagingOptions { exclude 'META-INF/rxjava.properties' } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard- android.txt'), 'proguard-rules.pro' } } compileOptions { targetCompatibility 1.8 sourceCompatibility 1.8 } }
The dependency
block will be set to the following:
dependencies { compile 'com.android.support:appcompat-v7:25.1.0' compile group: 'io.reactivex.rxjava2', name: 'rxjava', version: '2.0.3' compile group: 'io.reactivex.rxjava2', name: 'rxandroid', version: '2.0.1' compile 'com.jakewharton:butterknife:8.4.0' annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0' compile 'com.jakewharton.rxbinding2:rxbinding:2.0.0' compile 'com.jakewharton.rxbinding2:rxbinding-recyclerview- v7:2.0.0' compile 'com.android.support:cardview-v7:25.1.0' compile 'com.android.support:recyclerview-v7:25.1.0' compile "com.github.akarnokd:rxjava2-interop:0.8.1" }
Let's update MainActivity
to look like this:
@BindView(R.id.hello_world_salute) TextView helloText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); Observable.just("Hello! Please use this app responsibly!") .subscribe(new Consumer<String>() { @Override public void accept(String s) { helloText.setText(s); } }); }
The import
block for the preceding code is as shown:
import butterknife.BindView; import butterknife.ButterKnife; import io.reactivex.Observable; import io.reactivex.functions.Consumer;
Note
If you are in doubt as to which imports are used at any point in the book, feel free to check out the code that's provided with the book.
The contents of activity_main.xml
at the moment are set to the following:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="6dp" tools:context="packt.reactivestocks.MainActivity"> <TextView android:id="@+id/hello_world_salute" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="" /> </RelativeLayout>
This will create a very basic screen with a single text element that will say "Hello! Please use this app responsibly!"
.
Since the application will be displaying changes of financial stocks over time, it means that we will be working with data that's represented best as a list. For this, we will use RecyclerView
.Â
First of all, add it to your activity_main.xml
 file:
<android.support.v7.widget.RecyclerView android:id="@+id/stock_updates_recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/hello_world_salute" />
Then, inject it into your code with the following:
@BindView(R.id.stock_updates_recycler_view) RecyclerView recyclerView;
The next step is to initialize it in your activity:
private LinearLayoutManager layoutManager; private StockDataAdapter stockDataAdapter; ... recyclerView.setHasFixedSize(true); layoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(layoutManager); stockDataAdapter = new StockDataAdapter(); recyclerView.setAdapter(stockDataAdapter);
The code for the StockDataAdapter
is as illustrated:
public class StockDataAdapter extends RecyclerView.Adapter<StockUpdateViewHolder> { private final List<String> data = new ArrayList<>(); @Override public StockUpdateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()) .inflate(R.layout.stock_update_item, parent, false); StockUpdateViewHolder vh = new StockUpdateViewHolder(v); return vh; } @Override public void onBindViewHolder(StockUpdateViewHolder holder, int position) { holder.stockSymbol.setText(data.get(position)); } @Override public int getItemCount() { return data.size(); } public void add(String stockSymbol) { this.data.add(stockSymbol); notifyItemInserted(data.size() - 1); } }
Finally, we need the layout XML for the RecyclerView
 items and a ViewHolder
for them:
<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="3dp"> <TextView android:id="@+id/stock_item_symbol" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_margin="20dp" /> </android.support.v7.widget.CardView>
To fill in the data for this view, we will have to utilize ViewHolders
from the RecyclerView
class:
public class StockUpdateViewHolder extends RecyclerView.ViewHolder { @BindView(R.id.stock_item_symbol) TextView stockSymbol; public StockUpdateViewHolder(View v) { super(v); ButterKnife.bind(this, v); } }
The very last step is to populate it with the data. We will do that in a Reactive way:
Observable.just("APPL", "GOOGLE", "TWTR") .subscribe(new Consumer<String>() { @Override public void accept(String stockSymbol) { stockDataAdapter.add(stockSymbol); } });
This is rather simple code and I hope that most of the developers had no trouble following this. In case there were some not-so-clear bits, feel free to check out the code that's provided with this book. The examples there are fully working.
One will notice pretty quickly that it is quite unwieldy to work just with string data as the app will process stock updates. For the purpose of a proper way to track stock updates, we will create a Value object that will keep that information. It will contain the following fields for now:
stockSymbol
: to know which symbol we will be followingcurrentPrice
: the price as of the last updatedate
: the date of the last update
The list might be a bit too naive for professional use, but it will serve as a starting point for upcoming work. The final class can look like this:
public class StockUpdate implements Serializable { private final String stockSymbol; private final BigDecimal price; private final Date date; public StockUpdate(String stockSymbol, double price, Date date) { this.stockSymbol = stockSymbol; this.price = new BigDecimal(price); this.date = date; } public String getStockSymbol() { return stockSymbol; } public BigDecimal getPrice() { return price; } public Date getDate() { return date; } }
Obviously, we then need to update other parts of the code to make use of this refactoring:
@Override public void onBindViewHolder(StockUpdateViewHolder holder, int position) { StockUpdate stockUpdate = data.get(position); holder.setStockSymbol(stockUpdate.getStockSymbol()); holder.setPrice(stockUpdate.getPrice()); holder.setDate(stockUpdate.getDate()); }
Also, StockUpdateViewHolder
now has additional methods and view fields:
@BindView(R.id.stock_item_date) TextView date; @BindView(R.id.stock_item_price) TextView price; private static final NumberFormat PRICE_FORMAT = new DecimalFormat("#0.00"); public void setStockSymbol(String stockSymbol) { this.stockSymbol.setText(stockSymbol); } public void setPrice(BigDecimal price) { this.price.setText(PRICE_FORMAT.format(price.floatValue())); } public void setDate(Date date) { this.date.setText(DateFormat.format("yyyy-MM-dd hh:mm", date)); }
Also, our file stock_update_item.xml
will change slightly to nicely display all this new data. The stock_item_symbol
is now as shown:
<TextView android:id="@+id/stock_item_symbol" android:layout_width="wrap_content" android:layout_height="match_parent" android:text="GOOGLE" android:textSize="18sp" />
At the same time, stock_item_price
and stock_item_date
are handled by the following:
<TextView android:id="@+id/stock_item_price" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:text="18.90" android:textColor="@android:color/holo_green_dark" android:textSize="22sp" /> <TextView android:id="@+id/stock_item_date" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_below="@id/stock_item_symbol" android:layout_marginTop="5dp" android:text="2012-12-01" android:textSize="12sp" />
Also, all the preceding elements will be wrapped inside the RelativeLayout
that will be in the CardView
:
<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="3dp"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="20dp"> [...] </RelativeLayout> </android.support.v7.widget.CardView>
Finally, our Observable
--the piece that creates the data--will look like this:
Observable.just( new StockUpdate("GOOGLE", 12.43, new Date()), new StockUpdate("APPL", 645.1, new Date()), new StockUpdate("TWTR", 1.43, new Date()) )
Â
A curious developer might have heard about new Java 8 features, such as lambdas and method references. However, they are not available by default on the Android platform.
At the moment, there are three ways to add some of the Java 8 features on the platform. One of them is the new Jack compiler, but it is now deprecated. The other option is to rely on the new Java 8 features that will be released sometime soon in the default toolchain (at the time of writing, Java 8 support was in the preview).
The option we will be using is Retrolambda
(https://github.com/orfjackal/retrolambda). We will enable it by using a plugin for Android (https://github.com/evant/gradle-retrolambda).
Note
Don't forget that, before using Java 8 features, you have to have the Oracle JDK 8 downloaded and running.
It can be done by simply adding the following at the very top of the application's app/build.gradle
file:
plugins { id "me.tatarka.retrolambda" version "3.4.0" } apply plugin: 'com.android.application'
Also, let's not forget to tell the IDE that we will be using Java 8 by adding the following block:
android { ... compileOptions { targetCompatibility 1.8 sourceCompatibility 1.8 } }
Finally, if proguard
is being used, adding a line---dontwarn java.lang.invoke.*
--to your proguard
file might come in handy.
Â
Â
You might think that setting all Retrolambda
can be just too much trouble for very little gain. Nothing could be further from the truth and it will be obvious after we take a look at a few examples of code.
Compare the following two samples of code. The first sample is as follows:
Observable.just("1") .subscribe(new Consumer<String>() { @Override public void accept(String e) { Log.d("APP", "Hello " + e); } });
Here's the next sample:
Observable.just("1") .subscribe(e -> Log.d("APP", "Hello " + e));
The latter is much more concise, but might fail to show the scale of the problem of the former approach. This example might be much better at doing so:
Observable.just("1") .map(new Function<String, String>() { @Override public String apply(String s) { return s + "mapped"; } }) .flatMap(new Function<String, Observable<String>>() { @Override public Observable<String> apply(String s) { return Observable.just("flat-" + s); } }) .doOnNext(new Consumer<String>() { @Override public void accept(String s) { Log.d("APP", "on next " + s); } }) .subscribe(new Consumer<String>() { @Override public void accept(String e) { Log.d("APP", "Hello " + e); } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) { Log.d("APP", "Error!"); } });
Now, take a look at this:
Observable.just("1") .map(s -> s + "mapped") .flatMap(s -> Observable.just("flat-" + s)) .doOnNext(s -> Log.d("APP", "on next " + s)) .subscribe(e -> Log.d("APP", "Hello " + e), throwable -> Log.d("APP", "Error!"));
That's 24 lines saved!Â
The observable flows can get pretty complicated and long, so these lines add up pretty quickly.
If you are stuck with an old version of Java, there are other means to make these flows clear; we will cover more about this in the later chapters.
Since we have just enabled Retrolambda
, let's replace the existing code in MainActivity
to make use of it:
Observable.just("Please use this app responsibly!") .subscribe(s -> helloText.setText(s)); Observable.just( new StockUpdate("GOOGLE", 12.43, new Date()), new StockUpdate("APPL", 645.1, new Date()), new StockUpdate("TWTR", 1.43, new Date()) ) .subscribe(stockDataAdapter::add);
Note that we could have used the helloText::setText
method reference in the first .subscribe()
call as well, but it was left as it is just to serve as an example.
In the case where multiple lines are required, the subscribe part can look like this:
.subscribe(stockUpdate -> { Log.d("APP", "New update " + stockUpdate.getStockSymbol()); stockDataAdapter.add(stockUpdate); });
However, in this particular case, I would recommend putting logging calls in .doOnNext()
blocks.
Finally, the resulting app should look like this screenshot:

If it looks different, feel free to check out the code that's provided with the book.
At this point, we have a solid framework on which we can build our further financial stock monitoring application. The means to display data are here along with some basic tools and libraries that will become useful as we develop the application.
In particular, we integrated the RxJava library into our code and tested that it works with a very simple Hello World example. Also, we've plugged in additional libraries, such as Butterknife, that will help us work with views with less effort.
Finally, the use of Retrolambda
was enabled, which will let us write a much more concise and readable code in future.
However, it's not clear what exactly an Observable
is. This is exactly what the next chapter will tackle!