使用嵌套的 CoroutineScopes 上传图片并跟踪它们
Using nested CoroutineScopes to upload images and keeping track of them
我是 android 协程的新手我的要求
- 需要上传20张图片
- 跟踪上传(至少当它完成时我需要隐藏每张图片的进度条)
- 上传所有图片后还需要启用“下一步”按钮
这是我的尝试:
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)
}
- 我总共需要上传“n”张图片
- 每张图片都在不同的协程中上传,因为我需要并行上传
整个问题是如何知道所有图像都已上传并启用下一步按钮?
您的代码看起来很松散。您有一个无限循环检查网络可用性。您在此处有一个嵌套循环来上传图像(为什么?)。您正在创建大量协程作用域并且无法控制它们
根据你在问题中提到的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
你可以进入 Activity
或 Fragment
我是 android 协程的新手我的要求
- 需要上传20张图片
- 跟踪上传(至少当它完成时我需要隐藏每张图片的进度条)
- 上传所有图片后还需要启用“下一步”按钮 这是我的尝试:
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)
}
- 我总共需要上传“n”张图片
- 每张图片都在不同的协程中上传,因为我需要并行上传
整个问题是如何知道所有图像都已上传并启用下一步按钮?
您的代码看起来很松散。您有一个无限循环检查网络可用性。您在此处有一个嵌套循环来上传图像(为什么?)。您正在创建大量协程作用域并且无法控制它们
根据你在问题中提到的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
你可以进入 Activity
或 Fragment