3 android kotlin kotlin-coroutines
我需要下载多个图像,并在所有下载完成后(在主线程之外),在活动中执行其他操作。
我目前使用Glide下载如下:
图片下载器.kt
class ImageDownloader {
fun downloadPack(context: Context, path: String, pack: PackModel) {
for (image: ImageModel in pack.images) {
Glide.with(context)
.asBitmap()
.load(image.imageFileUrl)
.listener(object : RequestListener<Bitmap> {
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Bitmap>?, isFirstResource: Boolean): Boolean {
return false
}
override fun onResourceReady(bitmap: Bitmap?, model: Any?, target: Target<Bitmap>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
saveImage(path, pack.id, image.imageFileName, bitmap!!)
return true
}
}).submit()
}
}
private fun saveImage(path: String, id: String, fileName: String, bitmap: Bitmap) {
val dir = File(path + id)
if (!dir.exists()) dir.mkdirs()
val file = File(dir, fileName)
val out = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.PNG, 75, out)
out.flush()
out.close()
// to check the download progress for each image in logcat
println("done: $fileName")
}
}
Run Code Online (Sandbox Code Playgroud)
在活动中,我在 CoroutineScope 内调用此方法,如下所示:
PackActivity.kt
class PackActivity: AppCompatActivity() {
private lateinit var bind: ActivityPackBinding
private lateinit var path: String
private lateinit var pack: PackModel
// other basic codes
override fun onCreate(savedInstanceState: Bundle?) {
// other basic codes
path = "$filesDir/images_asset/"
pack = intent.getParcelableExtra(PACK_DATA)!!
bind.buttonDownload.setOnClickListener {
downloadPack()
}
}
private fun downloadPack() {
CoroutineScope(Dispatchers.IO).launch {
val async = async {
ImageDownloader().downloadPack(applicationContext, path, pack)
}
val result = async.await()
withContext(Dispatchers.Main) {
result.apply {
println("finished")
// other things todo
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
下载PackActivity.kt中的所有图像后,我尝试继续执行其他操作,但结果是,使用 println("finished") 并检查 logcat,代码甚至在第一次下载开始之前就开始了...
一些信息:
PackModel 和 ImageModel 是我的数据类,其中 PackModel 具有每个包的 Id 和 ImageModel 列表,而 ImageModel 列表又具有 ImageFileName 和 ImageFileUrl。所有数据均通过网络请求获取。
我希望将图像保存在文件夹 data/data/ AppPackageName /files/images_asset/ PackID /... 中,通过我所做的测试,我无法使用 DownloadManager 将图像定向到应用程序的此内部文件夹,即为什么我使用 Glide。
第一个关键步骤是将异步 Glide 请求调整为suspend funusing suspendCancellableCoroutine. 就是这样:
private suspend fun downloadBitmap(
context: Context,
image: ImageModel
) = suspendCancellableCoroutine<Bitmap> { cont ->
Glide.with(context)
.asBitmap()
.load(image.imageFileUrl)
.listener(object : RequestListener<Bitmap> {
override fun onLoadFailed(
e: GlideException, model: Any?,
target: Target<Bitmap>?, isFirstResource: Boolean
): Boolean {
cont.resumeWith(Result.failure(e))
return false
}
override fun onResourceReady(
bitmap: Bitmap, model: Any?, target: Target<Bitmap>?,
dataSource: DataSource?, isFirstResource: Boolean
): Boolean {
cont.resumeWith(Result.success(bitmap))
return false
}
}).submit()
}
Run Code Online (Sandbox Code Playgroud)
完成后,现在您将可以轻松地进行并发下载并等待所有下载:
class ImageDownloader {
suspend fun downloadPack(context: Context, path: String, pack: PackModel) {
coroutineScope {
for (image: ImageModel in pack.images) {
launch {
val bitmap = downloadBitmap(context, image)
saveImage(path, pack.id, image.imageFileName, bitmap)
}
}
}
}
private suspend fun saveImage(
path: String, id: String, imageFileName: String, bitmap: Bitmap
) {
withContext(IO) {
// your code
}
}
}
Run Code Online (Sandbox Code Playgroud)
仔细观察我上面使用的调度程序:Main对除 之外的所有内容都使用调度程序saveImage,这是唯一包含实际阻塞代码的地方。
最后,要使用所有内容,这就是您所需要的:
private fun downloadPack() {
GlobalScope.launch {
ImageDownloader().downloadPack(applicationContext, path, pack)
println("finished")
// other things todo
}
}
Run Code Online (Sandbox Code Playgroud)
同样,Main调度程序上的所有内容,因为阻塞代码都被安全地收集到IO调度程序中。
我使用GlobalScope上面的内容是因为缺乏对您的更大背景的了解,但这可能是一个坏主意。写作CoroutineScope(IO).launch也有同样的问题,而且还要分配更多的对象。
请三思,如果用户离开应用程序,或者反复导航离开并返回,从而触发越来越多的后台下载,您的下载将会发生什么情况。在上面的代码中,我没有在内部处理取消,suspendCancellableCoroutine因为我对 Glide 不太熟悉。您应该添加一个cont.onCancellation处理程序才能正确。
| 归档时间: |
|
| 查看次数: |
2292 次 |
| 最近记录: |