I'm fairly new to coroutines, therefore I wanted to ask for an opinion.
I've created an extension function to read data from the InputStream
:
suspend fun InputStream.readData(): ByteArray {
return withContext(Dispatchers.IO) {
while (available() == 0) {
delay(10)
}
val count = available()
val buffer = ByteArray(count)
read(buffer, 0, count)
return@withContext buffer
}
}
Do you think is there something I could improve from the coroutines point of view?
Java Thread suspend() methodThe suspend() method of thread class puts the thread from running to waiting state. This method is used if you want to stop the thread execution and start it again when a certain event occurs. This method allows a thread to temporarily cease execution.
Suspended means directly stopped. Blocked means indirectly stopped by something higher priority or intervening in access to some mutually accessible resource.
Coroutines are more efficient than threads because they are suspended and resumed instead of blocking execution. However, we need to block threads in some specific use cases. For example, in the main() function, we need to block the thread, otherwise, our program will end without waiting for the coroutines to complete.
An InputStream you can create on a ParcelFileDescriptor, which will take care of calling android. PushbackInputStream. A PushbackInputStream adds functionality to another input stream, namely the ability to "push back" or "unread" bytes, by storing pushed-back bytes in an internal buffer.
while (available() == 0) {
delay(10)
}
Here you hope you've achieved non-blocking IO using the InputStream
. You imagine that the data would be somehow "trickling in" on its own and you could just wait for it to become available so you can pick it up without blocking in the subsequent read()
call.
This behavior isn't universal to any InputStream
. In fact, it probably only works with a SocketInputStream
and there it also has issues: when the remote end has closed the connection, it will keep returning 0
until you make another read
call to observe that the socket is closed.
In other implementations of InputStream
, available()
will always return 0 unless the stream is buffered, in which case it will simply tell you how much is left in the buffer. When the buffer is empty, the input stream implementation won't try to fetch any more data from the underlying resource until you call read()
.
Therefore I'd suggest at least narrowing down the receiver of your function to SocketInputStream
, but for full correctness you should use NIO code instead.
Finally, if you find that for your specific use case the available()
loop does work as expected and read()
never blocks, then you should drop withContext(IO)
because it is implies two costly context switches (to a background thread and back) and its purpose is only to run blocking code off the GUI thread.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With