ZOFTINO.COM android and web dev tutorials

Android Retrofit Example

Retrofit http library allows apps to interact with Rest Services. Using Retrofit, one can easily build http get, post, and multipart request components to interact with your application services running on server, meaning building rest clients is easy with Retrofit. Because of its ease of use, this is one of the widely used frameworks in android applications.

Retrofit is not an http client library like apache http client, OkHttp, and HttpUrlConnection. But Retrofit uses http client library and provides type-safe api for apps to interact with Rest Services. By default Retrofit uses OkHttp library as http client.

Retrofit is a type-safe library because it takes care of serialization, deserialization, concurrency and response handling. Retrofit functionality can extended to make it work with libraries like RxJava and Java8 with adapters. Retrofit supports easy plugging capability for converters and adapters.

Retrofit is similar to the other networking library called volley.

Adding Retrofit Libraries

Add below libraries to your project to use retrofit and gson converter. OkHttp is part of retrofit latest version so no need to add it separately to gradle build properties file.

compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.google.code.gson:gson:2.8.0'
compile 'com.squareup.retrofit2:converter-gson'

Creating Rest Service Client Using Retrofit

Below are the steps to create retrofit service client and use it to get response from server.

  1. Define api interface using appropriate annotations. You can get more details on retrofit annotations in below sections.
     public interface CouponApi {
        @GET("coupons/")
        Call<CouponsWrapper> getCoupons(@Query("status") String status);
    }
     
  2. Build Retrofit object using Retrofit.Builder. You can set base url and converter etc.
     Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build();
     
  3. Create service client, which is an implementation of api interface you defined, using the Retrofit object, passing your api interface to create() method on Retrofit object.
     CouponApi couponService = retrofit.create(CouponApi.class); 
  4. Now, you can use the service client to create Call object.
     Call<CouponsWrapper> call = couponService.getCoupons("latest");
     
  5. To send request to server and get response, you can either call execute method to make synchronous request or call enqueue method to make asynchronous request on Call object.
        Response<CouponsWrapper> rp  =  call.execute();
                    return rp.body(); 

Retrofit Converters

To make retrofit convert the responses it receives from server, from different formats to java objects and from java objects to other formats which services take as input, you need to add converters to retrofit. For example, to convert json response to your java model object or java model object to Json, you need to add gson converter to retrofit.

Retrofit provides converters for various serialization libraries like gson, jacson, moshi and simple xml.

Converter can be added by calling addConverterFactory method on Retrofit.Builder object.

Retrofit retrofit = new Retrofit.Builder()  
        .baseUrl("http://...")
        .addConverterFactory(GsonConverterFactory.create())
        .build();

Retrofit Call Adapters

You can plug adapters to retrofit to make retrofit workable with other libraries like RxJava, Guva and Java 8. Retrofit services can return types from other libraries with adapters. For example, to make retrofit service return object types from RxJava, you need to add RxJava adapter as shown below.

You can add adapters to retrofit by calling addCallAdapterFactory method on Retrofit.Builder class.

 Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("http//...")
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    .build();
 Observable<Coupons> latestCoupons = couponService.getCoupons("latest"); 

Android Retrofit Get

To send a get request using retrofit, you need to define the method in your api interface with GET annotation as shown below.

    @GET("coupons/")
    Call<CouponsWrapper> getCoupons(); 

Android Retrofit Get With QueryString

To pass query string with http get request using retrofit, annotate input parameter with QUERY. You can define parameter name by passing it to QUERY annotation.

    @GET("coupons/")
    Call<CouponsWrapper> getCoupons(@Query("status") String status); 

Android Retrofit Dynamic Url

You can create dynamic URLs with retrofit using {} in the path and PATH annotation.

    @GET("coupon/{store}")
    Call<CouponsWrapper> getCoupons(@Path("store") String storename);

Android Retrofit Post Form

To send post request with parameters and their values like submitting an html forms, you need to annotate the method with @FormUrlEncoded and @POST and annotate each parameter you want to send in the post request with @Field.

    @FormUrlEncoded    
    @POST("coupons/")
    Call<CouponsWrapper> getCoupons(@Field("store") String storeName, @Field("category") String category);

Android Retrofit Post Json

To send Json request to server, annotate method with POST and annotate input java object which contains json data with Body annotation as shown below. In order for this to work, you need to add gson converter, retrofit uses this to convert java object annotated as body to Json string which will be sent to server as request body.

    @POST("add/coupons")
    Call<String> loadCoupons(@Body Coupons); 

Android Retrofit Multipart

