Introduction
There are several ways to make a REST API call like AsyncTask, Volley, etc. Nowadays, with the increasing popularity of RxJava, developers are preferring to use this library to make asynchronous API calls efficiently.
- dependencies {
- implementation fileTree(dir: 'libs', include: ['*.jar'])
- implementation 'com.android.support:appcompat-v7:27.1.0'
- implementation 'com.android.support.constraint:constraint-layout:1.0.2'
- implementation 'com.android.support:cardview-v7:27.1.0'
- implementation 'com.android.support:design:27.1.0'
- testImplementation 'junit:junit:4.12'
- androidTestImplementation 'com.android.support.test:runner:1.0.1'
- androidTestImplementation
- implementation 'com.android.support:cardview-v7:27.1.0'
- implementation 'com.android.support:design:27.1.0'
- implementation('com.squareup.retrofit2:retrofit:2.3.0')
- {
- exclude module: 'okhttp'
- }
- implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
- implementation 'io.reactivex.rxjava2:rxjava:2.1.9'
- implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
- implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
- implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1'
- }
Here, we can see the dependencies, cardview, and design are for recyclerview lists. We need a converter for parsing the response into a valid JSON. Step 2 Create an instance of Retrofit and interceptor as well. Here, Interceptor is used for logging the data during a network call. We generally use different threads in RxJava - a background thread for the network call and the main thread for updating the UI. Schedulers in RxJava are responsible for performing operations using different threads.
- HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
- interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
- OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
- Gson gson = new GsonBuilder()
- .setLenient()
- .create();
- retrofit = new Retrofit.Builder()
- .baseUrl(BASE_URL)
- .client(client)
- .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
- .addConverterFactory(GsonConverterFactory.create(gson))
- .build();
Since we are using Retrofit in RxJava environment, we need to make some changes as below.
- Adding RxJava in Retrofit Builder.
- Use Observable type in the interface instead of Call. Call is generally used with Retrofit.
Step 3
Let's see the activity_main.xml
- <?xml version="1.0" encoding="utf-8"?>
- <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context=".MainActivity">
- <android.support.v7.widget.RecyclerView
- android:id="@+id/recyclerView"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
- </android.support.constraint.ConstraintLayout>
Since we are creating a list, so we have to take a recyclerview and must code an adapter to hold the data in the list.
Now create an interface before making an API call. Let's have a look at the full interface code below.
- String BASE_URL = "https://api.cryptonator.com/api/full/";
- @GET("{coin}-usd")
- Observable<Crypto> getCoinData(@Path("coin") String coin);
- public class Crypto {
- @SerializedName("ticker")
- public Ticker ticker;
- @SerializedName("timestamp")
- public Integer timestamp;
- @SerializedName("success")
- public Boolean success;
- @SerializedName("error")
- public String error;
- public class Market {
- @SerializedName("market")
- public String market;
- @SerializedName("price")
- public String price;
- @SerializedName("volume")
- public Float volume;
- public String coinName;
- }
- public class Ticker {
- @SerializedName("base")
- public String base;
- @SerializedName("target")
- public String target;
- @SerializedName("price")
- public String price;
- @SerializedName("volume")
- public String volume;
- @SerializedName("change")
- public String change;
- @SerializedName("markets")
- public List<Market> markets = null;
- }
- }
- //Single call
- Observable<Crypto> cryptoObservable = cryptocurrencyService.getCoinData("btc");
- cryptoObservable.subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread())
- .map(result -> Observable.fromIterable(result.ticker.markets))
- .flatMap(x -> x).filter(y -> {
- y.coinName = "btc";
- return true;
- }).toList().toObservable()
- .subscribe(this::handleResults, this::handleError);
- CryptocurrencyService cryptocurrencyService = retrofit.create(CryptocurrencyService.class);
- Observable<List<Crypto.Market>> btcObservable = cryptocurrencyService.getCoinData("btc")
- .map(result -> Observable.fromIterable(result.ticker.markets))
- .flatMap(x -> x).filter(y -> {
- y.coinName = "btc";
- return true;
- }).toList().toObservable();
- Observable<List<Crypto.Market>> ethObservable = cryptocurrencyService.getCoinData("eth")
- .map(result -> Observable.fromIterable(result.ticker.markets))
- .flatMap(x -> x).filter(y -> {
- y.coinName = "eth";
- return true;
- }).toList().toObservable();
- Observable.merge(btcObservable, ethObservable)
- .subscribeOn(Schedulers.computation())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(this::handleResults, this::handleError);
- We use Observable.fromIterable to convert the map result into Observable streams.
- flatMap works on the elements one by one. Thus converting the ArrayList to single singular elements.
- In the filter method, we change the response.
- toList() is used to convert the results of flatMap back into a List.
- toObservable() wraps them as Observable streams.
- import android.support.v7.app.AppCompatActivity;
- import android.os.Bundle;
- import android.support.v7.widget.LinearLayoutManager;
- import android.support.v7.widget.RecyclerView;
- import android.widget.Toast;
- import com.google.gson.Gson;
- import com.google.gson.GsonBuilder;
- import com.journaldev.rxjavaretrofit.pojo.Crypto;
- import java.util.List;
- import io.reactivex.Observable;
- import io.reactivex.android.schedulers.AndroidSchedulers;
- import io.reactivex.schedulers.Schedulers;
- import okhttp3.OkHttpClient;
- import okhttp3.logging.HttpLoggingInterceptor;
- import retrofit2.Retrofit;
- import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
- import retrofit2.converter.gson.GsonConverterFactory;
- public class MainActivity extends AppCompatActivity {
- RecyclerView recyclerView;
- Retrofit retrofit;
- RecyclerViewAdapter recyclerViewAdapter;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- recyclerView = findViewById(R.id.recyclerView);
- recyclerView.setLayoutManager(new LinearLayoutManager(this));
- recyclerViewAdapter = new RecyclerViewAdapter();
- recyclerView.setAdapter(recyclerViewAdapter);
- HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
- interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
- OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
- Gson gson = new GsonBuilder()
- .setLenient()
- .create();
- retrofit = new Retrofit.Builder()
- .baseUrl(CryptocurrencyService.BASE_URL)
- .client(client)
- .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
- .addConverterFactory(GsonConverterFactory.create(gson))
- .build();
- callEndpoints();
- }
- private void callEndpoints() {
- CryptocurrencyService cryptocurrencyService = retrofit.create(CryptocurrencyService.class);
- Observable<List<Crypto.Market>> btcObservable = cryptocurrencyService.getCoinData("btc")
- .map(result -> Observable.fromIterable(result.ticker.markets))
- .flatMap(x -> x).filter(y -> {
- y.coinName = "btc";
- return true;
- }).toList().toObservable();
- Observable<List<Crypto.Market>> ethObservable = cryptocurrencyService.getCoinData("eth")
- .map(result -> Observable.fromIterable(result.ticker.markets))
- .flatMap(x -> x).filter(y -> {
- y.coinName = "eth";
- return true;
- }).toList().toObservable();
- Observable.merge(btcObservable, ethObservable)
- .subscribeOn(Schedulers.computation())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(this::handleResults, this::handleError);
- }
- private void handleResults(List<Crypto.Market> marketList) {
- if (marketList != null && marketList.size() != 0) {
- recyclerViewAdapter.setData(marketList);
- } else {
- Toast.makeText(this, "NO RESULTS FOUND",
- Toast.LENGTH_LONG).show();
- }
- }
- private void handleError(Throwable t) {
- Toast.makeText(this, "ERROR IN FETCHING API RESPONSE. Try again",
- Toast.LENGTH_LONG).show();
- }
- }
Here, we are using handleResults and handleError is invoked using the Java 8 invocation. Converted response must be set in the ReyclerViewAdapter.
- <?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"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <android.support.v7.widget.CardView
- android:id="@+id/cardView"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_margin="16dp">
- <android.support.constraint.ConstraintLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:padding="8dp">
- <TextView
- android:id="@+id/txtCoin"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="8dp"
- android:layout_marginRight="8dp"
- android:layout_marginTop="8dp"
- android:textAllCaps="true"
- android:textColor="@android:color/black"
- app:layout_constraintHorizontal_bias="0.023"
- app:layout_constraintLeft_toLeftOf="parent"
- app:layout_constraintRight_toRightOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
- <TextView
- android:id="@+id/txtMarket"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="8dp"
- android:layout_marginRight="8dp"
- android:layout_marginTop="8dp"
- app:layout_constraintHorizontal_bias="0.025"
- app:layout_constraintLeft_toLeftOf="parent"
- app:layout_constraintRight_toRightOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/txtCoin" />
- <TextView
- android:id="@+id/txtPrice"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="8dp"
- android:layout_marginStart="8dp"
- android:layout_marginTop="8dp"
- app:layout_constraintHorizontal_bias="0.025"
- app:layout_constraintLeft_toLeftOf="parent"
- app:layout_constraintRight_toRightOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/txtMarket" />
- </android.support.constraint.ConstraintLayout>
- </android.support.v7.widget.CardView>
- </LinearLayout>
Finally, we have an adapter class named as RecyclerViewAdapter.java.
- public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
- private List<Crypto.Market> marketList;
- public RecyclerViewAdapter() {
- marketList = new ArrayList<>();
- }
- @Override
- public RecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
- int viewType) {
- View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_item_layout, parent, false);
- RecyclerViewAdapter.ViewHolder viewHolder = new RecyclerViewAdapter.ViewHolder(view);
- return viewHolder;
- }
- @Override
- public void onBindViewHolder(RecyclerViewAdapter.ViewHolder holder, int position) {
- Crypto.Market market = marketList.get(position);
- holder.txtCoin.setText(market.coinName);
- holder.txtMarket.setText(market.market);
- holder.txtPrice.setText("$" + String.format("%.2f", Double.parseDouble(market.price)));
- if (market.coinName.equalsIgnoreCase("eth")) {
- holder.cardView.setCardBackgroundColor(Color.GRAY);
- } else {
- holder.cardView.setCardBackgroundColor(Color.GREEN);
- }
- }
- @Override
- public int getItemCount() {
- return marketList.size();
- }
- public void setData(List<Crypto.Market> data) {
- this.marketList.addAll(data);
- notifyDataSetChanged();
- }
- public class ViewHolder extends RecyclerView.ViewHolder {
- public TextView txtCoin;
- public TextView txtMarket;
- public TextView txtPrice;
- public CardView cardView;
- public ViewHolder(View view) {
- super(view);
- txtCoin = view.findViewById(R.id.txtCoin);
- txtMarket = view.findViewById(R.id.txtMarket);
- txtPrice = view.findViewById(R.id.txtPrice);
- cardView = view.findViewById(R.id.cardView);
- }
- }
- }
Output

You can see the green color lines and grey color lines - one for "btc" and another for "eth" respectively. This is a mix data of two API calls. Above, we have created two Observables - one for btc and another for eth.
Conclusion
In this article, we learned how to set up retrofit and instance making. The article mainly focused on the Retrofit bond with RxJava. Here, we used merge operator of RxJava to combine two Retrofit calls.

Join the conversation! Your thoughts help the community grow.