ZOFTINO.COM android and web dev tutorials

Android OkHttp Cache Authenticator and Interceptor Example

To know how to use OkHttp in Android, you can view my previous article Android OkHttp Examples.

In this post, I am going to show how to implement OkHttp authenticator and interceptors, and show how to use OkHttp cache in android. For this, I’ll take one screen from coupon app and its rest service. The example calls rest service using OkHttp, gets coupon categories and displays the data in UI. We used spinner to display list of coupon categories in UI.

Again below example makes a service call using OkHttp, parses Json response using gson and popuates spinner in UI. All these tasks are performed in the background using AyncTask.

android  okhttp interceptor, authenticator, cache and json parsing example

Below are the details of OkHttp features used and complete code of the example.

Android OkHttp Timeouts

You can set connection timeout, read timeout and write timeout while creating OkHttp connection object OkHttpClient.

 new OkHttpClient.Builder()
        .connectTimeout(15, TimeUnit.SECONDS)
        .writeTimeout(15, TimeUnit.SECONDS)
        .readTimeout(28, TimeUnit.SECONDS)

Android OkHttp Cache

You can configure OkHttp to have cache and to serve requests from cache. Cache configuration for OkHttp can be defined using Cache object. Cache object constructor takes cache size and cache directory as parameters. In android, you can get cache directory by calling Activity’s getCacheDir() method.

 int cacheSize = 25 * 1024 * 1024;
Cache cache = new Cache(getCacheDir(), cacheSize);
 client = new OkHttpClient.Builder()
                .cache(cache)
                .build(); 

Android OkHttp Authenticator

If server responds with 401 http response, OkHttp uses configured authenticator for authentication and continue with request. To configure authenticator, you need to create Authenticator object by implementing authenticate method and add it to OkHttpClient.Builder.

 class ZoftinoAuthenticator implements Authenticator{
    //gets called if server responds with 401 http response to retry authentication
    @Override public Request authenticate(Route route, Response response) throws IOException {
        String credential = Credentials.basic("zoftinoapi", "testerd");

        //prevent infinite loop , if same credentials already tired, no need to try again
        if (credential.equals(response.request().header("Authorization"))) {
            return null;
        }

        return response.request().newBuilder()
                .header("Authorization", credential)
                .build();
    }
}
 

Android OkHttp Canceling Requests

It is a best practice to cancel pending http calls or requests when they are not required. Activity’s onStop callback method can be used to cancel pending http requests, as onStop is called when user moves away from current activity. In order to cancel OkHttp requests, you need to have reference to the Call objects using request are made.

 @Override
public void onStop(){
    super.onStop();
    //cancel okhttp request call if its not complete
    call.cancel();
}
 

Android OkHttp Interceptor

OkHttp allows you to add interceptors to add common functionality (logging and monitoring.. etc) or modify request and response with additional data. Interceptors are called in the order they are added to OkHttpClient.

OkHttp supports two types of interceptors; one is application related and second one is related to network. Application interceptors are called when request is in application layer. Network interceptors are called when request is in network layer. For example, if there is a redirect for a request, network interceptors are called twice and application interceptors are called only once.

You can add application interceptor by calling addInterceptor method and network interceptor by calling addNetworkInterceptor method on OkHttpClient.Builder.

 class LoggingInterceptor implements Interceptor {
    @Override public Response intercept(Interceptor.Chain chain) throws IOException {
        Request request = chain.request();

        long startTime = System.nanoTime();
        Log.i("",request.url()+" start time :"+startTime);
        Log.i("" ,""+request.header("Authorization"));

        Response response = chain.proceed(request);

        long endTime = System.nanoTime();
        Log.i("",request.url()+" time taken to process :"+(endTime -startTime));

        return response;
    }
}
 

Android OkHttp Interceptor Cache Authenticator Example

Activity

 package com.zoftino.androidui;


import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.ArrayAdapter;
import android.widget.Spinner;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.List;
import java.util.concurrent.TimeUnit;

import okhttp3.Authenticator;
import okhttp3.Cache;
import okhttp3.Call;
import okhttp3.Credentials;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.Route;

public class ActivityOkHttpCache extends AppCompatActivity {

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

    private OkHttpClient client;
    private Call call;

