Today, it's rare to encounter an Android application that never connects to the internet.
Whether your app is backing up data to the cloud, authenticating users via "Sign In With Google," downloading images, or posting content to social media sites, many apps need to be in regular communication with remote servers.
Networking has become such a staple of mobile applications, that there's a wide range of libraries designed specifically to help you retrieve data from remote servers and share data with the wider internet.
In this article, I'll show you how to add networking capabilities to your Android app using Retrofit. We'll take a look at what Retrofit is, and how you can use it to connect to any HTTP-based API service, retrieve data from that API, and then use this data in your app.
By the end of this article you'll have created an Android application that issues a HTTP request to the free JSONPlaceholder API, processes the response, and then displays this information to the user, in the form of a scrollable RecyclerView.
What is Retrofit?
Retrofit is a type-safe HTTP client for Android that lets you connect to a web Application Programming Interface (API). You might use Retrofit to connect with the Twitter API so you can display the latest Tweets inside your app, retrieve information about the latest blockbusters with The Movie Database (TMDb) API, or check the forecast via the Weather API.
To make a Retrofit request, you'll need the following:
- A Retrofit class: This is where you'll create a Retrofit instance and define the base URL that your app will use for all of its HTTP requests. In our application, the base URL will be https://jsonplaceholder.typicode.com/
- An Interface that defines the HTTP operations: This where you'll describe each Retrofit request that you want to make, using special Retrofit annotations that contain details about the parameters and the request method.
- A POJO: This is a data model class that ensures the server's response gets mapped automatically, so you don't have to perform any manual parsing.
- A synchronous or asynchronous network request: Once you've crafted your network request, you'll need to execute it, and specify how your application should handle the response — whether that's a success or a failure.
After creating these components, your project structure should look something like this:
There's plenty of APIs out there, but we'll be using JSONPlaceholder, which is a fake REST API designed for people who need easy access to fake data, such as someone who's testing a new library or application, or someone who's following an online tutorial! Specifically, we'll be using the API's "/users" resource, which supplies a list of names.
Getting Started: Serialization and deserialization with Gson
To start, create a new Android project with the settings of your choice, and then add the dependencies we'll be using throughout this project.
To issue HTTP requests, we'll need the latest version of Retrofit, but we'll also need a special converter.
In most cases, server requests and responses are mapped to a language neutral format such as JSON, rather than provided as Java objects. When you're using Retrofit, you'll typically have to deal with serializing and deserializing JSON data:
- Serialization: This is the process of translating data structures or object state into a format that can be stored.
- Deserialization: This is the process where a data structure is extracted from a series of bytes.
By default, Retrofit can only deserialize HTTP bodies into OkHttp's ResponseBody type, but you can support other types by using different converters.
There are various convertors available for different formats, but we'll be using Gson, which is a Java library that can convert Java objects into their JSON representation. It can also convert JSON strings into their equivalent Java objects. One of the major benefits of using Gson is that you won't have to perform additional setup in your Java classes, as the response will be mapped automatically.
After we've successfully retrieved data from the server, we'll display it as a list. I'm also adding RecyclerView and CardView as project dependencies.
After adding these dependencies, your project-level build.gradle file should look something like this:
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:28.0.0-rc02' implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'com.squareup.retrofit2:retrofit:2.4.0' implementation 'com.squareup.retrofit2:converter-gson:2.3.0' implementation 'com.android.support:cardview-v7:28.0.0-rc02' implementation 'com.android.support:recyclerview-v7:28.0.0-rc02' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' }
Since we'll be communicating with a remote server, you also need to open your project's Manifest and add the internet permission:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.jessicathornsby.retrofitsample"> //Add the following// <uses-permission android:name="android.permission.INTERNET"/>
Note that the internet permission falls under the category of safe permissions, so you don't have to worry about requesting this permission at runtime.
Defining endpoints with HTTP annotations
Next, let's create an interface that contains information about the API endpoints we want to interact with. An endpoint is simply the URL we want to retrieve some information from, which in this instance is https://jsonplaceholder.typicode.com/users. We'll specify the base URL (https://jsonplaceholder.typicode.com) elsewhere in our project, so for now we just need to define the relative endpoint URL, which is "/users."
Each endpoint is represented as a method, which must include at least one HTTP annotation indicating how this request should be handled.
Retrofit supports the following built-in annotations for each of the standard request types:
- GET: A method that's annotated with @GET is responsible for processing a HTTP GET request, where data is retrieved from a server. This is the annotation we'll be using to retrieve the list of names.
- POST: A method that's annotated with @POST is responsible for processing a HTTP POST request, where you send data to a server.
- PUT: This method will process a HTTP PUT request, where we provide some data and ask the server to store it under a specific URL.
- DELETE: This method will process a HTTP DELETE request, which specifies a resource that should be deleted.
- HEAD: This method will process a HTTP HEAD request. HEAD is similar to GET, except that a @HEAD method retrieves information without the corresponding response body. By using @HEAD annotations, you can obtain data that's written in a response header, without having to retrieve the rest of that content.
In our app, we'll be using the @GET annotation to make a simple HTTP GET request to a relative URL, which gives us the following:
@GET("/users")
Most endpoints are declared with a specific return type in the format Call<T>. In our app, the return type will be "RetroUsers," which we'll be implementing shortly.
To create this interface:
- Select "File > New > Java Class" from the Android Studio toolbar.
- In the subsequent menu, open the "Kind" dropdown, and then select "Interface."
- Give this interface the name "GetData" and then click "OK."
- Open your new "GetData" interface, and add the following:
package com.jessicathornsby.retrofitsample; import java.util.List; import retrofit2.Call; import retrofit2.http.GET; public interface GetData { //Specify the request type and pass the relative URL// @GET("/users") //Wrap the response in a Call object with the type of the expected result// Call<List<RetroUsers>> getAllUsers(); }
To help keep things straightforward, this interface contains a single endpoint, but you can include multiple endpoints in a single interface.
Creating a data model
Next, we need to create a class that provides the getter and setter methods for each field we're expecting in the response object.
We're also going to use the @SerializedName annotation, which indicates that the field should be serialized with the provided name rather than the standard API field name.
To create this model:
- Select "File > New > Java Class" from the Android Studio toolbar.
- Name this class "RetroUsers," and then click "OK."
- Open your new "RetroUsers" class, and then add the following:
package com.jessicathornsby.retrofitsample; import com.google.gson.annotations.SerializedName; public class RetroUsers { //Give the field a custom name// @SerializedName("name") private String name; public RetroUsers(String name) { this.name = name; } //Retrieve the data using setter/getter methods// public String getUser() { return name; } public void setUser(String name) { this.name = name; } }
Building a Retrofit instance
The next step is using the Retrofit.Builder class to create a Retrofit instance, where we'll call our endpoint and retrieve the list of names.
After building our Retrofit object, we'll need to specify:
- The default convertor factory, which in this instance is Gson. You apply a convertor using the addConverterFactory() method.
- The base URL. It's not uncommon for project requirements to change, so at some point you may need to switch your project to a different URL. If your base URL is defined in a single location, then you can change it without necessarily touching all of your app's endpoints. Typically, you'll define your base URL when you instantiate the Retrofit instance, which is exactly what we're doing here.
Finally, we get a usable Retrofit object by calling .build().
We're going to implement this functionality in a reusable class, as this allows us to create the Retrofit object once and then reuse it across our entire application.
Create a new Java class ("File > New > Java Class") called "RetrofitClient," and then add the following:
package com.jessicathornsby.retrofitsample; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; public class RetrofitClient { private static Retrofit retrofit; //Define the base URL// private static final String BASE_URL = "https://jsonplaceholder.typicode.com"; //Create the Retrofit instance// public static Retrofit getRetrofitInstance() { if (retrofit == null) { retrofit = new retrofit2.Retrofit.Builder() .baseUrl(BASE_URL) //Add the converter// .addConverterFactory(GsonConverterFactory.create()) //Build the Retrofit instance// .build(); } return retrofit; } }
Although we're only using one converter in our project, you can use multiple converters in a single Retrofit instance, for example:
public static Retrofit getRetrofitInstance() { if (retrofit == null) { retrofit = new retrofit2.Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) //Add Moshi's converter factory// .addConverterFactory(MoshiConverterFactory.create()) .build(); } return retrofit;
If you apply multiple converters, then your app will always use the first compatible converter that's passed to Retrofit, which in the above example is Gson. Assuming that the above code retrieves data that can be processed by either Gson or Moshi, then it will always use the Gson converter.
Executing the network request
Now these pieces are in place, we're ready to execute our network call.
You can execute Retrofit requests synchronously using call.execute(), or asynchronously using call.enqueue. Synchronous requests get executed on the main thread and run the risk of blocking the main UI thread across all versions of Android. In addition, if you attempt to execute a Retrofit request synchronously on Android 4.0 or higher, then your application will crash with a `NetworkOnMainThreadException` error. So, we'll be using the enqueue() method to send our request asynchronously.
Retrofit will download and parse the API data on a background thread, and then return the response on the UI thread. We'll handle this response via onResponse() and onFailure() callback methods, where we'll define how our application should respond once the request has finished.
Open the MainActivity class, and add the following:
package com.jessicathornsby.retrofitsample; 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 retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import java.util.List; public class MainActivity extends AppCompatActivity { private MyAdapter myAdapter; private RecyclerView myRecyclerView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //Create a handler for the RetrofitInstance interface// GetData service = RetrofitClient.getRetrofitInstance().create(GetData.class); Call<List<RetroUsers>> call = service.getAllUsers(); //Execute the request asynchronously// call.enqueue(new Callback<List<RetroUsers>>() { @Override //Handle a successful response// public void onResponse(Call<List<RetroUsers>> call, Response<List<RetroUsers>> response) { loadDataList(response.body()); } @Override //Handle execution failures// public void onFailure(Call<List<RetroUsers>> call, Throwable throwable) { //If the request fails, then display the following toast// Toast.makeText(MainActivity.this, "Unable to load users", Toast.LENGTH_SHORT).show(); } }); } //Display the retrieved data as a list// private void loadDataList(List<RetroUsers> usersList) { //Get a reference to the RecyclerView// myRecyclerView = findViewById(R.id.myRecyclerView); myAdapter = new MyAdapter(usersList); //Use a LinearLayoutManager with default vertical orientation// RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(MainActivity.this); myRecyclerView.setLayoutManager(layoutManager); //Set the Adapter to the RecyclerView// myRecyclerView.setAdapter(myAdapter); } }
Displaying the API data
Once we've retrieved our data, we need to display it in a scrollable list.
Open your project's activity_main.xml file, and add a RecylcerView widget.
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="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_height="match_parent" android:layout_width="match_parent" tools:context="com.jessicathornsby.retrofitsample.MainActivity"> //Add the RecyclerView widget// <android.support.v7.widget.RecyclerView android:id="@+id/myRecyclerView" android:layout_height="match_parent" android:layout_width="match_parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintLeft_toLeftOf="parent" /> </android.support.constraint.ConstraintLayout>
We also need to define the layout of each row in our RecyclerView:
- Control-click your project's "res/layout" folder.
- Select "New > Layout resource file."
- Give this file the name "row_layout," and then click "OK."
- Open this file, and then add the following:
<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:card_view="http://schemas.android.com/apk/res-auto" android:id="@+id/card" android:layout_width="match_parent" android:layout_height="wrap_content" card_view:cardUseCompatPadding="true"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/user" style="@style/TextAppearance.AppCompat.Large" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:layout_marginLeft="20dp" android:lines="2"/> </RelativeLayout> </android.support.v7.widget.CardView>
Binding data with Android Adapters
A RecyclerView consists of several components:
- The RecyclerView widget, which we've already added to our layout.
- A layout manager, such as LinearLayoutManager or GridLayoutManager.
- View holder objects, which are instances of a class that extends RecyclerView.ViewHolder. Each view holder displays a single item.
- An adapter, which creates view holder objects as required and binds the view holders to their data, by calling the onBindViewHolder() method.
To bind our data, create a new Java class named "MyAdapter" and then add the following:
import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.support.v7.widget.RecyclerView; import android.widget.TextView; import java.util.List; //Extend the RecyclerView.Adapter class// public class MyAdapter extends RecyclerView.Adapter<MyAdapter.CustomViewHolder> { private List<RetroUsers> dataList; public MyAdapter(List<RetroUsers> dataList){ this.dataList = dataList; } class CustomViewHolder extends RecyclerView.ViewHolder { //Get a reference to the Views in our layout// public final View myView; TextView textUser; CustomViewHolder(View itemView) { super(itemView); myView = itemView; textUser = myView.findViewById(R.id.user); } } @Override //Construct a RecyclerView.ViewHolder// public CustomViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); View view = layoutInflater.inflate(R.layout.row_layout, parent, false); return new CustomViewHolder(view); } @Override //Set the data// public void onBindViewHolder(CustomViewHolder holder, int position) { holder.textUser.setText(dataList.get(position).getUser()); } //Calculate the item count for the RecylerView// @Override public int getItemCount() { return dataList.size(); } }
Making a network call: Testing our Retrofit app
Now it's finally time to put our app to the test! Make sure you have an active internet connection and then install the app on a physical Android smartphone or tablet, or Android Virtual Device (AVD).
As soon as you launch the app, Retrofit will download and parse the API data, and then display it inside the RecylcerView.
You can download this completed project from GitHub.
Using Retrofit with RxJava 2
It's also possible to use Retrofit in combination with other libraries, including RxJava.
To create API interface methods that return RxJava types, you'll need to add the RxJava adapter as a project dependency:
dependencies { ... ... ... implementation 'com.squareup.retrofit2:adapter-rxjava2:latest.version' }
Then, you'll need to add RxJava2CallAdapterFactory as a Call adapter when building your Retrofit instance:
public static Retrofit getRetrofitInstance() { if (retrofit == null) { retrofit = new retrofit2.Retrofit.Builder() .baseUrl(BASE_URL) //Add the following// .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); }
Once this adapter has been applied, you can return RxJava types such as Observables and Flowables. For example:
@GET("users") Observable<List<RetroUsers>> getAllUsers();
If you're interested in learning more about RxJava, check out our Starting Android App development with RxJava 2.0 article.
Wrapping up
In this tutorial, we looked at how you can request information from a remote server, process the response, and display that information in your app using the popular Retrofit HTTP client. We also touched on how to use Retrofit in combination with other libraries, including RxJava, using adapters.
Do you plan to use Retrofit in your future projects? Or do you have any recommendations for APIs that you regularly use in your Android projects?
Learn How To Develop Your Own Android App
Get Certified in Android App Development! No Coding Experience Required.
Android Authority is proud to present the DGiT Academy: the most detailed and comprehensive course covering every aspect of Android app development, run by our own Gary Sims. Whether you are an absolute beginner with zero coding knowledge or a veteran programmer, this course will guide you through the process of building beautiful, functional Android apps and bring you up to speed on the latest features of Android and Android Studio.
The package includes over 6 hours of high quality videos and over 60 different lessons. Reams of in-depth glossaries and resources, highly detailed written tutorials and exclusive access to our private slack group where you can get help directly from Gary and our other elite developers.
AA readers get an additional 60% off today. That's a savings of over $150. Claim your discount now using exclusive promo code: SIXTYOFF. This is your ticket to a lucrative future in Android App Development. What are you wating for?from Android Authority https://ift.tt/2DAgNXD
via IFTTT
Aucun commentaire:
Enregistrer un commentaire