Today, it’s pretty common for mobile apps to exchange data with remote servers, using web Application Programming Interfaces (APIs).
Whether it’s checking for new emails using the Gmail API, searching for events in the local area with the Ticketmaster API, or leveraging the user’s Spotify history to recommend new music, APIs (usually REST APIs) provide access to a wealth of information that you can use in your app.
Communicating with remote servers has become such an important part of modern mobile development that there are countless libraries dedicated to helping you make network calls, but Retrofit is one of the most widely used networking libraries for Android.
In this article, I’ll show you how to retrieve data from a remote API, using Retrofit and the free Nomics Cryptocurrency & Bitcoin API.
Once you’ve retrieved some data, you’ll want to put it to good use! In Android, this often involves forwarding the data to Android’s all-important main UI thread, ready to display in your app’s UI, or transforming the data in some way, such as filtering or combining it. Conveniently, you can do all of these things with the RxJava library, so we’ll be using Retrofit and RxJava side by side in our project.
By the end of this article, you’ll have created an app that retrieves data from a remote API using Retrofit, converts that data into RxJava’s Observable
format, and then manipulates that data using a selection of RxJava and RxAndroid operators and schedulers.
What We’ll Be Building
There’s no shortage of APIs out there, but to help keep things straightforward, we’ll be using the free Nomics Cryptocurrency & Bitcoin API to retrieve a list of cryptocurrencies and their market prices for the last recorded trade. I’ll then use a RecyclerView
to display this information in our app.
To issue a Retrofit request, we’ll need to create the following:
- A Retrofit class. This is where you’ll create a Retrofit instance, add a converter (we’ll be using Gson), and then specify the base URL that your app should use for all of its HTTP requests.
- A data class. This class represents the responses for each of your app’s API calls.
- An API interface. This is where you describe each Retrofit request that you want to make.
By the end of this tutorial, you’ll have a project that looks something like this:
Why Should I Use RxJava for My API Calls?
RxJava is an open-source, JVM-compatible implementation of the ReactiveX library that’s designed to help you work with asynchronous streams of data in a reactive programming style, and without having to write a ton of callbacks.
There are many benefits to using RxJava in your Android projects, but some of the most important include:
- Simplified code. RxJava lets you describe what you want to achieve, rather than writing a list of instructions for your application to work through. This approach typically results in more concise, human-readable code that’s easier to maintain and less prone to errors.
- Consistency. RxJava may be a data-focused library, but its definition of “data” is pretty broad and includes things like variables, caches, properties, and even user input events such as clicks and swipes. Since RxJava treats pretty much everything as data, it provides a consistent workflow that you can use across your application, in lots of different scenarios.
-
Multithreading made easy. Modern mobile apps must be able to multitask, but as a single-threaded environment, Android isn’t exactly a natural born multitasker! Android does provide several built-in tools for creating additional threads, but none of these solutions are particularly easy to work with, and they can quickly result in complex code that’s susceptible to performance problems such as memory leaks. By adding RxJava to your project, you get access to
subscribeOn
andobserveOn
operators, which make it much easier to create and manage additional threads. - Complex data transformations. RxJava has an enormous collection of operators that you can use to modify, filter, merge and transform the data that’s being emitted by your RxJava Observables. Applying an operator to an Observable typically returns another Observable, so if you can’t find the perfect operator for your project, then you can just keep applying operators to an Observable until you get exactly the results you want.
For more information about the benefits of using RxJava in your Android projects, check out our Get Started With RxJava 2 article.
Authenticating With an API Key
Many APIs require you to include a unique API key in your HTTP requests. These keys allow the company or individual behind the API to track the requests associated with your project, which is handy for enforcing usage restrictions or if the API’s creator simply wants some insight into how their API is being used.
When querying the Nomics Cryptocurrency & Bitcoin API, you’ll need to incorporate a unique API key into your request, so let’s generate this key now:
- In your web browser, head over to the Nomics website.
- Find the Get Free API Key button, and give it a click.
- Check the Terms and Conditions, and if you’re happy to proceed then complete the form.
- Select Complete Order. After a few moments, you should receive an email containing your unique API key.
Create Your Application: Adding Dependencies
Create a new Android application with the settings of your choice, but when prompted, opt to Include Kotlin support.
Next, open your build.gradle file and add all the libraries we’ll be using throughout this project. In addition to Retrofit and RxJava 2.0, we’ll need the following:
1. RecyclerView
After retrieving our data, we’ll display it in a list, using RecyclerView
.
implementation 'com.android.support:recyclerview-v7:26.1.0'
2. Gson Converter
Most of the time, the server’s response will be mapped to a language-neutral format, such as JSON, so your app will need to serialize and deserialize the JSON data. Out of the box, Retrofit can only deserialize HTTP bodies into OkHttp’s ResponseBody
type, but you can support other types by outsourcing the conversion to independent converters.
There’s a wide range of converters available, but in this tutorial I’ll be using Gson, which converts JSON into the equivalent Java objects.
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
3. The RxJava Adapter
To create interface methods that are capable of returning RxJava types, we’ll need to use the Retrofit adapter for RxJava.
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
4. The RxAndroid Library
The RxAndroid library provides some Android specific bindings for RxJava, most notably AndroidSchedulers.mainThread
.
In Android, you can only update your app’s UI from the main UI thread. If your users are going to see any cryptocurrency data, then you’ll need to switch to the main UI thread at some point.
You can quickly and easily schedule code to run on Android’s main UI thread by using RxJava’s observeOn
operator in combination with RxAndroid’s AndroidSchedulers.mainThread
scheduler:
.observeOn(AndroidSchedulers.mainThread())
To use RxAndroid in your project, you’ll need the following project dependency:
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
… But Not RxKotlin
At this point, you may be wondering why we’re coding in Kotlin but using RxJava, when there is a dedicated RxKotlin library available.
Since Kotlin is 100% interoperable with Java, the majority of Java libraries are compatible with Kotlin, and RxJava is no exception! You could use RxJava and RxKotlin in your project, but to help keep things simple, I’ll be sticking to RxJava throughout this tutorial. If you’re interested in learning more about RxKotlin, then you’ll find lots of information in my article on Kotlin Reactive Programming With RxJava and RxKotlin.
Add the Project Dependencies
After adding all these project dependencies, your build.gradle file should look something like this:
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.android.support:recyclerview-v7:26.1.0' implementation 'com.android.support.constraint:constraint-layout:1.0.2' implementation 'com.squareup.retrofit2:retrofit:2.3.0' implementation 'com.squareup.retrofit2:converter-gson:2.3.0' implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0' implementation 'io.reactivex.rxjava2:rxjava:2.1.9' implementation 'io.reactivex.rxjava2:rxandroid:2.0.2' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.1' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' }
Request Access to the Internet
Since our app will be communicating with a remote server, it needs permission to access the Internet. Open your project’s Manifest and add the following:
http://schemas.android.com/apk/res/android" package="com.jessicathornsby.myapplication">
Note that since android.permission.INTERNET
is categorised as a safe permission, we don’t need to request it at runtime.
Define Your HTTP Endpoints
Next, we need to create an interface where we describe our HTTP requests:
- Select File > New > Kotlin File/Class from the Android Studio toolbar.
- Name this file GetData.
- Open the dropdown menu, and select Interface.
- Click OK.
Each request needs to include at least one HTTP annotation, which indicates how this request should be handled. Retrofit supports annotations for all the standard request types, but we’ll only be using the @GET
annotation, since we’re retrieving data from the server.
We also need to describe the endpoint, which is the URL we’re retrieving data from. In our app, this is https://api.nomics.com/v1/markets/prices, which consists of a base URL (https://api.nomics.com/v1/) and a relative URL (prices). We’ll define the base URL elsewhere in our project, so for now we just need to worry about the relative URL.
If the API in question doesn’t require an API key, then declaring the base and relative URL should be enough, but since the Nomics API does require a key, we need to incorporate this information into our request.
This process can vary between APIs, so you should always refer to your API’s documentation for more information. In particular, be on the lookout for any section marked Authentication, as this is often where you’ll find instructions relating to API keys. The Nomics API documentation is a perfect example of this, as it has a dedicated Authentication section that describes how you should incorporate your key in the following format:
https://api.nomics.com/v1/prices?key=your-api-key-goes-here
Combining the @GET
annotation, relative URL, and your API key gives us the following:
@GET("prices?key=your-api-key-goes-here")
I’ll fetch the API data using Kotlin’s getData()
function. Since we want this function to emit RxJava Observables, we need to define our endpoint as a valid RxJava type:
fun getData() : Observable>
Here’s the completed interface:
import io.reactivex.Observable import retrofit2.http.GET interface GetData { //Describe the request type and the relative URL// @GET("prices?key=YOUR-API-KEY-HERE") fun getData() : Observable> }
Make sure you replace YOUR-API-KEY-HERE
with your own key!
Creating a Model With Kotlin’s Data Class
Next, we need to create a data class that represents the response to each of our API calls:
- Select File > New > Kotlin File / Class from the Android Studio toolbar.
- Name this class RetroCrypto, and then click OK.
- Open your new RetroCrypto class.
If you’ve previously performed Retrofit calls using Java, then this is one area where Kotlin is much more concise than the equivalent Java, thanks to data classes.
In Kotlin, a data class is a class that’s designed solely to hold some data. The Kotlin compiler implements the required hashCode()
, equals()
and toString()
methods automatically, so you can create a data model class in a single line of code.
Open your new RetroCrypto file and add the following:
data class RetroCrypto(val currency : String, val price : String)
Building the Retrofit Instance
To send network requests, we need to use the Retrofit.Builder
class to create a Retrofit instance, where we’ll call our endpoint and retrieve the cryptocurrency information.
private fun loadData() { val requestInterface = Retrofit.Builder()
Once we’ve built our Retrofit object, we need to:
-
Set the base URL. Before you can call the
build()
method onRetrofit.Builder
, you need to have at least defined the base URL. -
Set the default converter, using the
addConverterFactory()
method. Once you’ve set up the Gson converter, it’ll translate the JSON response for you automatically. -
Apply the adapter. Retrofit ships with a default adapter for executing
Call
instances, but you can apply one or more additional adapters by supplying an instance of that adapter when you’re building the Retrofit instance. To use RxJava alongside Retrofit, we need to addRxJava2CallAdapterFactory
as the call adapter, using.addCallAdapterFactory
.
This gives us the following:
private fun loadData() { //Build a Retrofit object// val requestInterface = Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) //Get a usable Retrofit object by calling .build()// .build().create(GetData::class.java)
By default, an Observable emits its data on the thread where the subscription was declared, which in Android is typically the main UI thread. However, for the best results, your app should only perform UI-related work on the UI thread, so we’ll be using RxJava’s subscribeOn
and an accompanying Scheduler
to create an alternative thread where the Observable should execute:
.subscribeOn(Schedulers.io())
Next, we’ll use the observeOn
operator, plus RxAndroid’s AndroidSchedulers.mainThread
Scheduler to send the Observable’s notifications to Android’s main UI thread, ready to display the retrieved data in our app’s UI.
For a more detailed look at subscribing to Observables in Kotlin, check out my post on Kotlin Reactive Programming With RxJava and RxKotlin.
In the following snippet, I’m also using the handleResponse()
method to handle the data we’ll receive, following a successful network operation:
myCompositeDisposable?.add(requestInterface.getData() .observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io()) .subscribe(this::handleResponse)) }
The above code returns a Disposable
—if you have previous experience of RxJava 1.0, then disposables are essentially RxJava 2.0’s version of subscriptions.
When you’ve finished with a disposable/subscription, you must dispose of it to avoid memory leaks. We can make this cleanup process easier by adding all of our disposables to a CompositeDisposable
, which is a container that can hold multiple disposables. In the above code, we’re initializing RxJava’s CompositeDisposable
, and then adding each disposable to the CompositeDisposable
, using the add()
method.
myCompositeDisposable?.add(requestInterface.getData()
Then, we simply need to clear the CompositeDisposable
during our project’s onDestroy()
method:
override fun onDestroy() { super.onDestroy() myCompositeDisposable?.clear() } }
Here’s the completed class:
import android.support.v7.app.AppCompatActivity import android.os.Bundle import io.reactivex.disposables.CompositeDisposable import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.RecyclerView import android.widget.Toast import retrofit2.converter.gson.GsonConverterFactory import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory import retrofit2.Retrofit import io.reactivex.schedulers.Schedulers import io.reactivex.android.schedulers.AndroidSchedulers import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity(), MyAdapter.Listener { private var myAdapter: MyAdapter? = null private var myCompositeDisposable: CompositeDisposable? = null private var myRetroCryptoArrayList: ArrayList? = null private val BASE_URL = "https://api.nomics.com/v1/" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) myCompositeDisposable = CompositeDisposable() initRecyclerView() loadData() } //Initialise the RecyclerView// private fun initRecyclerView() { //Use a layout manager to position your items to look like a standard ListView// val layoutManager : RecyclerView.LayoutManager = LinearLayoutManager(this) cryptocurrency_list.layoutManager = layoutManager } //Implement loadData// private fun loadData() { //Define the Retrofit request// val requestInterface = Retrofit.Builder() //Set the API’s base URL// .baseUrl(BASE_URL) //Specify the converter factory to use for serialization and deserialization// .addConverterFactory(GsonConverterFactory.create()) //Add a call adapter factory to support RxJava return types// .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) //Build the Retrofit instance// .build().create(GetData::class.java) //Add all RxJava disposables to a CompositeDisposable// myCompositeDisposable?.add(requestInterface.getData() //Send the Observable’s notifications to the main UI thread// .observeOn(AndroidSchedulers.mainThread()) //Subscribe to the Observer away from the main UI thread// .subscribeOn(Schedulers.io()) .subscribe(this::handleResponse)) } private fun handleResponse(cryptoList: List ) { myRetroCryptoArrayList = ArrayList(cryptoList) myAdapter = MyAdapter(myRetroCryptoArrayList!!, this) //Set the adapter// cryptocurrency_list.adapter = myAdapter } override fun onItemClick(retroCrypto: RetroCrypto) { //If the user clicks on an item, then display a Toast// Toast.makeText(this, "You clicked: ${retroCrypto.currency}", Toast.LENGTH_LONG).show() } override fun onDestroy() { super.onDestroy() //Clear all your disposables// myCompositeDisposable?.clear() } }
Displaying the Cryptocurrency API Data
At this point, our app can successfully retrieve data from the Nomics API, so our final task is displaying this data to the user, via a RecyclerView.
To implement a RecyclerView, we need the following:
- A
RecyclerView
widget. - A custom XML layout that the
RecyclerView
can use to display each of its items. - An adapter, which binds the data to your
RecyclerView
.
Creating a Scrollable List
Let’s start by adding a RecyclerView
widget to our activity_main.xml
file:
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" tools:context="com.jessicathornsby.myapplication.MainActivity">
Now that we have our RecyclerView
widget, we need to define the layout for each row within that RecyclerView
. I’m going to create a simple layout consisting of two TextView
s, where I’ll display the name and the price for each cryptocurrency retrieved from the Nomics API:
- Control-click your project’s res/layout folder.
- Select New > Layout resource file.
- Name this file row_layout, and then click OK.
Open the row_layout.xml
file, and add the following:
Our RecyclerView will now use this layout for each item in our list.
Binding Data With Android Adapters
The adapter is the component that’s responsible for binding each item to a View
within the RecyclerView
.
To connect the underlying data model to your app’s UI, you need to extend RecyclerView.Adapter
, and then implement the following:
-
onCreateViewHolder()
. This is where we specify the layout (R.layout.row_layout
) that each item in the RecyclerView should use, and inflate that layout usingLayoutInflater
. -
onBindViewHolder()
. Called by RecyclerView to display the data at the specified position. -
getItemCount()
. Returns the number of items present in the data set.
When you’re working with Views
, you can often simplify your code by using Kotlin Android extensions. These extensions allow you to access a layout’s Views
directly, by importing them into your class as “synthetic” properties. For example, you can import references to all the Views within your row_layout
file, using the following:
import kotlinx.android.synthetic.main.row_layout.view.*
At this point, you can access all of row_layout
’s Views
, using just their IDs—and without a findViewById()
in sight! For example, you can update the text_name
widget, using the following:
itemView.text_name.text = retroCrypto.currency
In this class, I’ll also pass the data as an ArrayList
, along with a Listener
that I’ll use to handle user input events. Finally, to make our app look a little more interesting, I’ll define an Array
of colours and then use them as the background for my RecyclerView
items.
Create a new Kotlin class named MyAdapter
and then add the following:
import android.graphics.Color import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.support.v7.widget.RecyclerView import kotlinx.android.synthetic.main.row_layout.view.* //Pass the ArrayList and a listener, and add a variable to hold your data// class MyAdapter (private val cryptoList : ArrayList, private val listener : //Extend RecyclerView.Adapter// Listener) : RecyclerView.Adapter () { interface Listener { fun onItemClick(retroCrypto : RetroCrypto) } //Define an array of colours// private val colors : Array = arrayOf("#7E57C2", "#42A5F5", "#26C6DA", "#66BB6A", "#FFEE58", "#FF7043" , "#EC407A" , "#d32f2f") //Bind the ViewHolder// override fun onBindViewHolder(holder: ViewHolder, position: Int) { //Pass the position where each item should be displayed// holder.bind(cryptoList[position], listener, colors, position) } //Check how many items you have to display// override fun getItemCount(): Int = cryptoList.count() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.row_layout, parent, false) return ViewHolder(view) } //Create a ViewHolder class for your RecyclerView items// class ViewHolder(view : View) : RecyclerView.ViewHolder(view) { //Assign values from the data model, to their corresponding Views// fun bind(retroCrypto: RetroCrypto, listener: Listener, colors : Array , position: Int) { //Listen for user input events// itemView.setOnClickListener{ listener.onItemClick(retroCrypto) } itemView.setBackgroundColor(Color.parseColor(colors[position % 8])) itemView.text_name.text = retroCrypto.currency itemView.text_price.text = retroCrypto.price } } }
Testing Your Retrofit and RxJava 2.0 App
Now it’s time to put your application to the test! Make sure you have an active Internet connection, and then install your app on an Android smartphone or tablet or AVD (Android Virtual Device). As soon as your app launches, Retrofit will retrieve all the available data from the Nomics API, and then display it as part of your RecyclerView
.
You can also download the completed project from our GitHub repo.
Conclusion
In this article, I showed you how to retrieve data from an API using Retrofit, convert that data into RxJava 2.0’s Observable format, and then display it in a RecyclerView
.
If you want to explore more APIs, then you’ll find a list of free, public APIs over at GitHub, or you can learn more about RxJava by checking out our series on Getting Started With RxJava 2 for Android, or by taking a look at our Kotlin Reactive Programming series.
Powered by WPeMatico