
In this tutorial, we are going to learn how to parse JSON data from URL and display it in a RecyclerView using Retrofit and RxJava. We parse the JSON array from the URL
In this tutorial, we are going to learn how to parse JSON data from URL and display it in a RecyclerView using Retrofit and RxJava. We parse the JSON array from the URL
About RxJava
RxJava is a trending Reactive programming Java framework. It can be used to create event-based asynchronous programs. In this tutorial, we are using RxJava for performing Retrofit network operations.
Creating Project
Here I have created an Android Studio project with package com.learn2crack.retrofitrxjava also Activity as MainActivity and layout as activity_main.
Adding Dependencies and enabling Jack compiler
In the app’s build.gradle insert the following snippet to the defaultConfig section to enable Jack,
jackOptions {
enabled true
}
Then we need to set the Java version to 8 for Jack and Lambda functions to work. Add the snippet to the android section,
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
The additional dependencies we require are,
compile 'com.android.support:recyclerview-v7:25.0.1'
compile 'com.android.support:cardview-v7:25.0.1'
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'io.reactivex.rxjava2:rxjava:2.0.1'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
We have added the dependencies for RxJava, Retrofit, Card View and Recycler View.
build.gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "25.0.0"
defaultConfig {
applicationId "com.learn2crack.retrofitrxjava"
minSdkVersion 19
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
jackOptions {
enabled true
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.0.1'
compile 'com.android.support:recyclerview-v7:25.0.1'
compile 'com.android.support:cardview-v7:25.0.1'
testCompile 'junit:junit:4.12'
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'io.reactivex.rxjava2:rxjava:2.0.1'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
}
Adding Permissions to Manifest
We need permission to access the Internet.
<uses-permission android:name="android.permission.INTERNET"/>
Creating Layout
Our main layout has only one RecyclerView widget with a root Relative Layout.
activity_main.xml
<?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"
tools:context="com.learn2crack.retrofitrxjava.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
The next layout is for RecylerView list item. It has three TextView widgets to display Android version name, version number and API level.
recycler_row.xml
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
card_view:cardCornerRadius="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv_name"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:textSize="18sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_version"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_api_level"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</android.support.v7.widget.CardView>
Creating Model class
Now we create Android model class to store Android version name, version number, and API level. We obtain an array of JSON objects similar to,
{
ver: "1.5",
name: "Cupcake",
api: "API level 3"
}
This Android class is created in the model package
Android.java
package com.learn2crack.retrofitrxjava.model;
public class Android {
private String ver;
private String name;
private String api;
public String getVer() {
return ver;
}
public String getName() {
return name;
}
public String getApi() {
return api;
}
}
Creating Retrofit Interface
The endpoint from which the JSON array is fetched is defined as the register() method. This method returns a RxJava Observable. An Observer can subscribe this Observable.
This Retrofit Interface is created in the network package.
RequestInterface.java
package com.learn2crack.retrofitrxjava.network;
import com.learn2crack.retrofitrxjava.model.Android;
import java.util.List;
import io.reactivex.Observable;
import retrofit2.http.GET;
public interface RequestInterface {
@GET("android/jsonarray/")
Observable<List<Android>> register();
}
Creating Adapter
We pass Android model class as ArrayList in the constructor. We obtain the Android version name, version number, API level from the Android object using the getter methods and set it to TextView widgets.
This Adapter is created in the adapter package.
DataAdapter.java
package com.learn2crack.retrofitrxjava.adapter;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.learn2crack.retrofitrxjava.R;
import com.learn2crack.retrofitrxjava.model.Android;
import java.util.ArrayList;
public class DataAdapter extends RecyclerView.Adapter<DataAdapter.ViewHolder> {
private ArrayList<Android> mAndroidList;
public DataAdapter(ArrayList<Android> androidList) {
mAndroidList = androidList;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_row, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.mTvName.setText(mAndroidList.get(position).getName());
holder.mTvVersion.setText(mAndroidList.get(position).getVer());
holder.mTvApi.setText(mAndroidList.get(position).getApi());
}
@Override
public int getItemCount() {
return mAndroidList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
private TextView mTvName,mTvVersion,mTvApi;
public ViewHolder(View view) {
super(view);
mTvName = (TextView)view.findViewById(R.id.tv_name);
mTvVersion = (TextView)view.findViewById(R.id.tv_version);
mTvApi = (TextView)view.findViewById(R.id.tv_api_level);
}
}
}
Creating Activity
We initialise a CompositeDisposable object and add all the RxJava disposable to it. Previously it was CompositeSubscription in RxJava 1 which is replaced by CompositeDisposable. During onDestroy() we can clear the disposables so that it prevents the memory leak.
The RecyclerView is initialised using initRecyclerView() method. The Retrofit request is defined using
RequestInterface requestInterface = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build().create(RequestInterface.class);
To use RxJava Observable along with Retrofit, we need to use a new Adapter Factory implementation. It is done by using addCallAdapterFactory() method.
The network operation is carried out by,
requestInterface.register()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(this::handleResponse,this::handleError)
The above code returns a Disposable. The observeOn() and subscribeOn() defines, in which thread we need to handle the results and perform the network operation. Here we have defined the results should be delivered and handled in the main thread and the network operation should be handled in IO thread. The handleResponse() and handleError() methods are called for success and failure. We are using Java 8 Lambda shorthand functions for handleResponse and handleError methods. Else we need to write,
subscribe(response -> handleResponse(response), error -> handleError(error))
Instead of the above code we are simplifying using this operator,
subscribe(this::handleResponse,this::handleError)
We display the failure messages in a Toast.
This disposable is added to a CompositeDisposable using the add() method so that it can be removed later.
MainActivity.java
package com.learn2crack.retrofitrxjava;
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.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import com.learn2crack.retrofitrxjava.adapter.DataAdapter;
import com.learn2crack.retrofitrxjava.model.Android;
import com.learn2crack.retrofitrxjava.network.RequestInterface;
import java.util.ArrayList;
import java.util.List;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class MainActivity extends AppCompatActivity {
public static final String BASE_URL = "https://api.learn2crack.com/";
private RecyclerView mRecyclerView;
private CompositeDisposable mCompositeDisposable;
private DataAdapter mAdapter;
private ArrayList<Android> mAndroidArrayList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mCompositeDisposable = new CompositeDisposable();
initRecyclerView();
loadJSON();
}
private void initRecyclerView() {
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
mRecyclerView.setHasFixedSize(true);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getApplicationContext());
mRecyclerView.setLayoutManager(layoutManager);
}
private void loadJSON() {
RequestInterface requestInterface = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build().create(RequestInterface.class);
mCompositeDisposable.add(requestInterface.register()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(this::handleResponse,this::handleError));
}
private void handleResponse(List<Android> androidList) {
mAndroidArrayList = new ArrayList<>(androidList);
mAdapter = new DataAdapter(mAndroidArrayList);
mRecyclerView.setAdapter(mAdapter);
}
private void handleError(Throwable error) {
Toast.makeText(this, "Error "+error.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
}
@Override
public void onDestroy() {
super.onDestroy();
mCompositeDisposable.clear();
}
}
Screenshot
Complete Project Files
You can download the complete project as zip or fork from our Github repository.
Stay tuned!
Enjoy coding 🙂