如何解决使用 Retrofit 进行队列下载
我正在尝试为我的 Android 应用创建一个队列管理器。
在我的应用中,我在 RecyclerView 中显示视频列表。当用户点击任何视频时,我会在设备上下载视频。下载本身运行良好,我什至可以同时下载多个视频并显示每次下载的下载进度。
问题: 我只想同时下载 3 个视频,并将所有其他下载放在队列中。
这是我的 Retrofit 服务生成器类:
object RetrofitInstance {
private val downloadRetrofit by lazy {
val dispatcher = dispatcher()
dispatcher.maxRequestsPerHost = 1
dispatcher.maxRequests = 3
val client = OkHttpClient
.Builder()
.dispatcher(dispatcher)
.build()
Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
val downloadApi: Endpoints by lazy {
downloadRetrofit.create(Endpoints::class.java)
}
}
这是我的端点接口类:
interface Endpoints {
@GET
@Streaming
suspend fun downloadFile(@Url fileURL: String): Response<ResponseBody>
}
我使用 Kotlin 协程开始下载:
suspend fun startDownload(url: String,filePath: String) {
val downloadService = RetrofitInstance.downloadApi.downloadFile(url)
if (downloadService.isSuccessful) {
saveFile(downloadService.body(),filePath)
} else {
// callback for error
}
}
我还尝试通过使用 dispatcher(Executors.newFixedThreadPool(1))
来减少 Retrofit 可以使用的线程数,但这也无济于事。它仍然会同时下载所有文件。
任何帮助将不胜感激。谢谢!
编辑
忘了提一件事。我正在为 recyclerView 项目使用自定义视图。这些自定义视图通过直接调用 Download 类来管理自己的下载状态。
解决方法
您可以使用 CoroutineWorker 在后台线程下载视频并处理下载队列。
- 创建工作线程
class DownloadVideoWorker(
private val context: Context,private val params: WorkerParameters,private val downloadApi: DownloadApi
) : CoroutineWorker(context,params) {
override suspend fun doWork(): Result {
val videos = inputData.getStringArray(VIDEOS)
//Download videos
return success()
}
companion object {
const val VIDEOS: String = "VIDEOS"
fun enqueue(videos: Array<String>): LiveData<WorkInfo> {
val downloadWorker = OneTimeWorkRequestBuilder<DownloadVideoWorker>()
.setInputData(Data.Builder().putStringArray(VIDEOS,videos).build())
.build()
val workManager = WorkManager.getInstance()
workManager.enqueue(downloadWorker)
return workManager.getWorkInfoByIdLiveData(downloadWorker.id)
}
}
}
- 在您的 viewModel 添加函数以从您的 Fragment/Activity 调用 worker
class DownloadViewModel() : ViewModel() {
private var listOfVideos: Array<String> // Videos urls
fun downloadVideos(): LiveData<WorkInfo> {
val videosToDownload = retrieveNextThreeVideos()
return DownloadVideoWorker.enqueue(videos)
}
fun retrieveNextThreeVideos(): Array<String> {
if(listOfVideos.size >= 3) {
val videosToDownload = listOfVideos.subList(0,3)
videosToDownload.forEach { listOfVideos.remove(it) }
return videosToDownload
}
return listOfVideos
}
}
- 观察 LiveData 并处理工作结果
fun downloadVideos() {
documentsViewModel.downloadVideos().observe(this,Observer {
when (it.state) {
WorkInfo.State.SUCCEEDED -> {
downloadVideos()
}
WorkInfo.State.FAILED -> {
// Handle error result
}
}
})
}
注意:要了解有关 Coroutine Worker 的更多信息,请参阅:https://developer.android.com/topic/libraries/architecture/workmanager/advanced/coroutineworker
,我终于能够实现它,但我仍然不确定这是否是最有效的方法。我使用了 ThreadPool 的单例变量。这是我所做的:
在我的下载类中,我创建了一个 ThreadPoolExecutor 的伴随对象:
companion object {
private val executor: ThreadPoolExecutor = Executors.newFixedThreadPool(3) as ThreadPoolExecutor
}
然后我在 startDownload 函数中进行了以下更改:
fun startDownloading(url: String,filePath: String) {
downloadUtilImp.downloadQueued()
runBlocking {
downloadJob = launch(executor.asCoroutineDispatcher()) {
val downloadService = RetrofitInstance.api.downloadFile(url)
if (downloadService.isSuccessful) saveFile(downloadService.body(),filePath)
else downloadUtilImp.downloadFailed(downloadService.errorBody().toString())
}
}
}
此代码一次仅下载 3 个视频,并将所有其他下载请求排队。
如果有更好的方法,我仍然愿意接受建议。感谢您的帮助!
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。