Build a Simple Networking Library in Android (Part 1)

Ashish Yadav
ProAndroidDev
Published in
8 min readJan 14, 2021

--

The idea of library internals has always been intimidating to me. Most people being an app developer prefer to assume the library as a black box. How do people code it, test it or release it? You are in for a treat as in my series of articles I will take you on a journey of creating, running and publishing an actual working library. Yes, we won’t be doing some “Hello World” stuff. We will create a real Android Library through which can make API requests and get the result. It will be 2 part series:

  1. All about the creation of the networking module
  2. Locally publish our library and run in demo/test app, check here

A little sneak peek to get you excited!

We will try to accomplish a very Simple Networking Library. It will make real network calls from API and give a response in JSONObject. Our library will model a network request by taking a URL, Method, Header, Body and return response via an interface.
Want to have a sneak peek of a simple GET request on a mock API? Below is a code snippet that the library will expose:

Creation of networking module

To begin with, we will create a new module that will contain all the library internals. Our module will comprise all the code necessary for making the network request, parsing the response, thread management and last but not least, returning response via an interface. Let’s head over to the starting point!

The Starting point

In the current Android Studio, it’s straightforward to get started for library development. Just a few clicks, no manual editing in any file.

Go to: File > New > New Module > Android Library. You will get something like this:

Create New Module has five options:

  1. Module name: name of your library (I have named it simple-networking)
  2. Package name: unique name to differentiate with other libraries
  3. Language: We are going to focus on Kotlin
  4. Bytecode Level: 8 means Java 8 (if you want Java 7 you change from the drop-down)
  5. Minimum SDK: Minimum Android API level for this SDK to compile.

Caution: The project which will be using our library it’s API level ≥ Libray API level. So choose the version carefully. For simplicity, I have kept the API level as 21 (94% device support)

Now hit Finish, the module is ready (after Gradle sync and indexing). Now we can talk about the architecture of the library module.

The Architecture

The libraries lack fixed structure defined as compared to app development. It depends on what you want to build and then have an architecture. We are building a Simple Networking library. It is supposed to make a network request, then parse the raw response and give a useful result back via an interface. For someone who is integrating our library should be able to configure fields like URL, HTTP Methods, Message Body, Request Headers. Here are the requirements for what we are building:

  1. Request Builder: A Builder class to set URL, method, body, header. It also has a ThreadExecutor to execute the request on bg thread. And lastly, a JSONObjectListener to listen for API response in JSON.
  2. RequestTask: Takes the Request Builder as a parameter to make the connection request and parse the response. It will give the acknowledgement back in a Response class as JSONObject.

We will try to put Request, RequestTask & Response in a single class named Http. Now that we have decided the necessary things for architecture. Let’s code the above two, starting with Request Builder.

1. Request Builder

This class will be responsible for creating the network Request. It will be a Builder class which will take the HTTP method in constructor. Supported methods will be GET, POST, DELETE, PUT. The other configurable parameters will be URL, Request Header and Request Body

Other configurable parameters will be URL, Request Header and Message Body:

  1. URL: It stands for Uniform Resource Locator. A URL is nothing more than the address of a given unique resource on the Web. The builder for URL will look like this:

The var url is internal as we already exposed the function get the URL. Firstly we will assign the received url to our class member then we will return this to follow Builder pattern. Check here, for learning about Builder pattern.

2. Request Header: According to Mozilla, HTTP headers let the client and the server pass additional information with an HTTP request or response. You can read more about headers here. By default, there are always some headers, but we can enable the functionality to add custom headers (if any). The builder for the header will look this:

The var header is internal as we already exposed the method get the header. We have added support to only allow header as a map (key-value pair). So we need to check whether it has any key-value pair, i.e. by isNullOrEmpty(). And then if all good we will put all the data from the received map in the class member’s map

3. Message Body: The message body part is optional for an HTTP message. It carries the entity-body associated with the request or response. You can read more about headers here. The builder for the body looks like this:

The var body is internal as we already exposed the method get the body. We have kept body as ByteArray as the HttpURLConnection supports only ByteArray as a type for body. But to make it easier for integration, we have to expose the body() function with JSONObject. Internally we are converting the JSON to String and then the String to a byte array. And then store it in the class member named body. Also, we have added Content-Type as application/json in the header as our body type is JSON. It is necessary as The MIME media type for JSON text is application/json

