Reactive Android Programming

3.7 (3 reviews total)
By Tadas Šubonis
    Advance your knowledge in tech with a Packt subscription

  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Building your First “Hello World” RxJava Application

About this book

Writing code on Android is hard. Writing a high quality code that involves concurrent and parallel tasks is even harder. Ensuring that this code will run without unforeseen race conditions is an the order of magnitude harder. RxJava is the tool that can help write code for such tasks.

In this book a novice developer will be introduced to a wide variety of tools that RxJava provides to enable them to produce robust and high-quality code for their asynchronous tasks by building a relatively simple(and high quality) application using advanced RxJava techniques to produce a high quality product.

Part 1 of the book will lead the developer through RxJava's initial setup in Android environment. In Part 2, the reader will learn RxJava 2.0 step-by-step by starting off with stock data processing and display.The developer will learn to choose appropriate Schedulers and to use Retrofit library for remote requests.In Part 3, the reader will also learn advanced topics such as adding integration to Twitter to process its streaming data by combining it with stock data.

Publication date:
May 2017
Publisher
Packt
Pages
225
ISBN
9781787289901

 

Chapter 1. Building your First “Hello World” RxJava Application

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
 

Creating the application


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:

 

Dependencies

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.

Core dependencies

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'

UI dependencies

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"
}

Very first user interface

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!".

Adding RecyclerView

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.

Stock data Value objects

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 following
  • currentPrice: the price as of the last update
  • date: 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())
)

 

Using Retrolambda

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).

Setup

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.

 

 

Usage

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.

Updating the existing code

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.

 

Summary


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!

About the Author

  • Tadas Šubonis

    Tadas Šubonis started coding roughly when he was thirteen. Since then, he has programmed with PHP, JavaScript, Python, C++, and Java (the language in which he has probably written the most code).

    He took up Android in 2014 and identified that Android lacks decent support for asynchronous programming (Async Task was/is a joke) while more reckless languages, such as JavaScript, had Promises for a long time. Furthermore, Java's standard library was lacking decent support for functional programming primitives (map, filter), but that was easily fixable with libraries such as Guava.

    This lead Tadas to a search for a library that would help him achieve a Promise-like functionality and interface. It didn't take long until he found ReactiveX and its family of implementations (including RxJava) that handle streams in Reactive fashion. It wasn't exactly the flow of Promised-like systems but soon enough, he realized that it's even more powerful.

    Since then, he has been using RxJava (and RxKotlin) for his daily Android programming. The quality of the code (the lack of bugs, readability, and maintainability) has improved ten-fold.

    Browse publications by this author

Latest Reviews

(3 reviews total)
Yahoo Services are down, you need to update your book
muy buenos libros que tienen
Love it, will buy again in the future.
Reactive Android Programming
Unlock this book and the full library for FREE
Start free trial