4

I am working on a simple app that requires data from a URL call. Here are the relevant code parts

class NewsFeed : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_news_feed)

    lifecycleScope.launchWhenCreated {
        getNewsData()
    }


private suspend fun getNewsData() {
    withContext(Dispatchers.IO) {
        try {
            val jObj = Jsoup.connect(getString(R.string.news_feed)).get()
          //many non relevant lines
BitmapFactory.decodeByteArray(URL(img).readBytes(), 0, 0)

The IDE gives a warning on both .get() and URL() methods saying it is a "inappropriate thread-blocking method call"

However if I understand correctly I am in a Dispacher.IO coroutine scope and the job is being done on a different thread created for blocking tasks. App does not crush or block the UI even if I timeout the calls.

Except suppressing the warning, what am I doing wrong?

error86
  • 102
  • 1
  • 10
  • Coroutines are kind of odd in this matter, if you take a closer look to `withContext` the method will sum the new dispatcher to the previous context, so what you have there is `Main` plus `IO`, because is inside `launchWhenCreated` so from there will get the previous context. Not a solution but hope to give you some clue. – cutiko Oct 21 '20 at 14:14
  • Thanks for the question, I had the same question in head since long. As far as I remember, in AS 3.6, the warning used to go away when the call was wrapped in `Dispatchers.IO`. Furthermore, from my experience with the profiler, the actual methods are executed out off the `Dispatchers.Main`. You can verify it yourself for your case. – Siddharth Kamaria Oct 21 '20 at 14:26
  • @cutiko Changing the call to lifeCycle.lunch(Dispachers.IO) that I understand changes the main + IO context to just IO does not help the issue. – error86 Oct 22 '20 at 13:10
  • @SiddharthKamaria It does not block the main thread I can tell you that much, I did not understand how to verify this. – error86 Oct 22 '20 at 13:11
  • @error86 That's been my experience too and that's what I stated above. By out off I meant outside of the Main context, sorry for the misunderstanding :) – Siddharth Kamaria Oct 22 '20 at 14:23

2 Answers2

1

Your way of doing this is correct, this is simply an issue of the inspection tool.

Dispatchers.IO is designed for exactly these situations, as it allocates additional threads to keep up with concurrent blocking IO operations.

This has also been explained by one of the kotlin devs here: https://medium.com/@elizarov/blocking-threads-suspending-coroutines-d33e11bf4761

Specifically the paragraph "Blocking IO to suspending" suggests using

withContext(Dispatchers.IO){
   ...
}

For blocking IO, e.g. network calls or reading files

Adrian K
  • 3,942
  • 12
  • 15
-1

You are right, that the Job is being done on a different thread, but that Thread is managed by a Dispatcher that works with coroutines.

If you block a Thread in that dispatcher it means that the thread can no longer be used for coroutines while you're blocking.

You are supposed to use the suspending variants of those methods that can get suspended in order to prevent the busy waiting of threads.

If there is no suspending alternative you're better off by using regular Java thread pools or async calls such as CompletalbeFuture.supplyAsync as they are better suited for regular blocking code.

Adam Arold
  • 29,285
  • 22
  • 112
  • 207
  • I did not follow the last part, threads created on the Dispacher.IO are meant to deal with IO tasks and as such they are supposed to be blocked. Please correct me if I am wrong the Dispacher IO is a thread pool meant for IO tasks, right? – error86 Oct 22 '20 at 13:14
  • It is, but not for non-suspending IO! – Adam Arold Oct 22 '20 at 13:30
  • just a little note here: IO is not a busy wait. Just because the thread is blocked, doesn't mean it's consuming CPU resources – Adrian K Oct 22 '20 at 17:10
  • I don´t think you need to use completeable future at all. Dispatchers.IO has 64 threads available. If they expected you to run everything that blocks in suspending functions it would be nonsence to increase the thread count anything above what Dispatchers.Main has. It has more Threads available, because it is expected some IO will be done in a blocking manner. If you still don´t have enough you can make another Dispatcher with a bigger Threadpool. But since we seem to be on Android I believe you probably should think about why it is your app needs to block >64 threads in the first place. – Max Oct 22 '20 at 19:02
  • That's besides the point. You are only supposed to use any `Dispatcher` for computation that can **suspend**. If it can't it will block other **suspending** functions from progressing. This is the reason you're getting a warning, because you are blocking instead of suspending. – Adam Arold Oct 23 '20 at 09:49
  • @AdamArold DoThank you, but do you know what is the correct way to write this code on a blocking function or executing the task on a thread that won't be blocked? I mean what the the best practice to execute web calls without AsyncTasks... – error86 Oct 25 '20 at 12:32
  • if it is a blocking call I'd use `CompletableFuture` or something similar. If it is non-blocking I'd use `Dispatchers.IO`. – Adam Arold Oct 25 '20 at 13:55
  • No, this answer is 100% wrong. The whole idea of Dispatchers.IO is to *block* on IO functions. A very important point of coroutines is being a concurrent API suited to completely replace direct use of threads, thread pools, etc. – niqueco Dec 24 '20 at 22:03
  • I can see that you don't understand the concept of coroutines. Having a bunch of `Thread`s in `Dispatchers.IO` just to block them and ignore the advantages of having coroutines just defeats the purpose of coroutines. – Adam Arold Dec 25 '20 at 09:32
  • You propose not using coroutines at all if you have to invoke a blocking call and use plain old threads directly. That's ignoring the advantages of having the coroutines API to handle concurrency. It's true that coroutines are great when you don't need to block, but often blocking is unavoidable. Read this (by Roman Elizarov!) that was posted in the right answer: https://elizarov.medium.com/blocking-threads-suspending-coroutines-d33e11bf4761 – niqueco Jan 02 '21 at 00:35
  • That's exactly what I said. – Adam Arold Jan 02 '21 at 00:38