Use IO dispatcher for some screen model work

Not sure if this is an ideal approach. If it is, we could migrate more usages to this.
This commit is contained in:
arkon 2023-03-28 22:52:30 -04:00
parent d1bf857079
commit 18f9e5ba6b
4 changed files with 34 additions and 16 deletions

View File

@ -31,11 +31,11 @@ import androidx.lifecycle.asFlow
import androidx.work.WorkInfo import androidx.work.WorkInfo
import androidx.work.WorkQuery import androidx.work.WorkQuery
import cafe.adriel.voyager.core.model.ScreenModel import cafe.adriel.voyager.core.model.ScreenModel
import cafe.adriel.voyager.core.model.coroutineScope
import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.presentation.util.Screen import eu.kanade.presentation.util.Screen
import eu.kanade.presentation.util.ioCoroutineScope
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.system.workManager import eu.kanade.tachiyomi.util.system.workManager
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
@ -128,19 +128,19 @@ object WorkerInfoScreen : Screen() {
.getWorkInfosLiveData(WorkQuery.fromStates(WorkInfo.State.SUCCEEDED, WorkInfo.State.FAILED, WorkInfo.State.CANCELLED)) .getWorkInfosLiveData(WorkQuery.fromStates(WorkInfo.State.SUCCEEDED, WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
.asFlow() .asFlow()
.map(::constructString) .map(::constructString)
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), "") .stateIn(ioCoroutineScope, SharingStarted.WhileSubscribed(), "")
val running = workManager val running = workManager
.getWorkInfosLiveData(WorkQuery.fromStates(WorkInfo.State.RUNNING)) .getWorkInfosLiveData(WorkQuery.fromStates(WorkInfo.State.RUNNING))
.asFlow() .asFlow()
.map(::constructString) .map(::constructString)
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), "") .stateIn(ioCoroutineScope, SharingStarted.WhileSubscribed(), "")
val enqueued = workManager val enqueued = workManager
.getWorkInfosLiveData(WorkQuery.fromStates(WorkInfo.State.ENQUEUED)) .getWorkInfosLiveData(WorkQuery.fromStates(WorkInfo.State.ENQUEUED))
.asFlow() .asFlow()
.map(::constructString) .map(::constructString)
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), "") .stateIn(ioCoroutineScope, SharingStarted.WhileSubscribed(), "")
private fun constructString(list: List<WorkInfo>) = buildString { private fun constructString(list: List<WorkInfo>) = buildString {
if (list.isEmpty()) { if (list.isEmpty()) {

View File

@ -3,12 +3,20 @@ package eu.kanade.presentation.util
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.ProvidableCompositionLocal import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.runtime.staticCompositionLocalOf
import cafe.adriel.voyager.core.model.ScreenModel
import cafe.adriel.voyager.core.model.ScreenModelStore
import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.core.screen.ScreenKey import cafe.adriel.voyager.core.screen.ScreenKey
import cafe.adriel.voyager.core.screen.uniqueScreenKey import cafe.adriel.voyager.core.screen.uniqueScreenKey
import cafe.adriel.voyager.core.stack.StackEvent import cafe.adriel.voyager.core.stack.StackEvent
import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.transitions.ScreenTransition import cafe.adriel.voyager.transitions.ScreenTransition
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.plus
import soup.compose.material.motion.animation.materialSharedAxisX import soup.compose.material.motion.animation.materialSharedAxisX
import soup.compose.material.motion.animation.rememberSlideDistance import soup.compose.material.motion.animation.rememberSlideDistance
@ -28,6 +36,18 @@ abstract class Screen : Screen {
override val key: ScreenKey = uniqueScreenKey override val key: ScreenKey = uniqueScreenKey
} }
/**
* A variant of ScreenModel.coroutineScope except with the IO dispatcher instead of the
* main dispatcher.
*/
val ScreenModel.ioCoroutineScope: CoroutineScope
get() = ScreenModelStore.getOrPutDependency(
screenModel = this,
name = "ScreenModelIoCoroutineScope",
factory = { key -> CoroutineScope(Dispatchers.IO + SupervisorJob()) + CoroutineName(key) },
onDispose = { scope -> scope.cancel() },
)
interface AssistContentScreen { interface AssistContentScreen {
fun onProvideAssistUrl(): String? fun onProvideAssistUrl(): String?
} }

View File

@ -18,6 +18,7 @@ import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.manga.model.toDomainManga import eu.kanade.domain.manga.model.toDomainManga
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.domain.track.model.toDomainTrack import eu.kanade.domain.track.model.toDomainTrack
import eu.kanade.presentation.util.ioCoroutineScope
import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.track.EnhancedTrackService import eu.kanade.tachiyomi.data.track.EnhancedTrackService
import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackManager
@ -125,12 +126,12 @@ class BrowseSourceScreenModel(
.filter { localManga -> .filter { localManga ->
!sourcePreferences.hideInLibraryItems().get() || !localManga.favorite !sourcePreferences.hideInLibraryItems().get() || !localManga.favorite
} }
.stateIn(coroutineScope) .stateIn(ioCoroutineScope)
} }
} }
.cachedIn(coroutineScope) .cachedIn(ioCoroutineScope)
} }
.stateIn(coroutineScope, SharingStarted.Lazily, emptyFlow()) .stateIn(ioCoroutineScope, SharingStarted.Lazily, emptyFlow())
fun getColumnsPreference(orientation: Int): GridCells { fun getColumnsPreference(orientation: Int): GridCells {
val isLandscape = orientation == Configuration.ORIENTATION_LANDSCAPE val isLandscape = orientation == Configuration.ORIENTATION_LANDSCAPE

View File

@ -4,10 +4,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.produceState import androidx.compose.runtime.produceState
import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope
import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.manga.model.toDomainManga import eu.kanade.domain.manga.model.toDomainManga
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.presentation.util.ioCoroutineScope
import eu.kanade.tachiyomi.extension.ExtensionManager import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.CatalogueSource
import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher
@ -17,7 +17,6 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import tachiyomi.core.util.lang.awaitSingle import tachiyomi.core.util.lang.awaitSingle
import tachiyomi.core.util.lang.withIOContext
import tachiyomi.domain.manga.interactor.GetManga import tachiyomi.domain.manga.interactor.GetManga
import tachiyomi.domain.manga.interactor.NetworkToLocalManga import tachiyomi.domain.manga.interactor.NetworkToLocalManga
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
@ -94,7 +93,7 @@ abstract class SearchScreenModel<T>(
abstract fun getItems(): Map<CatalogueSource, SearchItemResult> abstract fun getItems(): Map<CatalogueSource, SearchItemResult>
fun getAndUpdateItems(function: (Map<CatalogueSource, SearchItemResult>) -> Map<CatalogueSource, SearchItemResult>) { private fun getAndUpdateItems(function: (Map<CatalogueSource, SearchItemResult>) -> Map<CatalogueSource, SearchItemResult>) {
updateItems(function(getItems())) updateItems(function(getItems()))
} }
@ -106,7 +105,7 @@ abstract class SearchScreenModel<T>(
val initialItems = getSelectedSources().associateWith { SearchItemResult.Loading } val initialItems = getSelectedSources().associateWith { SearchItemResult.Loading }
updateItems(initialItems) updateItems(initialItems)
coroutineScope.launch { ioCoroutineScope.launch {
sources sources
.map { source -> .map { source ->
async { async {
@ -115,11 +114,9 @@ abstract class SearchScreenModel<T>(
source.fetchSearchManga(1, query, source.getFilterList()).awaitSingle() source.fetchSearchManga(1, query, source.getFilterList()).awaitSingle()
} }
val titles = withIOContext { val titles = page.mangas.map {
page.mangas.map {
networkToLocalManga.await(it.toDomainManga(source.id)) networkToLocalManga.await(it.toDomainManga(source.id))
} }
}
getAndUpdateItems { items -> getAndUpdateItems { items ->
val mutableMap = items.toMutableMap() val mutableMap = items.toMutableMap()
@ -129,7 +126,7 @@ abstract class SearchScreenModel<T>(
} catch (e: Exception) { } catch (e: Exception) {
getAndUpdateItems { items -> getAndUpdateItems { items ->
val mutableMap = items.toMutableMap() val mutableMap = items.toMutableMap()
mutableMap[source] = SearchItemResult.Error(throwable = e) mutableMap[source] = SearchItemResult.Error(e)
mutableMap.toSortedMap(sortComparator(mutableMap)) mutableMap.toSortedMap(sortComparator(mutableMap))
} }
} }