OkHttp Interceptor for Retrofit2 | With Example

This tutorial only focuses on OkHttp Interceptor. If you want to learn how to send a network request with Retrofit from basics read this

In our last post we have learnt how to send a network request with Retrofit on Android. While Retrofit makes sending HTTP requests quite simple it also allows a unique mechanism to monitor and rewrite these requests. This is implemented with the help of OkHttp Interceptor. Intercept – the word means “to obstruct something from reaching its destinations”, similarly Interceptors obstruct a request, rewrite it and then send it to the destinations(server). For example suppose you need all the HTTP requests sent from your app to have authorization token as header. Instead of dynamically adding the same header to all the endpoints as shown here you can simply add a interceptor which will be invoked every time you send a request.

The official explanation for Interceptors is

Interceptors are to observe, modify and potentially short-circuits requests going out and the corresponding responses coming back in. Typically interceptors add, remove, or transform headers on the request  or response.

Interceptors are basically of two types

  • Application Interceptors
    These are high level interceptors which are used to intercept request and response. They are usually used to rewrite headers/query of both request and response. These are definitly invoked once even if the response is fetched from cache.
  • Network Interceptors
    These are low level interceptors used to monitor requested just as it is transmitted over the network. Are very useful to follow redirects and retries and give access to various low level details of the request. These are not invoked if the response is cached.

The diagram below explains the difference between Network and Application Interceptors

interceptors.png

Creating Interceptors

Creating or defining a Interceptor is very simple. You just need to implement the Interceptor interface and override the intercept() method as shown below.  The same interface implementation works for both  NetworkInterceptor and ApplicationInterceptor

private static class CustomInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        /*
        chain.request() returns original request that you can work with(modify, rewrite)
        */
        Request request = chain.request();

        // Here you can rewrite the request

        /*
        chain.proceed(request) is the call which will initiate the HTTP work. This call invokes the
        request and returns the response as per the request.
        */
        Response response = chain.proceed(request);

        //Here you can rewrite/modify the response

        return response;
    }
}

 

A call to chain.proceed(request) is a critical part of each interceptor’s implementation. This simple-looking method is where all the HTTP work happens, this is where the request is initiated and a response is fetched to satisfy the request.

Once you have defined your interface you can register it with the OkHttp client as shown below. Now you should register this client with Retrofit.Builder thereby for all your requests OkHttp client will be used and your interceptor will be invoked

OkHttpClient okHttpClient = new OkHttpClient.Builder()
    .addInterceptor(new CustomInterceptor()) // This is used to add ApplicationInterceptor.
    .addNetworkInterceptor(new CustomInterceptor()) //This is used to add NetworkInterceptor.
    .build();

//Defining the Retrofit using Builder
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl(BASE_URL) //This is the onlt mandatory call on Builder object.
    .client(okHttpClient) //The Htttp client to be used for requests
    .addConverterFactory(GsonConverterFactory.create()) // Convertor library used to convert response into POJO
    .build();

 

 

Logging Request and Response

As developers it is very important that we log the requests/responses which are sent and received  from the app. These logs give us details about the headers, response body, request body and various other details which are crucial for debugging any error. Thanks to Interceptors logging all the HTTP operations on Android is very simple.

Retrofit provides us with a Custom Interceptor – HttpLoggingInterceptor which can be registered with OkHttpClient. With this you will be able to print all the logs for the HTTP operations through this client.

HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://backend.example.com")
        .client(client)
        .addConverterFactory(GsonConverterFactory.create())
        .build();

 

Rewriting Requests- Adding/Removing Headers

We have already seen how to define a Custom Interceptor. Now in this section we will define a Custom Interceptor to modify the headers sent with a network request.

private static class RequestInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        /*
        chain.request() returns original request that you can work with(modify, rewrite)
        */
        Request originalRequest = chain.request();

        Headers headers = new Headers.Builder()
            .add("Authorization", "auth-value")
            .add("User-Agent", "you-app-name")
            .build();

        Request newRequest = originalRequest.newBuilder()
            .addHeader("Authorization", "auth-value") //Adds a header with name and value.
            .addHeader("User-Agent", "you-app-name")
            .cacheControl(CacheControl.FORCE_CACHE) // Sets this request's Cache-Control header, replacing any cache control headers already present.
            .headers(headers) //Removes all headers on this builder and adds headers.
            .method(originalRequest.method(), null) // Adds request method and request body
            .removeHeader("Authorization") // Removes all the headers with this name
            .build();

        /*
        chain.proceed(request) is the call which will initiate the HTTP work. This call invokes the
        request and returns the response as per the request.
        */
        Response response = chain.proceed(newRequest);

        return response;
    }

}

 

