Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android with Kotlin - How to use HttpUrlConnection

I´m trying to get data from a url inside an AsyncTask but I get an error when creating a new instance of HttpUrlConnection.

Something like this on Java

URL url = new URL("http://www.android.com/");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
    InputStream in = new BufferedInputStream(urlConnection.getInputStream());
    readStream(in);
finally {
    urlConnection.disconnect();
}

But I keep getting the error shown below.

class GetWeatherTask : AsyncTast<Void, Void, Void>() {

    override fun doInBackground(vararg params: Void?): Void? {
        val httpClient = HttpURLConnection();
        return null
    }
    override fun onPreExecute() {
        super.onPreExecute()
    }
    override fun onPostExecute(result: Void?) {
        super.onPostExecute(result)
    }
}

Cannot access '': it is 'protected/protected and package/' in 'HttpURLConnection' Cannot create an instance of an abstract class

Am I missing something? I tryied to create a class object extending HttpUrlConnection and try to implement the init method but I couldn't

Thanks in advance.

like image 980
axierjhtjz Avatar asked Apr 22 '15 15:04

axierjhtjz


People also ask

What is the difference between URLConnection and HttpURLConnection?

URLConnection is the base class. HttpURLConnection is a derived class which you can use when you need the extra API and you are dealing with HTTP or HTTPS only. HttpsURLConnection is a 'more derived' class which you can use when you need the 'more extra' API and you are dealing with HTTPS only.

What is the use of HttpURLConnection?

By the help of HttpURLConnection class, you can retrieve information of any HTTP URL such as header information, status code, response code etc. The java. net. HttpURLConnection is subclass of URLConnection class.


2 Answers

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()
}
like image 138
2 revs, 2 users 99% Avatar answered Nov 14 '22 01:11

2 revs, 2 users 99%


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.

like image 34
HAXM Avatar answered Nov 14 '22 03:11

HAXM