我正在尝试为我的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
}
}
我还尝试减少线程的数量Retrofit可以使用Dispatcher(Executors.newFixedThreadPool(1))
,但这没有帮助,以及。它仍然同时下载所有文件。
任何帮助都会很感激。谢谢!
编辑
忘了提一件事。我正在为recyclerView项目使用自定义视图。这些自定义视图通过直接调用Download类来管理它们自己的下载状态。
你可以使用CoroutineWorker在后台线程中下载视频,并处理下载队列。
- 创建worker
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并处理worker结果
fun downloadVideos() {
documentsViewModel.downloadVideos().observe(this, Observer {
when (it.state) {
WorkInfo.State.SUCCEEDED -> {
downloadVideos()
}
WorkInfo.State.FAILED -> {
// Handle error result
}
}
})
}
注意:要了解更多关于协程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个视频,并将所有其他下载请求排队。
如果有更好的方法,我仍然愿意听取建议。谢谢你的帮助!