下载队列在Android中可以说是非常常见了,常见的做法是将下载任务放到服务中,创建线程任务队列,对其进行处理,这样子问题也有很多,比如常见的线程安全之类的。并且考虑到线程创建和关闭对资源的消耗,我们还要维护一个线程池。同时实现动态像队列中添加任务,采用生产者-消费者模式。非常复杂。
今天采用kotlin来实现一个可以随时添加下载任务的下载队列。
知识点
-
协程
-
通道
-
Android service
如果对于协程和通道不明白的可以参考kotlin官方文档
思路
首先创建一个Android Service,onBind返回null,因为不需要绑定任何activity。重写onCreate函数,onStartCommand函数。
class DownloadService : Service(), CoroutineScope by MainScope() {
override fun onBind(intent: Intent): IBinder? {
return null
}
override fun onCreate() {
super.onCreate()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
}
}
简单说明onCreate和onStartCommand函数:
- onCreate : 在服务首次开启时调用一次,之后不再调用
- onStartCommand :每次调用startService()都会调用一次
这样就非常清晰了,在onCreate方法中实现下载队列,在onStartCommand 方法中添加下载队列。
/**
* 声明下载通道
*/
private val downLoadChannel = Channel<String>()
/**
* 添加下载任务
*/
private suspend fun addDownloadTask(down: String) {
count++
Log.d(TAG, "增加:$down,当前任务数:$count")
downLoadChannel.send(down)
}
我们只要在onStartCommand中调用添加下载任务就行了
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
launch {
c++
Log.d(TAG, "准备增加")
addDownloadTask("下载任务$c")
}
return super.onStartCommand(intent, flags, startId)
}
在onCreate中,我们需要创建一个在IO线程中的协程来处理下载任务。每当任务处理完毕之后调用销毁程序关闭任务,销毁程序需要先等待11s检查是否没有任务添加了,如果是在等待5s中确认真的没有任务添加之后,关闭通道和服务进程。
@ExperimentalCoroutinesApi
suspend fun processingDownloadTasks() {
while (!downLoadChannel.isEmpty) {
val down = downLoadChannel.receive()
Log.d(TAG, "正在下载:$down")
// 模拟下载
delay(1000)
count--
}
waitingToStop()
}
@ExperimentalCoroutinesApi
suspend fun waitingToStop() {
Log.d(TAG, "等待...${downLoadChannel.isEmpty}")
delay(11000)
Log.d(TAG, "每11s检查是否有下载任务,当前任务:${downLoadChannel.isEmpty}")
if (downLoadChannel.isEmpty) {
Log.d(TAG, "没有下载任务,最后5s到底有没有,当前任务:${downLoadChannel.isEmpty}")
delay(5000)
Log.d(TAG, "延迟,当前任务:${downLoadChannel.isEmpty}")
if (downLoadChannel.isEmpty) {
Log.d(TAG, "真的没有下载任务,我要关闭了")
downLoadChannel.cancel()
// 停止
stopSelf()
return
}
}
processingDownloadTasks()
}
override fun onBind(intent: Intent): IBinder? {
return null
}
@ExperimentalCoroutinesApi
override fun onCreate() {
super.onCreate()
Log.d(TAG, "启动服务")
launch(Dispatchers.IO) {
processingDownloadTasks()
}
}
在activity中随时都可以调用startService来开启下载进程。
startService<DownloadService>()
运行效果
启动服务
等待...true
准备增加
增加:下载任务1,当前任务数:1
准备增加
增加:下载任务2,当前任务数:2
准备增加
增加:下载任务3,当前任务数:3
准备增加
增加:下载任务4,当前任务数:4
每11s检查是否有下载任务,当前任务:false
正在下载:下载任务1
准备增加
增加:下载任务5,当前任务数:5
正在下载:下载任务2
正在下载:下载任务3
正在下载:下载任务4
正在下载:下载任务5
等待...true
准备增加
增加:下载任务6,当前任务数:1
每11s检查是否有下载任务,当前任务:false
正在下载:下载任务6
等待...true
每11s检查是否有下载任务,当前任务:true
没有下载任务,最后5s到底有没有,当前任务:true
延迟,当前任务:true
真的没有下载任务,我要关闭了
关闭服务