22

I have the following class:

class Repository(
    private val assetManager: AssetManager,
    private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) {
    suspend fun fetchHeritagesList(): HeritageResponse = withContext(ioDispatcher) {
        try {
            // TODO Blocking method call?
            val bufferReader = assetManager.open("heritages.json").bufferedReader()
...

and I'm wondering why do I get a warning in the open("heritages.json") saying Innapropriate blocking method call? isn't the withContext(ioDispatcher) fixing that?

Thanks for the explanation!

noloman
  • 11,411
  • 20
  • 82
  • 129

2 Answers2

30

IntelliJ inspection that looks for blocking calls inside suspendable functions isn't powerful enough to see through a level of indirection between Dispatchers.IO and its usage in withContext. Here's a minimal reproducer of the issue:

class IoTest {
    private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO

    suspend fun indirectRef() = withContext(ioDispatcher) {
        FileInputStream(File("dummy.txt")) // Flagged as inappropriate blocking call
    }

    suspend fun directRef() = withContext(Dispatchers.IO) {
        FileInputStream(File("dummy.txt")) // Not flagged
    }
}

However, unlike in my case, your ioDispatcher is exposed for injection through the constructor so you could just as easily supply Dispatchers.Main instead of it, and that would constitute inappropriate blocking.

Unfortunately I haven't yet heard of any way to formally specify the contract of a dispatcher as "tolerating blocking calls", so that you could enforce it in the constructor.

There is already a similar issue open on YouTrack.

EDIT: As of the build from March 16, 2022, there seems to be a regression that flags IO calls even inside a withContext(IO).

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • 7
    The second one actually is flagged: https://i.imgur.com/UdfcQ6y.png – android developer Jan 30 '21 at 20:49
  • 4
    I just showed you it is. Maybe you use a newer version of the IDE. I use "Android Studio 4.2 Beta 4 Build #AI-202.7660.26.42.7094744, built on January 21, 2021 Runtime version: 11.0.8+10-b944.6842174 amd64" – android developer Jan 30 '21 at 22:42
  • Both are flagged here as well, on Android Studio Chipmunk | 2021.2.1 Build #AI-212.5712.43.2112.8512546. – atrocia6 May 13 '22 at 13:04
  • See also: https://stackoverflow.com/questions/58680028/how-to-make-inappropriate-blocking-method-call-appropriate – atrocia6 May 13 '22 at 13:05
  • At first glance this looks like a regression. I'm running `Android Studio 2021.1.1 Patch 3 Build #AI-211.7628.21.2111.8309675 on March 16, 2022`, and now a statement like `FileInputStream(File("dummy.txt"))` is flagged even inside `withContext(Dispatchers.IO)` – Marko Topolnik May 14 '22 at 14:32
0

Code below can hang, because of blocking IO:

class MyService(assetManager: AssetManager) {
   private val repo = Repository(assetManager, newFixedThreadPoolContext(nThreads: 1, name: "SO QA"))

   suspend fun read(): HeritageResponse {
      return fetchHeritagesList(); // <-- please note, that we replaced IO dispatcher to single-threaded one
   }
}


Manushin Igor
  • 3,398
  • 1
  • 26
  • 40