Limited images concurrent loading to 3

This commit is contained in:
Abdelilah El Aissaoui 2021-12-07 17:42:15 +01:00
parent bf6600878b
commit 7a3d68e3a6
6 changed files with 102 additions and 59 deletions

View 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()
}
}

View File

@ -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)
}

View File

@ -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]
}

View 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()

View File

@ -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) }

View File

@ -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