【问题标题】:Using nested CoroutineScopes to upload images and keeping track of them使用嵌套的 CoroutineScopes 上传图像并跟踪它们
【发布时间】:2021-12-29 20:36:57
【问题描述】:

我是 android 协程的新手,我的要求

  1. 需要上传20张图片
  2. 跟踪上传(至少完成后我需要隐藏每张图片的进度条)
  3. 上传所有图片后还需要启用“下一步”按钮 这是我的尝试:

private fun startUploading(){
    // Get AWS data
    val accessKey = sharedPreferences.getString(getString(R.string.aws_access_key), "").toString()
    val secretKey = sharedPreferences.getString(getString(R.string.aws_secret_key), "").toString()
    val bucketName = sharedPreferences.getString(getString(R.string.aws_bucket_name), "").toString()
    val region = sharedPreferences.getString(getString(R.string.aws_region), "").toString()
    val distributionUrl = sharedPreferences.getString(getString(R.string.aws_distribution_url), "").toString()

    var totalImagesNeedToUpload = 0
    var totalImagesUploaded = 0
    CoroutineScope(Dispatchers.IO).launch {
        for (i in allCapturedImages.indices) {
            val allImageFiles = allCapturedImages[i].viewItem.ImageFiles
            totalImagesNeedToUpload += allImageFiles.size
            for (j in allImageFiles.indices) {
                CoroutineScope(Dispatchers.IO).launch {
                    while (true) {
                        val internetActive = utilsClassInstance.hasInternetConnected()
                        if (internetActive){
                            try {
                                val file = allImageFiles[j]
                                if (!file.uploaded) {
                                    // Upload the file
                                    val cfUrl = utilsClassInstance.uploadFile(file.imageFile, accessKey, secretKey, bucketName, region, distributionUrl)

                                    // Set the uploaded status to true
                                    file.uploaded = true
                                    file.uploadedUrl = cfUrl

                                    // Increment the count of total uploaded images
                                    totalImagesUploaded += 1

                                    // Upload is done for that particular set image
                                    CoroutineScope(Dispatchers.Main).launch {
                                        mainRecyclerAdapter?.uploadCompleteForViewItemImage(i, j, cfUrl)
                                        // Set the next button enabled
                                        if (totalImagesUploaded == totalImagesNeedToUpload){
                                            binding.btnNext.isEnabled = true
                                        }
                                    }
                                    break
                                }else{
                                    totalImagesUploaded += 1
                                    break
                                }
                            } catch (e: Exception) {
                                println(e.printStackTrace())
                            }
                        }
                    }
                    CoroutineScope(Dispatchers.Main).launch {
                        if (totalImagesUploaded == totalImagesNeedToUpload){
                            updateProgressForAllImages()
                            binding.btnNext.isEnabled = true
                        }
                    }
                }
            }
        }
    }
}

    fun uploadFile(file: File, accessKey:String, secretKey:String, bucketName: String, region:String, distributionUrl: String): String{
    // Create a S3 client
    val s3Client = AmazonS3Client(BasicAWSCredentials(accessKey, secretKey))
    s3Client.setRegion(Region.getRegion(region))

    // Create a put object
    val por = PutObjectRequest(bucketName, file.name, file)
    s3Client.putObject(por)

    // Override the response headers
    val override = ResponseHeaderOverrides()
    override.contentType = "image/jpeg"

    // Generate the url request
    val urlRequest = GeneratePresignedUrlRequest(bucketName, file.name)
    urlRequest.responseHeaders = override

    // Get the generated url
    val url = s3Client.generatePresignedUrl(urlRequest)
    return url.toString().replace("https://${bucketName}.s3.amazonaws.com/", distributionUrl)
}        
  1. 我需要上传总共“n”张图片
  2. 每张图片都以不同的协程上传,因为我需要进行并行上传

整个问题是如何知道所有图像都已上传并启用下一步按钮?

【问题讨论】:

  • 请指出您面临的问题。你期望什么结果,什么不起作用?代码是否会引发错误或其他问题?请提出问题。
  • SO 是一个问题和回答网站。您发布了您的要求和一些代码,但没有问题。看到这里再试一次:stackoverflow.com/help/how-to-ask
  • 你的代码让我想起了我继承的一个没有交接的项目..都让我哭了。
  • @RickyMo 请再检查一遍

标签: android kotlin kotlin-coroutines


【解决方案1】:

您的代码似乎非常不结构化。您有一个无限循环检查网络可用性。您在这里有一个嵌套循环来上传图像(为什么?)。您正在创建很多协程范围并且无法控制它们

根据您在问题中提到的 3 个要求,您可以执行以下操作:

val imagesToUpload: List<File> = /* ... */
var filesUploaded = 0
lifecycleScope.launchWhenStarted { 
    coroutineScope { // This will return only when all child coroutines have finished
        imagesToUpload.forEach { imageFile ->
            launch { // Run every upload in parallel
                val url = utilsClassInstance.uploadFile(file.imageFile, ...) // Assuming this is a non-blocking suspend function.
                filesUploaded++
                // Pass the `url` to your adapter to display the image
                binding.progressBar.progress = (filesUploaded * 100) / imagesToUpload.size // Update progress bar
            }
        }
    }
    // All images have been uploaded at this point.
    binding.btnNext.enabled = true
}

理想情况下,您应该使用viewModelScope,并且上传代码应该在存储库中,但是由于您似乎没有适当的架构,所以我使用了lifecycleScope,您可以将其放入@ 987654325@或Fragment

【讨论】:

  • filesUploaded++ 在 Dipatchers.IO 上以并行协同程序运行。我不认为这是线程安全的。由于您假设 uploadFile 挂起,因此无需指定 IO,因此您可以让它在 Main 上运行,而不必担心并发递增它。
  • 是的,我注意到了。你能为此提出一个简单的解决方案吗?我不想在答案中添加互斥锁并使其复杂化。
  • 我编辑了我的评论,以便在您回复时提出解决方案。 Dispatchers.Main 是单线程的,所以如果你在 Main 上运行启动的协程就可以了。我假设uploadFiles 暂停并且不阻塞。如果它阻塞,你可以将它包装在 withContext 中。
  • 范围取决于用例。如果我们想在用户退出时取消,ViewModel 范围是合适的。
  • @SanketBhangale 您的 uploadFile 函数不是暂停函数。而且您也没有上传完成的回调。我没有使用过 AWS,所以不知道它的 API,但我相信它为您提供了一些上传完成的回调。您首先需要在代码中引入该回调。然后您可以使用suspendCancellableCoroutine 将该基于回调的函数转换为挂起函数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-04
  • 2013-05-30
  • 1970-01-01
  • 2017-04-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多