Development

Using Retrofit 2.x as REST Client

Published on

Retrofit makes it easy to consume JSON or XML data which is parsed into Plain Old Java Objects (POJOs)

If you just want to get the sample project, then you can find it here. Please note that there are some static files (drawables, fonts etc)  you'll have to download from this repo if you're to follow through with the tutorial.

So, without any further delays, let's get started by first creating a new project in Android Studio.

1. Create a New Project

Go to File ⇒ New Project. When it prompts you to select the default activity, select Empty Activity and proceed.

2. Add Dependencies

Open build.gradle in (Module: app) and add ButterKnife, Retrofit, Picasso, RecyclerView, Parcelables & Gson dependencies like this.

// ButterKnife Dependencies
implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
// Retrofit Dependencies
implementation 'com.squareup.retrofit2:retrofit:2.1.0'
implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
implementation 'com.android.support:recyclerview-v7:27.1.1'
implementation 'com.squareup.picasso:picasso:2.5.2'
implementation 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
// Parcelables
implementation 'org.parceler:parceler-api:1.1.6'
annotationProcessor 'org.parceler:parceler:1.1.6'
                

3. Internet Permissions

Don’t forget to add INTERNET permissions in AndroidManifest.xml file like this:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.adzumi.moviesdb">

    <!--Internet Permission-->
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/splash_logo" <!--Download from Repo -->
        android:label="@string/app_name"
        android:roundIcon="@mipmap/splash_logo" <!--Download from Repo -->
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".ui.MainActivity">
        </activity>
        <activity
            android:name=".ui.SplashActivity"
            android:theme="@style/AppTheme.NoActionBar" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

4. Managing API Keys

Hide API Key by adding credentials to gradle.properties which is located in the root directory.

MovieDB_API_Key = "YOUR-UNIQUE-API-KEY-HERE"

Next, let’s hide our gradle.properties file from GitHub by adding it to our .gitignore file so that the credentials we just listed will not be pushed to Github:

*.iml
...
/gradle.properties

Next we’ll create a class to contain references to our MovieDB credentials. Right click on the main package and select New ⇒ Java Class. Name this new class Constants.java. Within this file, we'll include the following code to reference the MovieDB credentials i.e the Base URL and API Key from our gradle.properties file:

public class Constants {
  public static final String MOVIE_DB_API = BuildConfig.MOVIEDB_API;
  public static final String MOVIE_DB_BASE_URL = "https://api.themoviedb.org/3/";
}

Now let's instruct our application to include our credentials in the BuildConfig file when it is created. We'll add the following to our build.gradle (Module: app) file:

android {
   ...
   buildTypes.each {
        it.buildConfigField 'String', 'MOVIE_DB_API', MovieDB_API_Key
   }
}

Here, we're instructing our application to include the values we placed in gradle.properties in the BuildConfig file when it is built. The key in our Constants.java class will now refer to the string added to gradle.properties at runtime.

5. JSON (and XML) and Data Models

We will use the, so make sure to take a glance at the MovieDB API documentation before proceeding with the next steps. We'll use the API to gather information regarding popular movies and display them in our MainActivity. This will allow us to return popular movies MovieDB has data in, instead of simply hard-coding a list of several movies. That's exponentially more powerful!

As depicted in the documentation linked above, the API contains many different endpoints. For instance, there's an endpoint to get the list of official genres for movies, an endpoint to get the primary information about a movie, and even an endpoint to get the images that belong to a TV season.

Our MovieDB application will specifically use the popular endpoint to get a list of the current popular movies on TMDb. This list updates daily.

Next, we will create data models to parse our sample JSON data with following structure.

Based on the JSON response depicted in the sample, let's create a Popular model to create objects containing necessary data for each movie. We're going to use jsonschema2pojo to generate Plain Old Java Objects from JSON or JSON-Schema.

We get two Java Pojos named Result and Popular that we’re going to copy / paste in our project.

6. Create Retrofit Instance

To issue network requests to a REST API with Retrofit, we need to create an instance using the Retrofit.Builder class and configure it with a base URL. Create a class RetrofitClient.java under services package. Here BASE_URL is the basic URL of our API where we will make a call.

package com.adzumi.moviesdb.services;

import com.adzumi.moviesdb.Constants;

import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class RetrofitClient {

    private static Retrofit retrofit = null;
    private static final String BASE_URL = Constants.MOVIE_DB_BASE_URL;

    public static Retrofit getClient() {
        if (retrofit==null) {
            retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }
        return retrofit;
    }
}

