0

I have written this code snippet to download image files from firebase storage to local storage.

contentResolver.openOutputStream(uri)?.use { ops ->   // *
    Firebase.storage.getReferenceFromUrl(model.mediaUrl).stream.await().stream.use { ips ->
        val buffer = ByteArray(1024)
        while (true) {
            val bytes = ips.read(buffer)   // *
            if (bytes == -1)
                break
            ops.write(buffer, 0, bytes)   // *
        }
    }
}

In the marked lines, android studio is giving me Inappropriate blocking method call warning, highlighting openOutputStream(), read() and write() functions. I have ran the code few times and it has worked properly. This entire code snippet is inside a suspend function, called from an IO CoroutineScope.
Someone please tell me the actual cause and solutions for this warning.

Edit This code is called in the following context.

fun someFunc() {
    lifecycleScope.launch(IO) {
        val uri = getUri(model)
        ...
    }
    ...
}
...
suspend fun getUri(model: Message): Uri {
    ... // Retrive the image file using mediastore.
    if ( imageNotFound ) return downloadImage(model)
    else return foundUri
}
suspend fun downloadImage(model: Message): Uri {
    ... // Create contentvalues for new image.
    val uri = contentResolver.insert(collectionUri, values)!!
    // Above mentioned code snippet is here.
    return uri
}
Sourav Kannantha B
  • 2,860
  • 1
  • 11
  • 35

1 Answers1

0

A properly composed suspend function never blocks. It should not depend on being called from a specific dispatcher, but rather it should explicitly wrap blocking code in the appropriate dispatcher.

So the parts of your suspend function that call blocking code should be wrapped in withContext(Dispatchers.IO){}. Then the coroutine that calls it doesn't even need to specify a dispatcher. This makes it very convenient to use lifecycleScope to call functions and update UI and never worry about dispatchers.

Example:

suspend fun foo(file: File) {
    val lines: List<String> = withContext(Dispatchers.iO) {
        file.readLines()
    }
    println("The file has ${lines.size} lines.")
}
Tenfour04
  • 83,111
  • 11
  • 94
  • 154
  • Okay.. But if suspend function has to return something, then the whole function body cannot be wrapped with an withContext{}. In this case, call site has to make decisions on calling this function with appropriate dispatcher. – Sourav Kannantha B Dec 08 '20 at 18:15
  • That's not true. `withContext` returns a result. – Tenfour04 Dec 08 '20 at 18:19
  • Oh yes.. It works.. Thanks. :) But this makes code more nested! – Sourav Kannantha B Dec 08 '20 at 18:26
  • In most cases, it is less nesting at the call site, because you won't have to change dispatchers back and forth. For suspend functions that don't require multiple dispatchers, there is no nesting because you can use `= withContext` syntax for the function. – Tenfour04 Dec 08 '20 at 18:29