Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inappropriate blocking method call, but Suspend function 'withContext' should be called only from a coroutine or another suspend function

In my Service, I need to call on onStartCommand some methods that require withContext(Dispatchers.IO) instead CoroutineScope(Dispatchers.IO) like:

  • url = URL(pokemon.linkImage)
  • url.openConnection().getInputStream()
  • fOut= FileOutputStream(file)
  • fOut.flush()
  • fOut.close()

But Suspend function 'withContext' should be called only from a coroutine or another suspend function. So if onStartCommand can't be a suspend function because it has override and withContext can't be called by CoroutineScope because Inappropriate blocking method call of methods

val url = URL(pokemon.linkImage)
val iS = url.openConnection().getInputStream()

How can I resolve this problem?

My onStartCommand():

override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {

    //create the directory on internal storage that will contains all pokemon images
    val path = applicationContext.filesDir.absolutePath
    val dirName = "/pokeimg"
    val dir = File(path, dirName)
    if(!dir.exists())
        dir.mkdir()
    CoroutineScope(Dispatchers.IO).launch {
        //get the list of pokemon when they are loaded from repository
        val listOfPokemon =
            PersistenceSingletonRepository.getInstance(applicationContext).getListOfPokemon()
        //download all the image for each pokemon in the list, but only if the image is
        //not present in the directory
        for(pokemon in listOfPokemon) {
            val imageName = "${pokemon.name}.png"
            val file = File("${dir.absolutePath}/$imageName")
            //check if image not exists on internal storage and if is not an official-artwork
            //images that aren't official-artwork has less quality so don't resize
            if(!file.exists()) {
                //download img
                val url = URL(pokemon.linkImage)
                val iS = url.openConnection().getInputStream()
                val opts = BitmapFactory.Options()
                if (!Utils.isBadImage(pokemon.linkImage)) {
                    //request a smaller image
                    opts.inSampleSize = 2
                }
                val bitmap = BitmapFactory.decodeStream(iS, null, opts)
                val fOut= FileOutputStream(file)

                bitmap?.compress(Bitmap.CompressFormat.PNG, 100, fOut)
                fOut.flush()
                fOut.close()
            }

        }
        stopSelf()
    }
    return START_NOT_STICKY
}
like image 866
Simone Avatar asked Oct 23 '25 18:10

Simone


1 Answers

You can safely ignore that warning, it's known to have many false positives. You launched the coroutine in the IO dispatcher, which is designed for blocking calls.

On the other hand, launching anything with an ad-hoc CoroutineScope that has no parent and no lifecycle binding, is usually the wrong thing to do. You should respect the Service lifecycle and cancel your coroutine in onDestroy. See for example here.

like image 69
Marko Topolnik Avatar answered Oct 25 '25 08:10

Marko Topolnik