To define api which sends multipart data to server, for example image and title of image, you need to annotate the api method with @Multipart and @PUT, and annotate parts with @Part annotation.

    @Multipart
    @PUR("uploadCoupons/")
    Call<String> uploadCoupons(@Part("xml") RequestBody couponXml, @Part("date") RequestBody date);

Android Retrofit Asynchronous Request

As mentioned before, http request can be sent to server synchronously (on the main thread) by calling execute method or asynchronously (on worker thread) by calling enqueue method on the Call object passing Callback object which handles response and error.

          call.enqueue(new Callback<CouponsWrapper>() {
                @Override
                public void onResponse(Call<CouponsWrapper> call, Response<CouponsWrapper> response) {
                    response.body().getCouponList();
		    Log.d(TAG, "got response from service using retrofit, performing further processing");
                }

                @Override
                public void onFailure(Call<CouponsWrapper> call, Throwable t) {
 		    Log.e(TAG, "error in getting response from service using retrofit");
                }
            });

Android Retrofit Adding Header

Headers can be added to request by annotating your api method with Headers annotation.

    @Headers({"Cache-Control: max-age=200000", "Authorization: askjfaks"})
    @GET("coupons/")
    Call<CouponsWrapper> getCoupons(@Query("status") String status);

Android Retrofit Example

Now, I am going to show how to use retrofit in android with complete example. For this, I am going to use one screen from coupons app. This screen uses coupon app’s rest service and displays list of coupons in recycler view using grid layout manager and custom recycler view item decorator.

Here the call to service and parsing of JSON response is done using retrofit and gson converter.

android retrofit gson recyclerview example

Retrofit api interface

package com.zoftino.androidui;

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

public interface CouponApi {
    @GET("coupons/")
    Call<CouponsWrapper> getCoupons(@Query("status") String status);
}

Retrofit Object Singleton

package com.zoftino.androidui;


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

public class CouponClient {
    public static final String BASE_URL = "http://www.zoftino.com/api/";
    private static Retrofit retrofit = null;


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

Model object

package com.zoftino.androidui;

public class Coupon {
    private String store;
    private String coupon;
    private String expiryDate;
    private String couponCode;

    public String getStore() {
        return store;
    }

    public void setStore(String store) {
        this.store = store;
    }

    public String getCoupon() {
        return coupon;
    }

    public void setCoupon(String coupon) {
        this.coupon = coupon;
    }

    public String getExpiryDate() {
        return expiryDate;
    }

    public void setExpiryDate(String expiryDate) {
        this.expiryDate = expiryDate;
    }

    public String getCouponCode() {
        return couponCode;
    }

    public void setCouponCode(String couponCode) {
        this.couponCode = couponCode;
    }

}

Activity

package com.zoftino.androidui;

import android.content.Context;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;

import java.io.IOException;
import java.util.List;

import retrofit2.Call;
import retrofit2.Response;


public class ActivityRetrofit extends AppCompatActivity {

    private String TAG = ActivityRetrofit.class.getSimpleName();

    private RecyclerView rv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.recylcer_grid);
        Log.d(TAG, "set retrofit example layout");

        rv = (RecyclerView) findViewById(R.id.store_rv);

        GridLayoutManager gridlm = new GridLayoutManager(this, 2);
        gridlm.setOrientation(GridLayoutManager.VERTICAL);
        gridlm.setSpanCount(2);
        rv.setLayoutManager(gridlm);

        BoundaryItemDecoration bid = new BoundaryItemDecoration(this, Color.DKGRAY, 4);
        rv.addItemDecoration(bid);
        Log.d(TAG, "set retrofit recycler view and start asynctask");

        new ActivityRetrofit.RetrofitAyncTask().execute(this);
    }
    private class RetrofitAyncTask extends AsyncTask<Context, Void, List<Coupon>> {

        private String TAG = ActivityRetrofit.RetrofitAyncTask.class.getSimpleName();
        private Context contx;

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected List<Coupon> doInBackground(Context... params) {
            Log.e(TAG, "processing http request in async task using retrofit");
            contx = (Context) params[0];

            //create service which is implementation of CouponApi interface by passing it to retrofit object
            CouponApi couponService = CouponClient.getCouponClient().create(CouponApi.class);
            //call api method which returns call object
            Call<CouponsWrapper> call = couponService.getCoupons("latest");

            try {
                //call the api synchronously as this is part of back ground thread already with AsncTask
                Response<CouponsWrapper> rp  =  call.execute();
                return rp.body().getCouponList();
            } catch (IOException e) {
                Log.e(TAG, "error in getting response from service using retrofit");
            }

            return null;
        }

        @Override
        protected void onPostExecute(List<Coupon> result) {
            super.onPostExecute(result);

            if (result != null) {
                Log.e(TAG, "populate recyclerview grid in UI after response from service using retrofit");
                //populate recyclerview grid from retrofit response result

                CouponsRecyclerViewGridAdapter rvadapter = new CouponsRecyclerViewGridAdapter(result,contx);
                rv.setAdapter(rvadapter);
            }
        }
    }
}

