mirror of
https://github.com/mihonapp/mihon.git
synced 2026-10-16 13:48:09 -07:00
Move blocking work off the main thread and fix threading races (#4068)
* Defer startup work until first use * Move cover work off the main thread * Make tracker calls main-safe and token refresh thread-safe Kitsu, Shikimori and MangaBaka refresh their token under a lock like MAL, so parallel calls no longer refresh it twice. * Fix extension threading and make installs cancellable The store service read response bodies on Main, which failed with NetworkOnMainThreadException for http stores. * Move reader work off the main thread and save progress in order * Serialize the downloader and fix download threading * Stop blocking on WorkManager status queries Also removes NotificationReceiver's open chapter action: nothing sends it, and its handler used runBlocking. * Start migration jobs on the migrator's dispatcher They started undispatched, so each ran on the caller's thread until its first suspension. * Run screen and view model work on fitting dispatchers * Launch work that outlives a screen on the app scope A launch on another dispatcher is dropped if the screen closes before it starts, which dialogs that dismiss right away often do. Only the UI follow-up stays on viewModelScope. * Remove the coroutine helper extensions They hid which dispatcher and scope a call used, and the GlobalScope ones hid that it was global.
This commit is contained in:
@@ -7,6 +7,8 @@ import eu.kanade.tachiyomi.extension.model.Extension
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
import eu.kanade.tachiyomi.network.awaitSuccess
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.decodeFromByteArray
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.okio.decodeFromBufferedSource
|
||||
@@ -30,7 +32,9 @@ class ExtensionStoreService(
|
||||
private val json: Json,
|
||||
private val protoBuf: ProtoBuf,
|
||||
) {
|
||||
suspend fun fetch(indexUrl: String): Result<ExtensionStore> {
|
||||
suspend fun fetch(indexUrl: String): Result<ExtensionStore> = withContext(Dispatchers.IO) { fetchStore(indexUrl) }
|
||||
|
||||
private suspend fun fetchStore(indexUrl: String): Result<ExtensionStore> {
|
||||
var updatedIndexUrl: String = indexUrl
|
||||
return try {
|
||||
val response = network.client.newCall(GET(updatedIndexUrl)).awaitSuccess()
|
||||
@@ -56,7 +60,7 @@ class ExtensionStoreService(
|
||||
}
|
||||
|
||||
if (networkStore is NetworkLegacyExtensionRepo && networkStore.indexV2 != null) {
|
||||
return fetch(networkStore.indexV2)
|
||||
return fetchStore(networkStore.indexV2)
|
||||
}
|
||||
|
||||
networkStore.toExtensionStore(updatedIndexUrl)
|
||||
@@ -72,8 +76,8 @@ class ExtensionStoreService(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getExtensions(store: ExtensionStore): Result<List<Extension.Available>> {
|
||||
return try {
|
||||
suspend fun getExtensions(store: ExtensionStore): Result<List<Extension.Available>> = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val extensions = if (store.extensionListUrl != null) {
|
||||
val response = network.client.newCall(GET(store.extensionListUrl!!)).awaitSuccess()
|
||||
response.body.source().decompressIfGzipped().use { source ->
|
||||
|
||||
@@ -9,6 +9,8 @@ import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
import eu.kanade.tachiyomi.network.awaitSuccess
|
||||
import eu.kanade.tachiyomi.network.parseAs
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.json.Json
|
||||
import tachiyomi.domain.release.interactor.GetApplicationRelease
|
||||
import tachiyomi.domain.release.model.Release
|
||||
@@ -23,11 +25,13 @@ class ReleaseServiceImpl(
|
||||
) : ReleaseService {
|
||||
|
||||
override suspend fun latest(arguments: GetApplicationRelease.Arguments): Release? {
|
||||
val release = with(json) {
|
||||
networkService.client
|
||||
.newCall(GET("https://api.github.com/repos/${arguments.repository}/releases/latest"))
|
||||
.awaitSuccess()
|
||||
.parseAs<GithubRelease>()
|
||||
val release = withContext(Dispatchers.IO) {
|
||||
with(json) {
|
||||
networkService.client
|
||||
.newCall(GET("https://api.github.com/repos/${arguments.repository}/releases/latest"))
|
||||
.awaitSuccess()
|
||||
.parseAs<GithubRelease>()
|
||||
}
|
||||
}
|
||||
|
||||
val downloadLink = getDownloadLink(release = release, isFoss = arguments.isFoss) ?: return null
|
||||
|
||||
@@ -5,8 +5,9 @@ import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import mihon.domain.manga.model.toDomainManga
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
import tachiyomi.domain.manga.interactor.NetworkToLocalManga
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.source.repository.SourcePagingSource
|
||||
@@ -54,7 +55,7 @@ abstract class BaseSourcePagingSource(
|
||||
|
||||
return try {
|
||||
val source = source()
|
||||
val mangasPage = withIOContext {
|
||||
val mangasPage = withContext(Dispatchers.IO) {
|
||||
requestNextPage(source, page.toInt())
|
||||
.takeIf { it.mangas.isNotEmpty() }
|
||||
?: throw NoResultsException()
|
||||
|
||||
Reference in New Issue
Block a user