Home > Net >  Not able to get the data out of a Coroutine block android
Not able to get the data out of a Coroutine block android

Time:11-23

Repository implementation where a suspend function of uploading and getting back the urls. the await() function said that it should be "suspend". so i added a Coroutine Scope block . inside the block have added urls to a list. the log statement inside the scope has required downloaded urls in the arraylist . but outside the block the list is empty. how to make the list updated outside the coroutine block and can anyone help me to understand how to work with Coroutine thanks

my code

override suspend fun addProductImagesToFirebaseStorage(
    productImages: List<Uri>
): AddProductImagesResponse {

    return try {
        val downloadUrls = mutableListOf<Uri>()

        val tasks = mutableListOf<UploadTask>()
        productImages.forEach { downloadUrl ->
            val task = categoryImageStorage.reference
                .child("Home").child("PP")
                .child("images")
                .putFile(downloadUrl)
            tasks.add(task)
        }

        Tasks.whenAllSuccess<UploadTask.TaskSnapshot>(tasks).addOnSuccessListener { uploadTask ->
            uploadTask.forEach {
         // downloadUrls.add(it.storage.downloadUrl.await()) Error: Suspension functions can be called only within coroutine body
                CoroutineScope(Dispatchers.Default).launch {
                    downloadUrls.add(it.storage.downloadUrl.await())
                    Log.i(TAG,"Inside the block : $downloadUrls")
                }
                Log.i(TAG,"Outside the block : $downloadUrls")
            }

        }

        Success(downloadUrls)

    } catch(e:Exception) {
        Failure(e)
    }
}

CodePudding user response:

That's because the outside log will be probably executed first, since changing the thread to Dispatchers.DEFAULT takes more time than it takes JVM to execute the next line.

What I'm trying to say is, the coroutine will start, immediately after that, the outside log will print the list(which is empty), then the coroutine job will do it's work on another thread (Dispatchers.DEFAULT), independently of the main program execution flow.

What I would suggest, is to use the MutableLiveData or LiveData so you can observe them to let the main program react when the list gets filled with downloadUrl

Here's more to LiveData and MutableLiveData

CodePudding user response:

As was stated by ndriqa, outside logging happens before the inside logging, since your coroutine will run in parallel and wait for download to complete before logging. During download, your code written outside of coroutine block continues to execute and your downloadUrls remains empty until download completes. Therefore you get empty value in outside log.

Your goal is to make sure that your method addProductImagesToFirebaseStorage return Success() with downloaded value after download completes. Since your method is suspend, it is able to wait for some long operation to complete (in your case download.await()). However, you are calling await() in the listener's method, therefore compiler does not know the context and cannot be sure that it is called from a coroutine or a suspend function.

Just call it like this to ensure that Success will be created with up to date value

val tasks = mutableListOf<UploadTask>()
    productImages.forEach { file -> // I renamed downloadUrl to file here to be clearer
        val url = categoryImageStorage.reference // I also renamed task to url since the result will be not a task but a downloaded url
            .child("Home").child("PP")
            .child("images")
            .putFile(file)
            .await() // wait for it to be put
            .storage
            .downloadUrl
            .await() // wait for it to download
        downloadUrls.add(url)
    }
  • Related