We have two more methods to support our Request class to make the connection request and send the response back. Before this, we need two more things, a ThreadExecutor (for creating a background thread) and JSONObjectListener (interface to give back the API response).

ThreadExecutor: Creates and executes thread for the connection request on the background thread.

execute() method takes a runnable and creates a HandlerThread by assigning the priority. To make it easier, we have used Process.THREAD_PRIORITY_DEFAULT, read more about it here. After making the thread (via handlerThread) we will create the handler object. Lastly, post the runnable via the handler.

Why not coroutines in place of HandlerThread?

We will have to integrate the coroutine library. Then our library will be bloated, which is not a good practice.

JSONObjectListener: This interface will give a response or failure from the API.

The response will be org.json.JSONObject and failure will be java.lang.Exception

Now we can bring back our focus on two more remaining methods to support our Request class, to make the connection request and send the response back:

  1. makeRequest(): makes the connection request for the existing builder, the Request object.

jsonObjReqListener is assigned to a class member here. This method takes an interface as an argument which will give the response back to the user. ThreadExecutor is used to pass this (current Request) in RequestTask which is a Runnable class as the ThreadExecutor’s execute() will take only Runnable (can be checked above). RequestTask, discussed in detail below.

2. sendResponse(): An internal method to invoke the response interface.

The above also takes a Response object (used to store the JSON response), the Response class is in detail below. It also takes care of failed requests. If the Exception(e) is not null then it is an exception else a success response comprising JSONObject.

Now we are done with the Request part. It contains everything required to make an API request. Check here to get the full overview of Request class. Now we will focus on how we can use the Request inside RequestTask.

2. Request Task

This class takes the above class i.e. Request as a constructor parameter. This class implements Runnable class so the main thread is not blocked. This class is Runnable as our current ThreadExecutor takes only Runnable class as a parameter. The signature of our class is as follows:

internal class RequestTask(private val req: Request) : Runnable {}

It is kept internal as we don’t want to expose it as it is not needed for someone who is integrating our library. RequestTask has three methods:

  1. run(): as the class implements runnable so this method will be overridden.

The run method creates a object of HttpURLConnection by calling getHttpURLConn(). The connection is then passed to parseResponse(). The req is actually Request class obj (passed in the constructor) which is used to access the sendResponse() method, in turn give the response back. The above also handles the exception scenario and sends back the apt response.

2. request(): Creates an object of HttpURLConnection. This method is the most core part of our library as it makes the connection writes the response as a stream.

Creates the obj by taking the req i.e. Request (passed in the constructor). It takes the URL and then opens the connection. Then takes the method (i.e. GET, POST, PUT, DELETE). Also iterates through the request header map, and writes the request body in the connection stream. In a nutshell, whatever we have used in the Builder class Request will be used here to make the connection. Lastly, makes the connection by calling connect().

3. parseResponse(): Parses the response received from the network call. Basically, the network gives back the response in ByteArrayOutputStream. The below method converts it into a useful/readable Response.

The above method checks for the valid status code. We are considering 2xx as success. After this, we get the InputStream, also taking care of the invalid status by reading the error response. Now the InputStream can be written to ByteArray and gives it to Response class (discussed below). It also disconnects after done with the request. Take a look at the full RequestTask here.

Response class: It converts the ByteArray response to JSONObject and stores it.

The ByteArray response gets converted to a String by encoding it as UTF-8. And then a JSONObject is created from the String. We can argue that we could have parsed directly to JSONObject. Response as a separate class allows us to extend functionality (in future maybe JSONArray) & keep things separated.

3. Assembling Request, RequestTask and Response

If you remember the sneak peek into a code I had mentioned in a section above. I had some Http.Request something? That’s why we will now assemble Request, RequestTask & Response in a single class. I have named it Http.kt, take a look at the same here. It’s a fairly large class so it’s not posted below and you can easily check on Github gist. I have kept it as object class because Kotlin provides an easy way to create singletons using the object declaration feature.

We are done with the core part of our library module (simple-networking). But at this stage, this library can’t be used in any project. In order to get there, we need to publish our library. Check out part 2 to learn this.

--

--