Recycler view adapter

package com.zoftino.androidui;

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.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.List;


public class CouponsRecyclerViewGridAdapter extends RecyclerView.Adapter<CouponsRecyclerViewGridAdapter.ViewHolder> {

    private List<Coupon> couponsList;
    private Context context;

    public CouponsRecyclerViewGridAdapter(List<Coupon> cLst, Context ctx) {
        couponsList = cLst;
        context = ctx;
    }

    @Override
    public CouponsRecyclerViewGridAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.retrofitgriditem, parent, false);

        CouponsRecyclerViewGridAdapter.ViewHolder viewHolder = new CouponsRecyclerViewGridAdapter.ViewHolder(view);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(CouponsRecyclerViewGridAdapter.ViewHolder holder, int position) {
        Coupon coupon = couponsList.get(position);
        holder.couponTv.setText(coupon.getCoupon());
        holder.expiryDateTv.setText(coupon.getExpiryDate());
        holder.couponCodeTv.setText(coupon.getCouponCode());

        int id = context.getResources().getIdentifier(coupon.getStore(), "drawable", context.getPackageName());
        holder.storeImg.setImageResource(id);
    }

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

    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

        public TextView couponTv;
        public TextView expiryDateTv;
        public TextView couponCodeTv;
        public ImageView storeImg;

        public ViewHolder(View view) {
            super(view);
            couponTv = (TextView) view.findViewById(R.id.coupon_t);
            expiryDateTv = (TextView) view.findViewById(R.id.expiry_dt_t);
            couponCodeTv = (TextView) view.findViewById(R.id.coupon_code_t);
            storeImg = (ImageView) view.findViewById(R.id.store_i);

            view.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {
            Toast.makeText(context, "You clicked coupons at  "+ getAdapterPosition(),
                    Toast.LENGTH_LONG).show();
        }
    }
}

Recyclerview grid item layout

<?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"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginBottom="8dp"
    android:layout_marginTop="8dp">

    <ImageView
        android:id="@+id/store_i"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:srcCompat="@drawable/bestbuy"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="8dp"
        android:layout_marginLeft="8dp"
        app:layout_constraintLeft_toLeftOf="parent"
        android:layout_marginRight="8dp"
        app:layout_constraintRight_toRightOf="parent"></ImageView>

    <TextView
        android:id="@+id/coupon_t"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        app:layout_constraintTop_toBottomOf="@+id/store_i"
        android:layout_marginLeft="8dp"
        app:layout_constraintLeft_toLeftOf="parent"
        android:layout_marginRight="8dp"
        app:layout_constraintRight_toRightOf="parent"></TextView>

    <TextView
        android:id="@+id/coupon_exp_txt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Expiry"
        app:layout_constraintLeft_toLeftOf="parent"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="16dp"
        app:layout_constraintTop_toBottomOf="@+id/coupon_t"></TextView>

    <TextView
        android:id="@+id/expiry_dt_t"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="8dp"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toRightOf="@+id/coupon_exp_txt"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="16dp"
        app:layout_constraintTop_toBottomOf="@+id/coupon_t"
        app:layout_constraintHorizontal_bias="0.278"></TextView>

    <TextView
        android:id="@+id/coupon_code_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Coupon Code"
        android:layout_marginTop="8dp"
        app:layout_constraintTop_toBottomOf="@+id/coupon_exp_txt"
        android:layout_marginLeft="8dp"
        app:layout_constraintLeft_toLeftOf="parent"></TextView>

    <TextView
        android:id="@+id/coupon_code_t"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        app:layout_constraintTop_toBottomOf="@+id/coupon_exp_txt"
        app:layout_constraintLeft_toRightOf="@+id/coupon_code_text"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintHorizontal_bias="0.278"></TextView>
</android.support.constraint.ConstraintLayout>

Activity layout

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.v7.widget.RecyclerView
        android:id="@+id/store_rv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></android.support.v7.widget.RecyclerView>
</LinearLayout>