Introduction
In this tutorial, we’re going through the basics of Retrofit and creating an Android client for HTTP requests against the GitHub API.
Background
Retrofit is a REST Client for Java and Android. With Retrofit, you can retrieve and/or upload JSON (or other structured data) via a REST based webservice through your Android mobile application very easily.
In this tutorial, we’re going through the basics of Retrofit and creating an Android client for HTTP requests against the GitHub API.
- Following are the implementation steps
- Create an Android project
- Define Gradle dependencies
- Define Android’s Network Permission
- Describe API Endpoints
- Create the UI
- Configure JSON mapping
- Create the Retrofit client
- Execute request and display data
Using the Code
Create an Android project with your preferred IDE. I will be using Android Studio. You can create an empty activity project.
Select the build.gradle (module) file under Gradle Scripts section and specify Retrofit as a dependency for your project.
dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
}
Click on the Sync Now link after adding the dependencies.
The build system will download and provide the necessary library when you run the command to build your code.
Note: Retrofit 2 (by default) is included with OkHttp as the networking layer. Therefore, you don’t need to explicitly define OkHttp as a dependency for your project. But Retrofit 2 requires a dependency for the automatic conversion of request and responses. The default converter is Gson. Gson is a very powerful library to map data structures represented as JSON to Java objects.
Define Android’s Network Permission
Android applications are designed to run inside sandboxed environments. Retrofit performs HTTP requests against APIs which are running on servers in the Internet. In order to execute such external requests from an Android application, it is required to have Internet permission to open network sockets. Therefore, you need to define these permissions inside the AndroidManifest.xml file.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="app.retrofit.nipunu.retrofitapp">
<uses-permission android:name="android.permission.INTERNET" />
<application
...
Describe API Endpoints
In order to send requests, first you need to describe the API endpoints. In this tutorial, we’ll describe a simple GitHub API endpoint.
Create an interface named GitHubClient
inside your application source set.
Select interface as the ‘kind’ and click OK.
Now you should have your interface created.
Define a method named UserRepositories
which takes a parameter of type string
which will be the user name. This method will return a RepoList
type list.
public interface GitHubClient {
List<RepoList> UserRepositories(String phrase);
}
Since we are issuing a Get
request to retrieve the results, we need to annotate our method with the endpoint as follows. The @GET
annotation declares that this request uses the HTTP GET
method.
import retrofit2.http.GET;
public interface GitHubClient {
@GET("users/USER_NAME/repos")
List<RepoList> UserRepositories(String phrase);
}
Instead of hard coding the user name, we can use Retrofit’s path parameter replacement functionality as follows:
import retrofit2.http.Path;
public interface GitHubClient {
@GET("users/{username}/repos")
List<RepoList> UserRepositories(@Path("username") String userName);
}
Currently, the UserRepositories
is a synchronous method, which will freeze the UI and crash the app for Android 4.0 or later. As a rule, network requests from the UI thread need to happen asynchronously. We can make this method call asynchronous easily by wrapping the method return inside a call<>
object as follows:
public interface GitHubClient {
@GET("users/{username}/repos")
Call<List<RepoList>> UserRepositories(@Path("username") String userName);
}
The complete GitHubClient
is as follows:
import java.util.List;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Path;
public interface GitHubClient {
@GET("users/{username}/repos")
Call<List<RepoList>> UserRepositories(@Path("username") String userName);
}
We still did not implement the RepoList
class. Let's go and add the RepoList
class to our application.
Create a class named RepoList
. This class is used to map with the return data. In this case, we only match the name of the repository.
public class RepoList {
private String name;
public String getRepoName(){return name;}
}
Create the UI
We need a UI to display the results. Let's use a ListView
control to display the names of the repositories which we receive as result.
Remove the default Textview
in the activity_mail.xml file and create a ListView
control with the id repository_list
. Set constraints to fill the entire activity’s space by the ListView
.
Reference the Listview
inside the MainActivity.class file’s onCreate
method.
public class MainActivity extends AppCompatActivity {
private ListView repositoryList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
repositoryList = (ListView)findViewById(R.id.repository_list);
}
}
Now we need to create a Layout
to hold a single item of the ListView
, which is the name of the repository in our case.
Create a new layout called repository_list_item.xml. Add a TextView
inside that layout and name it repository_name_text
.
="1.0"="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_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/repository_name_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
The ListView
needs an adapter to load data. Let's create the adapter
class.
Create a class named RepoListAdapter
which extends from ArrayAdapter
of type RepoList
.
import android.widget.ArrayAdapter;
public class RepoListAdapter extends ArrayAdapter<RepoList> {
Context context;
List<RepoList> repoList;
}
Add a constructor to the class.
public RepoListAdapter(Context context, List<RepoList> repoList) {
super(context, R.layout.repository_list_item,repoList);
this.context = context;
this.repoList = repoList;
}
Finally, override the getView
method to retrieve every repo name and set that to the TextView
.
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if (row == null) {
LayoutInflater inflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = inflater.inflate(R.layout.repository_list_item, parent, false);
}
TextView textView = (TextView) row.findViewById(R.id.repository_name_text);
RepoList list = repoList.get(position);
String repoName = list.getRepoName();
textView.setText(repoName);
return row;
}
Now inside the MainActivity
’s onCreate
method, let's instantiate the Retrofit
builder class. This class is responsible for creating the Retrofit
object for us.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
repositoryList = (ListView) findViewById(R.id.repository_list);
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.create());
}
Next, inside the same onCreate
method, let's create the Retrofit
object with the help of the builder and make the API call.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
repositoryList = (ListView) findViewById(R.id.repository_list);
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = builder.build();
GitHubClient client = retrofit.create(GitHubClient.class);
Call<List<RepoList>> call = client.UserRepositories("USER_NAME");
}
The above call is an asynchronous method call. Therefore, we need to utilize that call asynchronously via enqueue method as a new callback.
call.enqueue(new Callback<List<RepoList>>() {
@Override
public void onResponse(Call<List<RepoList>> call, Response<List<RepoList>> response) {
}
@Override
public void onFailure(Call<List<RepoList>> call, Throwable t) {
}
});
We need to implement the onResponse
and onFailure
methods as the final steps.
call.enqueue(new Callback<List<RepoList>>() {
@Override
public void onResponse(Call<List<RepoList>> call, Response<List<RepoList>> response) {
List<RepoList> list = response.body();
repositoryList.setAdapter(new RepoListAdapter(MainActivity.this,list));
}
@Override
public void onFailure(Call<List<RepoList>> call, Throwable t) {
Toast.makeText(MainActivity.this, "Error...!!!", Toast.LENGTH_SHORT).show();
}
});
Now run the application.
History
- 20th August, 2019: Initial version