Rewriting Response with OKHttp Interceptor

Similarly, OkHttp Interceptor can be used to rewrite/ modify response from the server. With this you can not only rewrite response headers but also can make changes to the response body.

In the example below we read the response code and build a new response body based on the response code.

private static class ResponseInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        // We obtain the response using chain.proceed() API. This invokes the request and return the response
        Response response = chain.proceed(chain.request());
        try {

            JSONObject jsonObject = new JSONObject();

            if (response.code() == 200) {
                jsonObject.put("code", 200);
                jsonObject.put("status", "OK");
                jsonObject.put("message", new JSONObject(response.body().string()));
            } else {
                jsonObject.put("code", 404);
                jsonObject.put("status", "ERROR");
                jsonObject.put("message", new JSONObject(response.body().string()));
            }
            MediaType contentType = response.body().contentType();
            ResponseBody body = ResponseBody.create(contentType, jsonObject.toString());
            return response.newBuilder().body(body).build();
        } catch (JSONException e) {
            e.printStackTrace();
        }

        return response;
    }
}

Android DownloadManager Example [Complete Tutorial]

We have a separate tutorial section on this website for HTTP operations. Over there we have already covered all the different network operations like sending a GET/POST request, uploading images, logging request, adding headers etc. We have used two of the most popular Libraries- Volley and Retrofit which provide complete support for all these network operations. But when it comes to downloading large files you cant use Retrofit or Volley, both of these recommend using DownloadManager. And that is what today’s tutorial is all about – DownloadManager Example

The DownloadManager is a system service that handles long-running HTTP downloads. Clients may request that a URI be downloaded to a particular destination file.

Here are some advantages of using DownloadManager

  • It performs all the HTTP interaction on background thread by default. You dont need to create any thread.
  • It gracefully handles connectivity issues if the network changes or device reboots by automatically retrying download after failure due to any reason.
  • It provides features like pause/ resume download thereby improving user experience.
  • Has a in built mechanism  to inform the user of download progress using notification.

Downloading a File

In this section of DownloadManager Example we will prepare our download request and initiate the File Download  

Add Permissions

First step in this DownloadManager Example is to add INTERNET permission to the AndroidManifest.xml

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

Create Activity

We need to create only one Activity in this DownloadManager Example which will have a button to initiate the download of file. Following is the layout file for this activity

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    tools:context="com.example.irshadkumail.downloadexample.MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:id="@+id/download"
        android:text="DOWNLOAD"
        android:layout_centerInParent="true"
        android:layout_height="wrap_content" />


</RelativeLayout>

Now in our Java file of the Activity we initialize the button and set up a click listener as shown below

public class MainActivity extends AppCompatActivity {

    private Button button;

    private long downloadID;

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

        button=findViewById(R.id.download);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
            }
        });
    }
}
  • Note that we have also created a global long variable which will be used to store download identifier id. More about this in next section

Prepare the DownloadManager.Request

Before starting the download we need to create a  DownloadManager.Request object with all the details of the download and conditions to start it. DownloadManager.Request has provided us with multiple APIs to set information necessary to begin the download. The only mandatory information to start a download is network URI. Even if you have not specified any destination, Android stores the downloaded file in a shared storage . Below code snippet shows how we prepare our DownloadManager.Request with all the details.

       File file=new File(getExternalFilesDir(null),"Dummy");
       /*
       Create a DownloadManager.Request with all the information necessary to start the download
        */

       DownloadManager.Request request=new DownloadManager.Request(Uri.parse("http://speedtest.ftp.otenet.gr/files/test10Mb.db"))
               .setTitle("Dummy File")// Title of the Download Notification
               .setDescription("Downloading")// Description of the Download Notification
               .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE)// Visibility of the download Notification
               .setDestinationUri(Uri.fromFile(file))// Uri of the destination file
               .setRequiresCharging(false)// Set if charging is required to begin the download
               .setAllowedOverMetered(true)// Set if download is allowed on Mobile network
               .setAllowedOverRoaming(true);// Set if download is allowed on roaming network

     
   

Following are some important APIs used to set information of the download

  • setNotificationVisibility(int)
    This API is to control whether a system notification is shown while this download is running or when it is completed.
    It can takes any of the following predefined values: 
    #VISIBILITY_HIDDEN
    #VISIBILITY_VISIBLE 
    #VISIBILITY_VISIBLE_NOTIFY_COMPLETED
    If set to VISIBILITY_HIDDEN, this requires the permission android.permission.DOWNLOAD_WITHOUT_NOTIFICATION.
  •  setDescription(String), setTitle(String)
    These APIs are used to set the description and title of the download notification (if displayed).
  • setDestinationInExternalFilesDir(Context ,String,String) , setDestinationUri(Uri), setDestinationInExternalPublicDir(String,String)
    These APIs are used to set the destination file path of the downloaded file. You can start a download even without specifying the destination, in which case file is temporarily stored in shared storage. Also if you are storing the file in external storage you need to add STORAGE permissions in the Manifest.
  • addRequestHeader(String,String)
    This API is used to add a request header to the HTTP request used to download the file

