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.unit.*
import centre.sciprog.maps.*
import io.ktor.utils.io.CancellationException
import kotlinx.coroutines.launch
import mu.KotlinLogging
import org.jetbrains.skia.Font
@ -185,8 +186,10 @@ actual fun MapView(
try {
mapTiles += deferred.await()
} catch (ex: Exception) {
//displaying the error is maps responsibility
logger.error(ex) { "Failed to load tile with id=$id" }
if (ex !is CancellationException) {
//displaying the error is maps responsibility
logger.error(ex) { "Failed to load tile with id=$id" }
}
}
}
}

View File

@ -5,6 +5,7 @@ import androidx.compose.ui.graphics.toComposeImageBitmap
import io.ktor.client.HttpClient
import io.ktor.client.request.get
import io.ktor.client.statement.readBytes
import io.ktor.utils.io.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
@ -37,6 +38,7 @@ class OpenStreetMapTileProvider(
* Download and cache the tile image
*/
private fun CoroutineScope.downloadImageAsync(id: TileId) = async(Dispatchers.IO) {
id.cacheFilePath()?.let { path ->
if (path.exists()) {
try {
@ -48,40 +50,46 @@ class OpenStreetMapTileProvider(
}
}
try {
//semaphore works only for actual download
semaphore.withPermit {
val url = id.osmUrl()
val byteArray = client.get(url).readBytes()
//semaphore works only for actual download
semaphore.withPermit {
val url = id.osmUrl()
val byteArray = client.get(url).readBytes()
logger.debug { "Finished downloading map tile with id $id from $url" }
logger.debug { "Finished downloading map tile with id $id from $url" }
id.cacheFilePath()?.let { path ->
logger.debug { "Caching map tile $id to $path" }
id.cacheFilePath()?.let { path ->
logger.debug { "Caching map tile $id to $path" }
path.parent.createDirectories()
path.writeBytes(byteArray)
}
Image.makeFromEncoded(byteArray).toComposeImageBitmap()
path.parent.createDirectories()
path.writeBytes(byteArray)
}
} catch (ex: Exception){
//if loading is failed for some reason, clear the cache
cache.remove(id)
throw ex
Image.makeFromEncoded(byteArray).toComposeImageBitmap()
}
}
override fun CoroutineScope.loadTileAsync(
tileId: TileId,
): Deferred<MapTile> {
//start image download
val image = cache.getOrPut(tileId) {
val imageDeferred = cache.getOrPut(tileId) {
downloadImageAsync(tileId)
}
//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)
}
}