    private Spinner coupnCategories;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.okhttpcache);

        instantiateOkHttpClient();

        //spinner to be populated with coupon categories from rest service
        coupnCategories = (Spinner) findViewById(R.id.cpn_cats_spn);


    }
    @Override
    public void onStart(){
        super.onStart();

        if(client == null){
            instantiateOkHttpClient();
        }
        //get response from service and populate ui in background
        new ActivityOkHttpCache.OkHttpAyncTask().execute(this);
    }
    @Override
    public void onStop(){
        super.onStop();
        //cancel okhttp request call if its not complete
        call.cancel();
    }
    public void instantiateOkHttpClient(){
        //create cache object passing cache dir
        int cacheSize = 25 * 1024 * 1024;
        Cache cache = new Cache(getCacheDir(), cacheSize);

        //OkHttpClient instance with connection, read and write time out settings
        //add cache
        //add authenticator
        client = new OkHttpClient.Builder()
                .connectTimeout(15, TimeUnit.SECONDS)
                .writeTimeout(15, TimeUnit.SECONDS)
                .readTimeout(28, TimeUnit.SECONDS)
                .cache(cache)
                .authenticator(new ZoftinoAuthenticator())
                .addInterceptor(new LoggingInterceptor())
                .build();
    }
    //okhttp authenticator
    class ZoftinoAuthenticator implements Authenticator{
        //gets called if server responds with 401 http response to retry authentication
        @Override public Request authenticate(Route route, Response response) throws IOException {
            String credential = Credentials.basic("zoftinoapi", "testerd");

            //prevent infinite loop , if same credentials already tired, no need to try again
            if (credential.equals(response.request().header("Authorization"))) {
                return null;
            }

            return response.request().newBuilder()
                    .header("Authorization", credential)
                    .build();
        }
    }
    //okhttp intercepter logs time taken to get response from rest service using Okhttp
    class LoggingInterceptor implements Interceptor {
        @Override public Response intercept(Interceptor.Chain chain) throws IOException {
            Request request = chain.request();

            long startTime = System.nanoTime();
            Log.i("",request.url()+" start time :"+startTime);
            Log.i("" ,""+request.header("Authorization"));

            Response response = chain.proceed(request);

            long endTime = System.nanoTime();
            Log.i("",request.url()+" time taken to process :"+(endTime -startTime));

            return response;
        }
    }
    private List<String> getCouponCategories(){
        //get json response from service
        String jsonStr = callCouponCategoriesService();
        //convert json response to list
        List<String> categories = convertJsonStringToList(jsonStr);
        return categories;
    }
    public String callCouponCategoriesService(){
        String url = "http://www.zoftino.com/api/couponCategories";
        Request request = new Request.Builder()
                .url(url)
                .addHeader("Authorization" , Credentials.basic("zoftinoapi", "testerd"))
                //.cacheControl(CacheControl.FORCE_CACHE)
                .build();

        try {
            Log.d(TAG, "okhttp request created, calling service");
            call = client.newCall(request);
            Response response = call.execute();

            return response.body().string();

        } catch (IOException e) {
            Log.e(TAG, "error in getting response get request okhttp");
        }
        return null;
    }
    private List<String> convertJsonStringToList(String jsonStr){
        Log.d(TAG, "got categories from rest service");
        //instantiate Gson
        final Gson gson = new Gson();

        //pass input stream and to-type (List<String>) to fromjson
        Type listType = new TypeToken<List<String>>(){}.getType();

        List<String> categories = gson.fromJson(jsonStr, listType);
        Log.d(TAG, "json parsing is complete");
        return categories;
    }
    private class OkHttpAyncTask extends AsyncTask<Object, Void, List<String>> {

        private String TAG = ActivityOkHttpCache.OkHttpAyncTask.class.getSimpleName();
        private Context contx;

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

        @Override
        protected List<String> doInBackground(Object... params) {
            contx = (Context) params[0];
            Log.e(TAG, "processing http request in async task using OkHttp");
            return getCouponCategories();
        }

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

            if (result != null) {
                Log.e(TAG, "populate spinner in UI after response from service using OkHttp");
                //populate spinner
                ArrayAdapter<CharSequence> adapter = new ArrayAdapter(contx,
                        android.R.layout.simple_spinner_item, result);
                adapter.setDropDownViewResource(R.layout.support_simple_spinner_dropdown_item);

                coupnCategories.setAdapter(adapter);
            }
        }
    }

}
 

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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginBottom="8dp"
    android:layout_marginTop="8dp">

    <Spinner
        android:id="@+id/cpn_cats_spn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="8dp"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginLeft="8dp"
        app:layout_constraintLeft_toLeftOf="parent"
        android:layout_marginTop="8dp"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        android:spinnerMode="dropdown"></Spinner>
</android.support.constraint.ConstraintLayout>