Apart from these they are a lot of other APIs like setAllowedOverRoaming(), setRequiresCharging(), setAllowedOverMetered()  etc which define the conditions for the download to start. We have not used all the APIs in this DownloadManager Example but you can read more about them here.

Initiate the download

Once the DownloadManager.Request is ready with all the information you can start the download as shown in snippet below

DownloadManager downloadManager= (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
downloadID = downloadManager.enqueue(request);// enqueue puts the download request in the queue.
  • enqueue(request) returns  a unique long ID which acts as an identifier for the download. Note that calling enqueue() doesnt necessarily start the download immediately. It rather schedules the download request in a queue. Once the DownloadManager is free is starts the download.
  • Important thing to note is how we initialize global variable downloadID . enqueue() returns an ID for the download, unique across the system. Global variable downloadID is used to store this ID. It is used to reference this download in future.

Listen to Download Complete

So far in this DownloadManager Example we saw to initiate downloading a file in Android. This section covers how to be notified when your download is completed.

One thing to note is that DownloadManager  is a separate system service which downloads files requested by client. Here your app is the client and once you call enqueue() from your app it is now the responsibility of DownloadManager to schedule that download and save the file at destination. Therefore it is important that the client is informed when the download is completed. Android DownloadManager sends a ACTION_DOWNLOAD_COMPLETE broadcast intent when any download is completed. You can listen for this broadcast using a BroadcastReceiver and identify if your download is completed using the unique long ID returned by enqueue().

Not sure what Broadcast is and How does BroadcastReceiver work?  Read this simple tutorial

You can be notified when your download is complete by following three steps

  • Create a BroadcastReceiver as shown in snippet below.Inside the receiver we just check if the received broadcast is for our download by matching the received download id with our enqueued download.
    private BroadcastReceiver onDownloadComplete = new BroadcastReceiver() {
           @Override
           public void onReceive(Context context, Intent intent) {
    
               //Fetching the download id received with the broadcast
               long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
    
               //Checking if the received broadcast is for our enqueued download by matching download id
               if (downloadID == id) {
                   Toast.makeText(MainActivity.this, "Download Completed", Toast.LENGTH_SHORT).show();
               }
    
           }
       };
  • Once the BroadcastReceiver is created you can register for ACTION_DOWNLOAD_COMPLETE in the onCreate method of your activity. This ACTION_DOWNLOAD_COMPLETE broadcast is fired everytime any file download is completed by DownloadManager.
    @Override
       protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_main);
    
           button=findViewById(R.id.download);
           registerReceiver(onDownloadComplete,new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
           button.setOnClickListener(new View.OnClickListener() {
               @Override
               public void onClick(View view) {
                   beginDownload();
               }
           });
       }
  • It is also important that you unregister the BroadcastReceiver in onDestroy. This ensures you only listen for this broadcast as long as the activity is active
    @Override
      public void onDestroy() {
          super.onDestroy();
          unregisterReceiver(onDownloadComplete);
      }
    

     

DownloadManager Example: Complete Code

This completes DownloadManager Example. Below is the complete code of your Activity

public class MainActivity extends AppCompatActivity {

    private Button button;

    private long downloadID;

    private BroadcastReceiver onDownloadComplete = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {

            //Fetching the download id received with the broadcast
            long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);

            //Checking if the received broadcast is for our enqueued download by matching download id
            if (downloadID == id) {
                Toast.makeText(MainActivity.this, "Download Completed", Toast.LENGTH_SHORT).show();
            }

        }
    };

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

        button=findViewById(R.id.download);
        registerReceiver(onDownloadComplete,new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                beginDownload();
            }
        });
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(onDownloadComplete);
    }

    private void beginDownload(){

        File file=new File(getExternalFilesDir(null),"Dummy");
        /*
        Create a DownloadManager.Request with all the information necessary to start the download
         */

        DownloadManager.Request request=new DownloadManager.Request(Uri.parse("http://speedtest.ftp.otenet.gr/files/test10Mb.db"))
                .setTitle("Dummy File")// Title of the Download Notification
                .setDescription("Downloading")// Description of the Download Notification
                .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE)// Visibility of the download Notification
                .setDestinationUri(Uri.fromFile(file))// Uri of the destination file
                .setRequiresCharging(false)// Set if charging is required to begin the download
                .setAllowedOverMetered(true)// Set if download is allowed on Mobile network
                .setAllowedOverRoaming(true);// Set if download is allowed on roaming network

        DownloadManager downloadManager= (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
        downloadID = downloadManager.enqueue(request);// enqueue puts the download request in the queue.
    }


}

 

