我们可以在 Android Kotlin 的单个协程中下载多个文件吗

Posted

技术标签:

【中文标题】我们可以在 Android Kotlin 的单个协程中下载多个文件吗【英文标题】:Can we download multiple files in a Single Coroutine in Android Kotlin 【发布时间】:2021-04-07 13:46:24 【问题描述】:

您好所有开发者,请尽可能多地整理我的查询。

我正在开发相册应用程序。在这里,当用户单击特定相册时,我们应该将所有相关图像下载到该特定相册。

例如专辑名称是树,所以专辑有多个图像 url 的数组。所以我应该通过 url 的数组下载该专辑中的所有图片

例如:imageArray = arrayOf("url1","url2","url3","url4",....等 url(n))

我应该将它们放入 for 循环或递归中,然后我应该在完成时将它们下载到 (n) 个 url。

我在这里为一个文件下载编写了 sn-p 我的疑问是如何继续下载多个文件。

我应该对所有文件下载使用相同的协程还是一个一个文件一个一个协程

CoroutineScope(Dispatchers.IO).launch 

**//here itself i can run for loop or else any other robust/proper way to do this requirement.**

                ktor.downloadFile(outputStream, url).collect 

                    withContext(Dispatchers.Main) 
                        when (it) 
                            is DownloadResult.Success -> 
**//on success of one file download should i call recursively to download one more file by this method - private fun downloadFile(context: Context, url: String, file: Uri)**
                                viewFile(file)
                            

下面是下载单个文件的代码


    private fun downloadFile(context: Context, url: String, file: Uri) 

        val ktor = HttpClient(android)
        contentResolver.openOutputStream(file)?.let  outputStream ->

            CoroutineScope(Dispatchers.IO).launch 

                ktor.downloadFile(outputStream, url).collect 

                    withContext(Dispatchers.Main) 
                        when (it) 
                            is DownloadResult.Success -> 
                                viewFile(file)
                            

                            is DownloadResult.Error -> 
                            

                            is DownloadResult.Progress -> 
                                txtProgress.text = "$it.progress"
                            

                        
                    
                

            
        

    


suspend fun HttpClient.downloadFile(file: OutputStream, url: String): Flow<DownloadResult> 
    return flow 
        try 
            val response = call 
                url(url)
                method = HttpMethod.Get
            .response

            val data = ByteArray(response.contentLength()!!.toInt())
            var offset = 0

            do 
                val currentRead = response.content.readAvailable(data, offset, data.size)
                offset += currentRead
                val progress = (offset * 100f / data.size).roundToInt()
                emit(DownloadResult.Progress(progress))
             while (currentRead > 0)

            response.close()

            if (response.status.isSuccess()) 
                withContext(Dispatchers.IO) 
                    file.write(data)
                
                emit(DownloadResult.Success)
             else 
                emit(DownloadResult.Error("File not downloaded"))
            
         catch (e: TimeoutCancellationException) 
            emit(DownloadResult.Error("Connection timed out", e))
         catch (t: Throwable) 
            emit(DownloadResult.Error("Failed to connect"))
        
    


sealed class DownloadResult 
    object Success : DownloadResult()

    data class Error(val message: String, val cause: Exception? = null) : DownloadResult()

    data class Progress(val progress: Int): DownloadResult()



我用过的 Gradle 文件

    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3'
    implementation "io.ktor:ktor-client-android:1.2.5"

【问题讨论】:

方法调用已弃用。并阻止响应,直到它被完全接收。您将无法获得实时进度 【参考方案1】:

应该是可以的。

    创建另一个函数,将遍历要下载的文件列表。 使用新函数,使用 asynclaunch 协程函数 - 这允许分别对逻辑流、顺序行为或异步行为进行更多控制。

例如fun downloadBatch(list: List&lt;Uri&gt;) GlobalScope.async(Dispatchers.IO) //logic goes here

注意:GlobalScope 只是一个简单的示例 - 不建议在现场制作中使用。

    在迭代器/for 循环中,调用一个函数来下载单个文件。这个特定的功能应该在开头附加 suspend 例如suspend fun downloadFile(uri: Uri)

注意:挂起的函数本身不会使用任何线程逻辑,它依赖于嵌套在函数式协程中。

    继续使用 rxJava 或尝试 LiveData 来广播您的文件。

【讨论】:

对批处理文件下载的一个非常精明的解释。 @OP 请尝试一次,如果您需要任何进一步的建议,请告诉我们 @zuko 我可以在这里使用 workmanager 而不是服务来连续下载多个文件,因为如果用户关闭应用程序,下载也应该运行并完成我想使用前台服务,但任何可能使用 workmanager 概念,如我可以添加 100 个下载 url 到 workmanager 并让它下载请给我你的建议。 这真的取决于您对应用程序的需求,以及您希望用户拥有的体验类型。 developer.android.com/docs/quality-guidelines/… - 我认为这篇文章对离线优先架构提供了非常有见地的解释,并且应该为您提供足够的信息来开始。要考虑的另一件事是这些文件对应用程序有多重要?越重要,您希望它们被获取的速度就越快。

以上是关于我们可以在 Android Kotlin 的单个协程中下载多个文件吗的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin - 协程基础及原理

深潜Kotlin协程(十三):构建协程作用域

深潜Kotlin协程(十三):构建协程作用域

Android Kotlin回顾10.如何启动协程

Android - ViewModel、LiveData、Room 和 Retrofit 以及协程放在 kotlin 中

Android Kotlin协程coroutine