Reduce DownloadManager initialization time

This commit is contained in:
FooIbar committed 2025-10-06 09:42:32 +08:00
1 parent cf10e2ff6d
commit 779dd1f8fd
2 files changed
+101 -55

No files matched your search

@@ -22,8 +22,11 @@ import androidx.compose.runtime.DisallowComposableCalls
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.core.util.size
import arrow.fx.coroutines.parMapNotNull
import com.ehviewer.core.data.model.asEntity
@@ -72,9 +75,7 @@ object DownloadManager : OnSpiderListener, CoroutineScope {
override val coroutineContext = Dispatchers.IO + Job()
// All download info list
private val allInfoList = runAssertingNotMainThread {
(EhDB.getAllDownloadInfo() as MutableList).apply { sortWith(comparator()) }
}
private val allInfoList = runAssertingNotMainThread { EhDB.getAllDownloadInfo() as MutableList }
val downloadInfoList: List<DownloadInfo>
get() = allInfoList
@@ -99,6 +100,21 @@ object DownloadManager : OnSpiderListener, CoroutineScope {
private val mutex = Mutex()
private val sortMutex = Mutex()
private val sortMode
get() = SortMode.from(Settings.downloadSortMode.value)
var isInitialized by mutableStateOf(false)
private set
init {
launch {
val mode = sortMode
if (mode != SortMode.Default) sortDownloads(mode)
isInitialized = true
}
}
fun containLabel(label: String?): Boolean {
if (label == null) {
return false
@@ -184,7 +200,9 @@ object DownloadManager : OnSpiderListener, CoroutineScope {
info.label = label
info.state = DownloadInfo.STATE_WAIT
// Add to all download list and map
allInfoList.insertWith(info, comparator())
sortMutex.withLock {
allInfoList.insertWith(info, sortMode.comparator())
}
mAllInfoMap[galleryInfo.gid] = info
// Add to wait list
@@ -241,10 +259,12 @@ object DownloadManager : OnSpiderListener, CoroutineScope {
}.collectAsState(transform(info)).value
suspend fun startAllDownload() {
val updateList = allInfoList.filter {
when (it.state) {
DownloadInfo.STATE_NONE, DownloadInfo.STATE_FAILED -> true
else -> false
val updateList = sortMutex.withLock {
allInfoList.filter {
when (it.state) {
DownloadInfo.STATE_NONE, DownloadInfo.STATE_FAILED -> true
else -> false
}
}
}
if (updateList.isNotEmpty()) {
@@ -258,8 +278,8 @@ object DownloadManager : OnSpiderListener, CoroutineScope {
}
}
suspend fun addDownload(downloadInfoList: List<DownloadInfo>) {
val comparator = comparator()
suspend fun addDownload(downloadInfoList: List<DownloadInfo>) = sortMutex.withLock {
val comparator = sortMode.comparator()
downloadInfoList.forEach { info ->
if (containDownloadInfo(info.gid)) return@forEach
@@ -293,7 +313,9 @@ object DownloadManager : OnSpiderListener, CoroutineScope {
info.state = DownloadInfo.STATE_NONE
// Add to all download list and map
allInfoList.insertWith(info, comparator())
sortMutex.withLock {
allInfoList.insertWith(info, sortMode.comparator())
}
mAllInfoMap[galleryInfo.gid] = info
// Save to
@@ -352,7 +374,9 @@ object DownloadManager : OnSpiderListener, CoroutineScope {
EhDB.removeDownloadInfo(info)
// Remove all list and map
allInfoList.remove(info)
sortMutex.withLock {
allInfoList.remove(info)
}
mAllInfoMap.remove(info.gid)
// Update listener
@@ -375,7 +399,9 @@ object DownloadManager : OnSpiderListener, CoroutineScope {
mAllInfoMap.remove(gid)
}
EhDB.removeDownloadInfo(list)
allInfoList.removeAll(list.toSet())
sortMutex.withLock {
allInfoList.removeAll(list.toSet())
}
// Update listener
list.forEach { mutableNotifyFlow.emit(it) }
@@ -384,7 +410,7 @@ object DownloadManager : OnSpiderListener, CoroutineScope {
ensureDownload()
}
fun sortDownloads(mode: SortMode) {
suspend fun sortDownloads(mode: SortMode) = sortMutex.withLock {
allInfoList.sortWith(mode.comparator())
}
@@ -503,9 +529,11 @@ object DownloadManager : OnSpiderListener, CoroutineScope {
val new = exist.copy(label = to)
labelList.add(index, new)
EhDB.updateDownloadLabel(new)
allInfoList.forEach {
if (it.label == from) {
it.label = to
sortMutex.withLock {
allInfoList.forEach {
if (it.label == from) {
it.label = to
}
}
}
}
@@ -521,21 +549,25 @@ object DownloadManager : OnSpiderListener, CoroutineScope {
}
removeAt(index)
}
allInfoList.forEach {
if (it.label == label) {
it.label = null
sortMutex.withLock {
allInfoList.forEach {
if (it.label == label) {
it.label = null
}
}
}
}
suspend fun readMetadataFromLocal() {
val list = allInfoList.mapNotNull {
val updateGallery = it.pages == 0 || it.simpleTags == null
val updateArtist = it.artistInfoList.isEmpty()
if (updateGallery || updateArtist) {
Triple(it, updateGallery, updateArtist)
} else {
null
val list = sortMutex.withLock {
allInfoList.mapNotNull {
val updateGallery = it.pages == 0 || it.simpleTags == null
val updateArtist = it.artistInfoList.isEmpty()
if (updateGallery || updateArtist) {
Triple(it, updateGallery, updateArtist)
} else {
null
}
}
}.parMapNotNull(concurrency = 5) { (info, updateGallery, updateArtist) ->
info.downloadDir?.run {
@@ -785,8 +817,6 @@ object DownloadManager : OnSpiderListener, CoroutineScope {
}
private const val TAG = "DownloadManager"
private fun comparator() = SortMode.from(Settings.downloadSortMode.value).comparator()
}
var downloadLocation: Path
@@ -5,8 +5,10 @@ import android.view.ViewConfiguration
import androidx.compose.animation.AnimatedVisibilityScope
import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
@@ -43,6 +45,7 @@ import androidx.compose.material.icons.filled.Reorder
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.filled.Shuffle
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CircularWavyProgressIndicator
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
@@ -93,7 +96,6 @@ import com.ehviewer.core.ui.component.LocalSideSheetState
import com.ehviewer.core.ui.component.ProvideSideSheetContent
import com.ehviewer.core.ui.icons.EhIcons
import com.ehviewer.core.ui.icons.big.Download
import com.ehviewer.core.ui.util.Await
import com.ehviewer.core.ui.util.HapticFeedbackType
import com.ehviewer.core.ui.util.asyncState
import com.ehviewer.core.ui.util.ifTrueThen
@@ -113,7 +115,6 @@ import com.hippo.ehviewer.asMutableState
import com.hippo.ehviewer.client.EhTagDatabase
import com.hippo.ehviewer.collectAsState
import com.hippo.ehviewer.download.DownloadManager
import com.hippo.ehviewer.download.DownloadManager.downloadInfoList
import com.hippo.ehviewer.download.DownloadService
import com.hippo.ehviewer.download.DownloadsFilterMode
import com.hippo.ehviewer.download.SortMode
@@ -148,6 +149,7 @@ fun AnimatedVisibilityScope.DownloadsScreen(navigator: DestinationsNavigator) =
val filterMode by Settings.downloadFilterMode.collectAsState { DownloadsFilterMode.from(it) }
var filterState by rememberSerializable { mutableStateOf(DownloadsFilterState(filterMode, Settings.recentDownloadLabel.value)) }
var invalidateKey by rememberSaveable { mutableStateOf(false) }
var isLoading by rememberSaveable { mutableStateOf(true) }
var searchBarExpanded by rememberSaveable { mutableStateOf(false) }
var searchBarOffsetY by remember { mutableIntStateOf(0) }
val animateItems by Settings.animateItems.collectAsState()
@@ -183,10 +185,19 @@ fun AnimatedVisibilityScope.DownloadsScreen(navigator: DestinationsNavigator) =
},
)
val hint = stringResource(R.string.search_bar_hint, title)
val list = remember(filterState, invalidateKey) {
downloadInfoList.filterTo(mutableStateListOf()) { info ->
filterState.take(info)
val list = if (DownloadManager.isInitialized) {
remember(filterState, invalidateKey) {
DownloadManager.downloadInfoList.filterTo(mutableStateListOf()) { info ->
filterState.take(info)
}.also {
launch {
delay(200)
isLoading = false
}
}
}
} else {
remember { mutableStateListOf() }
}
val newLabel = stringResource(R.string.new_label_title)
@@ -632,24 +643,26 @@ fun AnimatedVisibilityScope.DownloadsScreen(navigator: DestinationsNavigator) =
}
}
Await({ delay(200) }) {
if (list.isEmpty()) {
Column(
modifier = Modifier.padding(realPadding).fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
imageVector = EhIcons.Big.Default.Download,
contentDescription = null,
modifier = Modifier.padding(16.dp),
tint = MaterialTheme.colorScheme.primary,
)
Text(
text = stringResource(id = R.string.no_download_info),
style = MaterialTheme.typography.headlineMedium,
)
}
if (isLoading) {
Box(modifier = Modifier.fillMaxSize().background(MaterialTheme.colorScheme.surface), contentAlignment = Alignment.Center) {
CircularWavyProgressIndicator()
}
} else if (list.isEmpty()) {
Column(
modifier = Modifier.padding(realPadding).fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
imageVector = EhIcons.Big.Default.Download,
contentDescription = null,
modifier = Modifier.padding(16.dp),
tint = MaterialTheme.colorScheme.primary,
)
Text(
text = stringResource(id = R.string.no_download_info),
style = MaterialTheme.typography.headlineMedium,
)
}
}
}
@@ -689,9 +702,12 @@ fun AnimatedVisibilityScope.DownloadsScreen(navigator: DestinationsNavigator) =
oldMode.groupByDownloadLabel,
)
val mode = SortMode.All[selected].copy(groupByDownloadLabel = checked)
DownloadManager.sortDownloads(mode)
sortMode = mode.flag
invalidateKey = !invalidateKey
if (mode != oldMode) {
sortMode = mode.flag
isLoading = true
DownloadManager.sortDownloads(mode)
invalidateKey = !invalidateKey
}
}
onClick(Icons.Default.FilterList) {
val downloadStates = contextOf<Context>().resources.getStringArray(com.hippo.ehviewer.R.array.download_state).toList()