Procházet zdrojové kódy

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.
arkon před 3 roky
rodič
revize
18f9e5ba6b

+ 4 - 4
app/src/main/java/eu/kanade/presentation/more/settings/screen/WorkerInfoScreen.kt

@@ -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()) {

+ 20 - 0
app/src/main/java/eu/kanade/presentation/util/Navigator.kt

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

+ 4 - 3
app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt

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

+ 6 - 9
app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/SearchScreenModel.kt

@@ -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,10 +114,8 @@ abstract class SearchScreenModel<T>(
                                 source.fetchSearchManga(1, query, source.getFilterList()).awaitSingle()
                                 source.fetchSearchManga(1, query, source.getFilterList()).awaitSingle()
                             }
                             }
 
 
-                            val titles = withIOContext {
-                                page.mangas.map {
-                                    networkToLocalManga.await(it.toDomainManga(source.id))
-                                }
+                            val titles = page.mangas.map {
+                                networkToLocalManga.await(it.toDomainManga(source.id))
                             }
                             }
 
 
                             getAndUpdateItems { items ->
                             getAndUpdateItems { items ->
@@ -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))
                             }
                             }
                         }
                         }