Limited images concurrent loading to 3
This commit is contained in:
parent
bf6600878b
commit
7a3d68e3a6
15
src/main/kotlin/app/extensions/SemaphoreExtensions.kt
Normal file
15
src/main/kotlin/app/extensions/SemaphoreExtensions.kt
Normal file
@ -0,0 +1,15 @@
|
||||
package app.extensions
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import kotlinx.coroutines.sync.Semaphore
|
||||
|
||||
suspend inline fun Semaphore.acquireAndUse(
|
||||
block: () -> Composable
|
||||
) {
|
||||
this.acquire()
|
||||
try {
|
||||
block()
|
||||
} finally {
|
||||
this.release()
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package app.images
|
||||
|
||||
interface ImagesCache {
|
||||
fun getCachedObject(urlSource: String): ByteArray?
|
||||
fun getCachedImage(urlSource: String): ByteArray?
|
||||
fun cacheImage(urlSource: String, image: ByteArray)
|
||||
}
|
@ -3,7 +3,7 @@ package app.images
|
||||
object InMemoryImagesCache : ImagesCache {
|
||||
private val cachedImages = hashMapOf<String, ByteArray>()
|
||||
|
||||
override fun getCachedObject(urlSource: String): ByteArray? {
|
||||
override fun getCachedImage(urlSource: String): ByteArray? {
|
||||
return cachedImages[urlSource]
|
||||
}
|
||||
|
||||
|
75
src/main/kotlin/app/images/NetworkImageLoader.kt
Normal file
75
src/main/kotlin/app/images/NetworkImageLoader.kt
Normal file
@ -0,0 +1,75 @@
|
||||
package app.images
|
||||
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.toComposeImageBitmap
|
||||
import androidx.compose.ui.res.useResource
|
||||
import app.extensions.acquireAndUse
|
||||
import app.extensions.toByteArray
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.sync.Semaphore
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jetbrains.skia.Image
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
|
||||
private const val MAX_LOADING_IMAGES = 3
|
||||
|
||||
object NetworkImageLoader {
|
||||
private val loadingImagesSemaphore = Semaphore(MAX_LOADING_IMAGES)
|
||||
private val cache: ImagesCache = InMemoryImagesCache
|
||||
|
||||
suspend fun loadImageNetwork(url: String): ImageBitmap? = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val cachedImage = cache.getCachedImage(url)
|
||||
|
||||
if(cachedImage != null)
|
||||
return@withContext cachedImage.toComposeImage()
|
||||
|
||||
loadingImagesSemaphore.acquireAndUse {
|
||||
val imageByteArray = loadImage(url)
|
||||
cache.cacheImage(url, imageByteArray)
|
||||
return@withContext imageByteArray.toComposeImage()
|
||||
}
|
||||
|
||||
} catch (ex: Exception) {
|
||||
ex.printStackTrace()
|
||||
}
|
||||
|
||||
// If a previous return hasn't been called, something has gone wrong, return null
|
||||
return@withContext null
|
||||
}
|
||||
|
||||
suspend fun loadImage(link: String): ByteArray = withContext(Dispatchers.IO) {
|
||||
val url = URL(link)
|
||||
val connection = url.openConnection() as HttpURLConnection
|
||||
connection.connect()
|
||||
|
||||
connection.inputStream.toByteArray()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Composable
|
||||
fun rememberNetworkImage(url: String): ImageBitmap {
|
||||
val networkImageLoader = NetworkImageLoader
|
||||
var image by remember(url) {
|
||||
mutableStateOf(
|
||||
useResource("image.jpg") {
|
||||
Image.makeFromEncoded(it.toByteArray()).toComposeImageBitmap()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
LaunchedEffect(url) {
|
||||
val networkImage = networkImageLoader.loadImageNetwork(url)
|
||||
if(networkImage != null)
|
||||
image = networkImage
|
||||
}
|
||||
|
||||
return image
|
||||
}
|
||||
|
||||
fun ByteArray.toComposeImage() = Image.makeFromEncoded(this).toComposeImageBitmap()
|
||||
|
@ -5,38 +5,30 @@ import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.Divider
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.res.useResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import app.extensions.*
|
||||
import kotlinx.coroutines.*
|
||||
import org.eclipse.jgit.diff.DiffEntry
|
||||
import org.eclipse.jgit.revwalk.RevCommit
|
||||
import app.git.GitManager
|
||||
import app.images.rememberNetworkImage
|
||||
import app.theme.headerBackground
|
||||
import app.theme.headerText
|
||||
import app.theme.primaryTextColor
|
||||
import app.theme.secondaryTextColor
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.graphics.toComposeImageBitmap
|
||||
import app.ui.components.ScrollableLazyColumn
|
||||
import app.git.GitManager
|
||||
import app.images.ImagesCache
|
||||
import app.images.InMemoryImagesCache
|
||||
import app.theme.headerText
|
||||
import app.ui.components.TooltipText
|
||||
import org.eclipse.jgit.lib.PersonIdent
|
||||
import org.jetbrains.skia.Image.Companion.makeFromEncoded
|
||||
import org.eclipse.jgit.diff.DiffEntry
|
||||
import org.eclipse.jgit.revwalk.RevCommit
|
||||
|
||||
@Composable
|
||||
fun CommitChanges(
|
||||
@ -166,45 +158,6 @@ fun Author(commit: RevCommit) {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun loadImage(link: String): ByteArray = withContext(Dispatchers.IO) {
|
||||
val url = URL(link)
|
||||
val connection = url.openConnection() as HttpURLConnection
|
||||
connection.connect()
|
||||
|
||||
connection.inputStream.toByteArray()
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun rememberNetworkImage(url: String, cache: ImagesCache = InMemoryImagesCache): ImageBitmap {
|
||||
val cachedImage = cache.getCachedObject(url)
|
||||
|
||||
var image by remember(url) {
|
||||
if(cachedImage != null)
|
||||
mutableStateOf(makeFromEncoded(cachedImage).toComposeImageBitmap())
|
||||
else
|
||||
mutableStateOf(
|
||||
useResource("image.jpg") {
|
||||
makeFromEncoded(it.toByteArray()).toComposeImageBitmap()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if(cachedImage == null) {
|
||||
LaunchedEffect(url) {
|
||||
try {
|
||||
loadImage(url).let {
|
||||
image = makeFromEncoded(it).toComposeImageBitmap()
|
||||
cache.cacheImage(url, it)
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
println("Avatar loading failed: ${ex.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return image
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CommitLogChanges(diffEntries: List<DiffEntry>, onDiffSelected: (DiffEntry) -> Unit) {
|
||||
val selectedIndex = remember(diffEntries) { mutableStateOf(-1) }
|
||||
|
@ -36,6 +36,7 @@ import app.extensions.*
|
||||
import app.git.GitManager
|
||||
import app.git.LogStatus
|
||||
import app.git.graph.GraphNode
|
||||
import app.images.rememberNetworkImage
|
||||
import app.theme.headerBackground
|
||||
import app.theme.headerText
|
||||
import app.theme.primaryTextColor
|
||||
@ -46,7 +47,6 @@ import app.ui.dialogs.MergeDialog
|
||||
import app.ui.dialogs.NewBranchDialog
|
||||
import app.ui.dialogs.NewTagDialog
|
||||
import app.ui.dialogs.ResetBranchDialog
|
||||
import app.ui.rememberNetworkImage
|
||||
import org.eclipse.jgit.lib.Ref
|
||||
import org.eclipse.jgit.revwalk.RevCommit
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user