Fix tile loading lock

This commit is contained in:
Alexander Nozik 2022-07-16 21:50:47 +03:00
parent 9f6386a8c2
commit f49ff34a18
No known key found for this signature in database
GPG Key ID: F7FCF2DD25C71357
3 changed files with 32 additions and 21 deletions

View File

@ -17,6 +17,7 @@ import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.input.pointer.* import androidx.compose.ui.input.pointer.*
import androidx.compose.ui.unit.* import androidx.compose.ui.unit.*
import centre.sciprog.maps.* import centre.sciprog.maps.*
import io.ktor.utils.io.CancellationException
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import mu.KotlinLogging import mu.KotlinLogging
import org.jetbrains.skia.Font import org.jetbrains.skia.Font
@ -185,6 +186,7 @@ actual fun MapView(
try { try {
mapTiles += deferred.await() mapTiles += deferred.await()
} catch (ex: Exception) { } catch (ex: Exception) {
if (ex !is CancellationException) {
//displaying the error is maps responsibility //displaying the error is maps responsibility
logger.error(ex) { "Failed to load tile with id=$id" } logger.error(ex) { "Failed to load tile with id=$id" }
} }
@ -193,6 +195,7 @@ actual fun MapView(
} }
} }
} }
}
Canvas(canvasModifier) { Canvas(canvasModifier) {

View File

@ -5,6 +5,7 @@ import androidx.compose.ui.graphics.toComposeImageBitmap
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.request.get import io.ktor.client.request.get
import io.ktor.client.statement.readBytes import io.ktor.client.statement.readBytes
import io.ktor.utils.io.CancellationException
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -37,6 +38,7 @@ class OpenStreetMapTileProvider(
* Download and cache the tile image * Download and cache the tile image
*/ */
private fun CoroutineScope.downloadImageAsync(id: TileId) = async(Dispatchers.IO) { private fun CoroutineScope.downloadImageAsync(id: TileId) = async(Dispatchers.IO) {
id.cacheFilePath()?.let { path -> id.cacheFilePath()?.let { path ->
if (path.exists()) { if (path.exists()) {
try { try {
@ -48,7 +50,6 @@ class OpenStreetMapTileProvider(
} }
} }
try {
//semaphore works only for actual download //semaphore works only for actual download
semaphore.withPermit { semaphore.withPermit {
val url = id.osmUrl() val url = id.osmUrl()
@ -65,23 +66,30 @@ class OpenStreetMapTileProvider(
Image.makeFromEncoded(byteArray).toComposeImageBitmap() Image.makeFromEncoded(byteArray).toComposeImageBitmap()
} }
} catch (ex: Exception){
//if loading is failed for some reason, clear the cache
cache.remove(id)
throw ex
}
} }
override fun CoroutineScope.loadTileAsync( override fun CoroutineScope.loadTileAsync(
tileId: TileId, tileId: TileId,
): Deferred<MapTile> { ): Deferred<MapTile> {
//start image download //start image download
val image = cache.getOrPut(tileId) { val imageDeferred = cache.getOrPut(tileId) {
downloadImageAsync(tileId) downloadImageAsync(tileId)
} }
//collect the result asynchronously //collect the result asynchronously
return async { MapTile(tileId, image.await()) } return async {
val image = try {
imageDeferred.await()
} catch (ex: Exception) {
cache.remove(tileId)
if(ex !is CancellationException) {
logger.error(ex) { "Failed to load tile image with id=$tileId" }
}
throw ex
}
MapTile(tileId, image)
}
} }