Android with Kotlin - How to use HttpUrlConnection

Here is a simplification of the question and answer.

Why does this fail?

val connection = HttpURLConnection()
val data = connection.inputStream.bufferedReader().readText()
// ... do something with "data"

with error:

Kotlin: Cannot access '': it is 'protected/protected and package/' in 'HttpURLConnection'

This fails because you are constructing a class that is not intended to directly be constructed. It is meant to be created by a factory, which is in the URL class openConnection() method. This is also not a direct port of the sample Java code in the original question.

The most idiomatic way in Kotlin to open this connection and read the contents as a string would be:

val connection = URL("http://www.android.com/").openConnection() as HttpURLConnection
val data = connection.inputStream.bufferedReader().readText()

This form will auto close everything when done reading the text or on an exception. If you want to do custom reading:

val connection = URL("http://www.android.com/").openConnection() as HttpURLConnection
connection.inputStream.bufferedReader().use { reader ->
    // ... do something with the reader
}

NOTE: the use() extension function will open and close the reader and handle closing on errors automatically.

About the disconnect() method

The docs for disconnect say:

Each HttpURLConnection instance is used to make a single request but the underlying network connection to the HTTP server may be transparently shared by other instances. Calling the close() methods on the InputStream or OutputStream of an HttpURLConnection after a request may free network resources associated with this instance but has no effect on any shared persistent connection. Calling the disconnect() method may close the underlying socket if a persistent connection is otherwise idle at that time.

So you decide if you want to call it or not. Here is a version of the code that calls disconnect:

val connection = URL("http://www.android.com/").openConnection() as HttpURLConnection
try {
    val data = connection.inputStream.bufferedReader().use { it.readText() }
    // ... do something with "data"
} finally {
    connection.disconnect()
}

The simplest way to do a get post request using HTTPUrlConnection is to create a common helper class that can be called from anywhere in the app to call the GET and POST request methods, without writing the same code again and again.

Below is the helper object (Singleton) class that you can use for the network call for GET and POST requests.

package com.dewari.ajay.androidnetworkcommunication.network

import org.json.JSONObject
import java.io.BufferedReader
import java.io.BufferedWriter
import java.io.IOException
import java.io.InputStreamReader
import java.io.OutputStream
import java.io.OutputStreamWriter
import java.net.HttpURLConnection
import java.net.URL
import java.net.URLEncoder
import javax.net.ssl.HttpsURLConnection


object RequestHandler {

const val GET : String = "GET"
const val POST : String = "POST"

@Throws(IOException::class)
fun requestPOST(r_url: String?, postDataParams: JSONObject): String? {
    val url = URL(r_url)
    val conn: HttpURLConnection = url.openConnection() as HttpURLConnection
    conn.readTimeout = 3000
    conn.connectTimeout = 3000
    conn.requestMethod = POST
    conn.doInput = true
    conn.doOutput = true
    val os: OutputStream = conn.outputStream
    val writer = BufferedWriter(OutputStreamWriter(os, "UTF-8"))
    writer.write(encodeParams(postDataParams))
    writer.flush()
    writer.close()
    os.close()
    val responseCode: Int = conn.responseCode // To Check for 200
    if (responseCode == HttpsURLConnection.HTTP_OK) {
        val `in` = BufferedReader(InputStreamReader(conn.inputStream))
        val sb = StringBuffer("")
        var line: String? = ""
        while (`in`.readLine().also { line = it } != null) {
            sb.append(line)
            break
        }
        `in`.close()
        return sb.toString()
    }
    return null
}

@Throws(IOException::class)
fun requestGET(url: String?): String? {
    val obj = URL(url)
    val con = obj.openConnection() as HttpURLConnection
    con.requestMethod = GET
    val responseCode = con.responseCode
    println("Response Code :: $responseCode")
    return if (responseCode == HttpURLConnection.HTTP_OK) { // connection ok
        val `in` =
            BufferedReader(InputStreamReader(con.inputStream))
        var inputLine: String?
        val response = StringBuffer()
        while (`in`.readLine().also { inputLine = it } != null) {
            response.append(inputLine)
        }
        `in`.close()
        response.toString()
    } else {
        ""
    }
}

@Throws(IOException::class)
private fun encodeParams(params: JSONObject): String? {
    val result = StringBuilder()
    var first = true
    val itr = params.keys()
    while (itr.hasNext()) {
        val key = itr.next()
        val value = params[key]
        if (first) first = false else result.append("&")
        result.append(URLEncoder.encode(key, "UTF-8"))
        result.append("=")
        result.append(URLEncoder.encode(value.toString(), "UTF-8"))
    }
    return result.toString()
  }
}

Using the above object class you can do your GET and POST requests as shown below:

//As this is network call it should be done in a separate thread
                Thread(Runnable {
                RequestHandler.requestGET(url)
                RequestHandler.requestPOST(url, postJSONObject)
            }).start()

Instead of using thread you can also use AsyncTask as followed:

    class NetworkAsyncCall(private val context: Context, private val url: String, private val requestType:
String, private val postJSONObject: JSONObject = JSONObject()
) : AsyncTask<String?, String?, String?>() {

    override fun doInBackground(vararg p0: String?): String? {
        return when (requestType) {
            RequestHandler.GET -> RequestHandler.requestGET(url)
            RequestHandler.GET -> RequestHandler.requestPOST(url, postJSONObject)
            else -> ""
        }
    }

    override fun onPostExecute(s: String?) {
        if (s != null) {
            Toast.makeText(context, s, Toast.LENGTH_LONG).show()
        }
    }
}

You can create the asyncTask as a inner class of Activity or a seperate indipendent class.

Now to call the newtwork call via the AsyncTask NetworkAsyncCall in your onCreate() or any function from which you want call the api you can write:

NOTE: The mentioned url will not work so, you have to replace it with your own.

    override fun onCreate(savedInstanceState: Bundle?) {
    setContentView(R.layout.activity_main)

    // Change the url with your own GET URL request
    val urlGET = "http://my-json-feed"
    //GET Request
    NetworkAsyncCall(this@MainActivity, urlGET, RequestHandler.GET).execute();

   //       POST Request
   //        doPost()
}

For POST request you can call:

    private fun doPost() {
    // Change the url with your own POST URL request
    val urlPOST = "http://my-json-feed"
    val postDataParams = JSONObject()
    postDataParams.put("name", "Ajay")
    postDataParams.put("email", "aj****[email protected]")
    postDataParams.put("phone", "+91 78******25")
    NetworkAsyncCall(this@MainActivity, urlPOST, RequestHandler.POST, postDataParams).execute()
}

you can check the complete code in github here. For a good explanation you can check this link.

the advantage of using the NetworkAsyncCall as seperate indipendent class is you don't have to write the AsyncTask code again, just call the same AsyncTask NetworkAsyncCall with a new object from different activitys/functions, however with this you have to implement a listener interface that you will require for the callback on onPostExecute() after getting the response from the api and to return back the response to the activity you have to perform the callback using that interface.