We hope you are now clear on how to download files on Android. You can even learn how to upload files on Android by reading this simple tutorial

IllegalStateException : Cannot perform this action after onSaveInstanceState()

Many of you might have encountered this exception while working with fragments . The source for this issue can be different based on the use case but if you look under the hood this happens only because of one reason- mismanagement of Activity states. In this article we will be discussing the same. First we will explain what happens behind the scenes and what is source of this crash and then most importantly how to resolve it.

stack_trace.PNG

Continue reading

[Android Example] Best solution for Fragment IllegalStateException

Many of you might have encountered this exception while working with fragments . The source for this issue can be different based on the use case but if you look under the hood this happens only because of one reason- mismanagement of Activity states. In this article we will be discussing the same. First we will explain what happens behind the scenes and what is source of this crash and then most importantly how to resolve it.

stack_trace.PNG

Continue reading

Create and Display Notification on Android with Example

Displaying notification is one of the most important features in an Android app. Notification are short messages which appear on home screen of the device. In this example you will learn how to programatically create and display a Notification from your app which will work for all versions of Android (Even Oreo)

notification_image

Continue reading

Android Example : Request Location Updates with FusedLocationProviderApi

In our last post we developed a app to retrieve the current location of the device on Android. While retrieving the current location can be useful in a lot of  real life scenarios sometimes it is just not enough. Thanks to Google’s FusedLocationProviderApi receiving location change updates in the app is fairly simple. In the following example we will be implementing an app which will be receiving updates on every location change.

 

request_location_updates

Continue reading

[Android Example] Location Updates with FusedLocationProviderApi

In our last post we developed a app to retrieve the current location of the device on Android. While retrieving the current location can be useful in a lot of  real life scenarios sometimes it is just not enough. Thanks to Google’s FusedLocationProviderApi receiving location change updates in the app is fairly simple. In the following example we will be implementing an app which will be receiving updates on every location change.

Continue reading

Android Broadcast Receivers with Example

What are Broadcasts?

Consider a situation in which you are downloading a large file in your app and suddenly the battery goes critically low , you don’t want the download to stop abruptly and would like to show the user a notification/dialog so that he can pause the download.  Creating a service just to keep on checking the battery power would be too much work for a simple notification. What if the android system itself notifies you of any such system event like battery low or airplane mode.

Guess what?  It does!. And you can handle these events very efficiently.. These events are known as broadcasts and you need to handle them in a broadcast receiver. Your app can have its own custom broadcasts which it can send within the app or to other apps on the device to notify them of an event they might be interested in.

One common use case of  sending broadcasts within the same app is mentioned here:

Suppose you have updated your  profile details in an activity inside the app, you would like the updated changes to be reflected immediately in all the other active activities without having to refresh them. In this case you can send a broadcast informing the other activities that the profile details are updated

Continue reading

BroadcastReceiver in Android | With Example

BroadcastReceiver is one of the four basic Android Components. In this example we will be learning how to create a BroadcastReceiver and listen for broadcast. Before that let us first understand what are Broadcasts

What are Broadcasts?

Consider a situation in which you are downloading a large file in your app and suddenly the battery goes critically low , you don’t want the download to stop abruptly and would like to show the user a notification/dialog so that he can pause the download.  Creating a service just to keep on checking the battery power would be too much work for a simple notification. What if the android system itself notifies you of any such system event like battery low or airplane mode.

Guess what?  It does!. And you can handle these events very efficiently. These events are known as broadcasts and you need to handle them in a broadcast receiver. Your app can have its own custom broadcasts which it can send within the app or to other apps on the device to notify them of an event they might be interested in.

One common use case of  sending broadcasts within the same app is mentioned here:

Suppose you have updated your  profile details in an activity inside the app, you would like the updated changes to be reflected immediately in all the other active activities without having to refresh them. In this case you can send a broadcast informing the other activities that the profile details are updated

Continue reading

Android Development System Requirements

system-configuration

About 85% of the world’s smartphone run on Android. Android SDK is available for free. Many App developers have turned into millionaires overnight. I don’t think you need any other reason to learn Android development. But first you need to set up an environment for developing. Today we will be discussing what are the basic Android Development system requirements.
Continue reading