For the BASE_URL remember we're referencing from the constants.java class that we created earlier in the Managing API Keys section.

7. Define the Endpoints

The endpoints are defined inside of an interface using special retrofit annotations to encode details about the parameters and request method. We'll create an interface in our services package called API_Instance.java and define the endpoints here. Our MovieDB application will specifically use the popular endpoint to get a list of the current popular movies on TMDb. This list updates daily.

package com.adzumi.moviesdb.services;

import com.adzumi.moviesdb.models.Popular;

import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;

public interface API_Instance {
    @GET("movie/popular")
    Call<Popular> getPopularMovies(@Query("api_key") String myAPIKey);
}

8. Create custom adapter for binding data with RecycleView

Create a class named PopularMoviesAdapter under adapters package like this.

package com.adzumi.moviesdb.adapters;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.adzumi.moviesdb.R;
import com.adzumi.moviesdb.models.Result;


import java.util.List;

import butterknife.BindView;
import butterknife.ButterKnife;

public class PopularMoviesAdapter extends RecyclerView.Adapter<PopularMoviesAdapter.CustomViewHolder> {

    private List<Result> mPopular;
    private Context context;

    public PopularMoviesAdapter(Context context,List<Result> mPopular){
        this.context = context;
        this.mPopular = mPopular;
    }

    class CustomViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

        public final View mView;

        @BindView(R.id.movieTitle) TextView mMovieTitle;
        @BindView(R.id.overview) TextView mOverview;
        @BindView(R.id.popularity) TextView mPopularity;

        CustomViewHolder(View itemView) {
            super(itemView);
            mView = itemView;
            ButterKnife.bind(this, mView);
            itemView.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {
            int itemPosition = getAdapterPosition();
        }
    }

    @Override
    public CustomViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
        View view = layoutInflater.inflate(R.layout.layout_popular, parent, false);
        return new CustomViewHolder(view);
    }

    @Override
    public void onBindViewHolder(CustomViewHolder holder, int position) {
        holder.mMovieTitle.setText(mPopular.get(position).getTitle());
        holder.mOverview.setText(mPopular.get(position).getOverview());
        holder.mPopularity.setText(mPopular.get(position).getPopularity().toString());
    }

    @Override
    public int getItemCount() {
        return mPopular.size();
    }
}

9. Final Step

Inside the onCreate() method of the MainActivity.java, we initialize an instance of the API_Instance interface, the RecyclerView, and also the adapter. Finally, we call the getPopularMovies() method.

public class MainActivity extends AppCompatActivity {

    @BindView(R.id.my_toolbar) Toolbar myToolbar;
    private RecyclerView recyclerView;
    private PopularMoviesAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);

        setSupportActionBar(myToolbar);
        getSupportActionBar().setDisplayShowHomeEnabled(true);
        getSupportActionBar().setLogo(R.drawable.splash_logo);
        getSupportActionBar().setDisplayUseLogoEnabled(true);
        getSupportActionBar().setDisplayShowTitleEnabled(false);

        getPopularMovies();
    }

    

    public void getPopularMovies() {
        API_Instance service = RetrofitClient.getClient().create(API_Instance.class);
        Call<Popular> call = service.getPopularMovies(Constants.MOVIE_DB_API);
        Log.v("MY URL", String.valueOf(call.request().url()));

        call.enqueue(new Callback<Popular>() {
            @Override
            public void onResponse(Call<Popular> call, Response<Popular> response) {
                List<Result> movies = response.body().getResults();
                getCurrentPopularMovies(movies);
            }

            @Override
            public void onFailure(Call<Popular> call, Throwable t) {
                Toast.makeText(MainActivity.this, "Something went wrong...Please try later!", Toast.LENGTH_SHORT).show();
            }
        });
    }

    public void getCurrentPopularMovies(List<Result> movies) {
        recyclerView = findViewById(R.id.popularRecyclerView);
        adapter = new PopularMoviesAdapter(this,movies);
        RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(MainActivity.this);
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setAdapter(adapter);
    }
}

10. Understanding enqueue()

enqueue() asynchronously sends the request and notifies your app with a callback when a response comes back. Since this request is asynchronous, Retrofit handles it on a background thread so that the main UI thread isn't blocked or interfered with.

Finally, you can fire up the app!

I am accepting new projects. Have a look at my work and services, or learn more about me. If you think I may be able to help with your project, then don’t hesitate to contact me.


Pic Here

Get in Touch

I am available for consulting, collaboration and contract work. Feel free to contact me if you have a project in mind.

Just say “Hi” to

Email samora.y@adzumi.co.keorTwitter @_adzumi