Android With Kotlin - How To Use HttpUrlConnection


Answer :

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****ri@gmail.com")     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.


Comments

Popular posts from this blog

Are Regular VACUUM ANALYZE Still Recommended Under 9.1?

Can Feynman Diagrams Be Used To Represent Any Perturbation Theory?