8

I wrote this code in Kotlin to download some API information using Coroutines for downloading the data. However, the code shows a lot of warnings stating the message as "Inappropriate blocking method call".

Here's the code:

class DownloadInfoClass {
    private lateinit var url: URL
    private lateinit var httpURLConnection: HttpURLConnection
    private lateinit var result: String
    private lateinit var inputStream: InputStream
    private lateinit var inputStreamReader: InputStreamReader
    private var dataMap: MutableMap<String, Any> = mutableMapOf()

    private fun downloadMethod(urls: String){
        CoroutineScope(IO).launch {
            try {
                url = URL(urls)
                httpURLConnection = url.openConnection() as HttpURLConnection
                inputStream = httpURLConnection.inputStream
                inputStreamReader = InputStreamReader(inputStream)

                var data: Int = inputStreamReader.read()
                while (data != -1){
                    val current: Char = data.toChar()
                    result += current
                    data = inputStreamReader.read()
                }
            }

            catch (e: Exception){
                e.printStackTrace()
            }
        }
        Log.i("Result: ", result)
    }
}

The specific areas where this problem occurs is:

  1. URL(urls)
  2. openConnection()
  3. read()

Can anyone help me understand why this occurs? I read through the Kotlin documentation but I wasn't able to comprehend. Also, could you tell me how to fix this issue?

Sergio
  • 27,326
  • 8
  • 128
  • 149
Arpan Sircar
  • 545
  • 2
  • 4
  • 15

3 Answers3

11

The problem is, that coroutines are built to only suspend and not block a thread. This means, that multiple coroutines can run on the same thread. When using blocking methods, they block the whole thread and will probably prevent other coroutines from running. So, it usually is bad practice to do this.

Since you obviously need to call these methods, use a dispatcher that tries to create a new thread for every coroutine, like Dispatchers.IO (which is designed for those operations, see the documentation). To do so, just wrap the blocking calls with withContext.

withContext(Dispatchers.IO) { 
   // your blocking calls 
}

I hope I could help!

theincxption
  • 189
  • 1
  • 2
  • 7
  • 6
    I see. I used the withContext() method and it does work. I looked at another and the code started working. But, I could understand why. Your explanation makes it quite clear. Thank you for the help :) – Arpan Sircar Jul 08 '20 at 11:01
  • 10
    The warning is still showing. We just ignore it? – Arst Sep 25 '21 at 04:37
1

To get rid of the warning try to use next code inside DownloadInfoClass:

private fun downloadMethod(urls: String) = CoroutineScope(Main).launch {
    url = URL(urls)
    val result = download() // suspends a coroutines launched using Main Context
    Log.i("Result: ", result)
}


private suspend fun download() = withContext(IO) { // download on background thread
    try {
        httpURLConnection = url.openConnection() as HttpURLConnection
        inputStream = httpURLConnection.inputStream
        inputStreamReader = InputStreamReader(inputStream)

        var data: Int = inputStreamReader.read()
        while (data != -1) {
            val current: Char = data.toChar()
            result += current
            data = inputStreamReader.read()
        }
        result // return result if there are no Exceptions
    } catch (e: Exception) {
        e.printStackTrace()
    }
    "" // return empty String if there is an Exception
}
Sergio
  • 27,326
  • 8
  • 128
  • 149
0
private fun downloadMethod(urls: String) {
    url = URL(urls)
    val result : Deferred<String> = coroutineScope { 
      async {download()}
    }    
    Log.i("Result: ", result.await())
}


private fun download() : String { // download on background thread
    try {
        httpURLConnection = url.openConnection() as HttpURLConnection
        inputStream = httpURLConnection.inputStream
        inputStreamReader = InputStreamReader(inputStream)

        var data: Int = inputStreamReader.read()
        while (data != -1) {
            val current: Char = data.toChar()
            result += current
            data = inputStreamReader.read()
        }
       return result // return result if there are no Exceptions
    } catch (e: Exception) {
        e.printStackTrace()
    }
    return "" // return empty String if there is an Exception
}