使用 Retrofit 队列下载
Queue download using Retrofit
我正在尝试为我的 Android 应用程序创建一个队列管理器。
在我的应用中,我在 RecyclerView 中显示了一个视频列表。当用户点击任何视频时,我将视频下载到设备上。下载本身运行良好,我什至可以同时下载多个视频并显示每个下载的下载进度。
问题:
我只想同时下载 3 个视频,并将所有其他视频放入队列中。
这是我的 Retrofit 服务生成器 class:
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)
}
}
这是我的端点接口 class:
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 class.
来管理自己的下载状态
您可以使用 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 并处理 worker 结果
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 的单例变量。这是我所做的:
在我的下载 class 中,我创建了 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 个视频,并对所有其他下载请求进行排队。
如果有更好的方法,我仍然愿意接受建议。感谢您的帮助!
我正在尝试为我的 Android 应用程序创建一个队列管理器。
在我的应用中,我在 RecyclerView 中显示了一个视频列表。当用户点击任何视频时,我将视频下载到设备上。下载本身运行良好,我什至可以同时下载多个视频并显示每个下载的下载进度。
问题: 我只想同时下载 3 个视频,并将所有其他视频放入队列中。
这是我的 Retrofit 服务生成器 class:
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)
}
}
这是我的端点接口 class:
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 class.
来管理自己的下载状态您可以使用 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 并处理 worker 结果
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 的单例变量。这是我所做的:
在我的下载 class 中,我创建了 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 个视频,并对所有其他下载请求进行排队。
如果有更好的方法,我仍然愿意接受建议。感谢您的帮助!