More coroutine tweaks

This commit is contained in:
arkon 2021-01-10 11:01:10 -05:00
parent 2ffbee3db2
commit c9cf9cfff0
13 changed files with 113 additions and 125 deletions

View file

@ -20,9 +20,7 @@ import org.acra.sender.HttpSender
import org.conscrypt.Conscrypt
import timber.log.Timber
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.InjektScope
import uy.kohesive.injekt.injectLazy
import uy.kohesive.injekt.registry.default.DefaultRegistrar
import java.security.Security
@AcraCore(
@ -53,7 +51,6 @@ open class App : Application(), LifecycleObserver {
Security.insertProviderAt(Conscrypt.newProvider(), 1)
}
Injekt = InjektScope(DefaultRegistrar())
Injekt.importModule(AppModule(this))
setupAcra()

View file

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi
import android.app.Application
import android.os.Handler
import com.google.gson.Gson
import eu.kanade.tachiyomi.data.cache.ChapterCache
import eu.kanade.tachiyomi.data.cache.CoverCache
@ -11,8 +12,6 @@ import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.source.SourceManager
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json
import uy.kohesive.injekt.api.InjektModule
import uy.kohesive.injekt.api.InjektRegistrar
@ -48,15 +47,16 @@ class AppModule(val app: Application) : InjektModule {
addSingletonFactory { Json { ignoreUnknownKeys = true } }
// Asynchronously init expensive components for a faster cold start
Handler().post {
get<PreferencesHelper>()
GlobalScope.launch { get<PreferencesHelper>() }
get<NetworkHelper>()
GlobalScope.launch { get<NetworkHelper>() }
get<SourceManager>()
GlobalScope.launch { get<SourceManager>() }
get<DatabaseHelper>()
GlobalScope.launch { get<DatabaseHelper>() }
GlobalScope.launch { get<DownloadManager>() }
get<DownloadManager>()
}
}
}

View file

@ -10,7 +10,6 @@ import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.util.chapter.NoChaptersException
import eu.kanade.tachiyomi.util.lang.await
import kotlinx.coroutines.Job
import uy.kohesive.injekt.injectLazy
import java.io.File
@ -91,7 +90,7 @@ abstract class AbstractBackupRestore<T : AbstractBackupManager>(protected val co
if (service != null && service.isLogged) {
try {
val updatedTrack = service.refresh(track)
db.insertTrack(updatedTrack).await()
db.insertTrack(updatedTrack).executeAsBlocking()
} catch (e: Exception) {
errors.add(Date() to "${manga.title} - ${e.message}")
}

View file

@ -15,7 +15,6 @@ import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.toSManga
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourcePresenter
import eu.kanade.tachiyomi.util.lang.await
import eu.kanade.tachiyomi.util.lang.runAsObservable
import rx.Observable
import rx.Subscription
@ -255,7 +254,7 @@ open class GlobalSearchPresenter(
val networkManga = source.getMangaDetails(manga.toMangaInfo())
manga.copyFrom(networkManga.toSManga())
manga.initialized = true
db.insertManga(manga).await()
db.insertManga(manga).executeAsBlocking()
return manga
}

View file

@ -199,7 +199,7 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() {
return
}
launchIO {
lifecycleScope.launchIO {
try {
val pendingUpdates = ExtensionGithubApi().checkForUpdates(this@MainActivity)
preferences.extensionUpdatesCount().set(pendingUpdates.size)

View file

@ -24,7 +24,6 @@ import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem
import eu.kanade.tachiyomi.util.chapter.ChapterSettingsHelper
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.isLocal
import eu.kanade.tachiyomi.util.lang.await
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.withUIContext
import eu.kanade.tachiyomi.util.prepUpdateCover
@ -168,7 +167,7 @@ class MangaPresenter(
manga.prepUpdateCover(coverCache, sManga, manualFetch)
manga.copyFrom(sManga)
manga.initialized = true
db.insertManga(manga).await()
db.insertManga(manga).executeAsBlocking()
withUIContext { view?.onFetchMangaInfoDone() }
} catch (e: Throwable) {
@ -350,7 +349,7 @@ class MangaPresenter(
hasRequested = true
if (fetchChaptersJob?.isActive == true) return
fetchChaptersJob = launchIO {
fetchChaptersJob = presenterScope.launchIO {
try {
val chapters = source.getChapterList(manga.toMangaInfo())
.map { it.toSChapter() }
@ -464,7 +463,7 @@ class MangaPresenter(
}
launchIO {
db.updateChaptersProgress(chapters).await()
db.updateChaptersProgress(chapters).executeAsBlocking()
if (preferences.removeAfterMarkedAsRead()) {
deleteChapters(chapters)
@ -489,7 +488,7 @@ class MangaPresenter(
selectedChapters
.forEach {
it.bookmark = bookmarked
db.updateChapterProgress(it).await()
db.updateChapterProgress(it).executeAsBlocking()
}
}
}

View file

@ -8,7 +8,6 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.util.lang.await
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.withUIContext
import eu.kanade.tachiyomi.util.system.toast
@ -67,7 +66,7 @@ class TrackPresenter(
.map {
async {
val track = it.service.refresh(it.track!!)
db.insertTrack(track).await()
db.insertTrack(track).executeAsBlocking()
}
}
.awaitAll()
@ -98,7 +97,7 @@ class TrackPresenter(
launchIO {
try {
service.bind(item)
db.insertTrack(item).await()
db.insertTrack(item).executeAsBlocking()
} catch (e: Throwable) {
withUIContext { context.toast(e.message) }
}
@ -116,7 +115,7 @@ class TrackPresenter(
launchIO {
try {
service.update(track)
db.insertTrack(track).await()
db.insertTrack(track).executeAsBlocking()
withUIContext { view?.onRefreshDone() }
} catch (e: Throwable) {
withUIContext { view?.onRefreshError(e) }

View file

@ -21,7 +21,6 @@ import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters
import eu.kanade.tachiyomi.util.isLocal
import eu.kanade.tachiyomi.util.lang.await
import eu.kanade.tachiyomi.util.lang.byteSize
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.takeBytes
@ -664,7 +663,7 @@ class ReaderPresenter(
val trackManager = Injekt.get<TrackManager>()
launchIO {
db.getTracks(manga).await()
db.getTracks(manga).executeAsBlocking()
.mapNotNull { track ->
val service = trackManager.getService(track.sync_id)
if (service != null && service.isLogged && chapterRead > track.last_chapter_read) {
@ -675,7 +674,7 @@ class ReaderPresenter(
async {
runCatching {
service.update(track)
db.insertTrack(track).await()
db.insertTrack(track).executeAsBlocking()
}
}
} else {
@ -683,8 +682,8 @@ class ReaderPresenter(
}
}
.awaitAll()
.filter { it.isFailure }
.forEach { it.exceptionOrNull()?.let { e -> Timber.w(e) } }
.mapNotNull { it.exceptionOrNull() }
.forEach { Timber.w(it) }
}
}

View file

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.ui.setting.track
import android.net.Uri
import androidx.lifecycle.lifecycleScope
import eu.kanade.tachiyomi.util.lang.launchIO
class AnilistLoginActivity : BaseOAuthLoginActivity() {
@ -9,7 +10,7 @@ class AnilistLoginActivity : BaseOAuthLoginActivity() {
val regex = "(?:access_token=)(.*?)(?:&)".toRegex()
val matchResult = regex.find(data?.fragment.toString())
if (matchResult?.groups?.get(1) != null) {
launchIO {
lifecycleScope.launchIO {
trackManager.aniList.login(matchResult.groups[1]!!.value)
returnToSettings()
}

View file

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.ui.setting.track
import android.net.Uri
import androidx.lifecycle.lifecycleScope
import eu.kanade.tachiyomi.util.lang.launchIO
class BangumiLoginActivity : BaseOAuthLoginActivity() {
@ -8,7 +9,7 @@ class BangumiLoginActivity : BaseOAuthLoginActivity() {
override fun handleResult(data: Uri?) {
val code = data?.getQueryParameter("code")
if (code != null) {
launchIO {
lifecycleScope.launchIO {
trackManager.bangumi.login(code)
returnToSettings()
}

View file

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.ui.setting.track
import android.net.Uri
import androidx.lifecycle.lifecycleScope
import eu.kanade.tachiyomi.util.lang.launchIO
class MyAnimeListLoginActivity : BaseOAuthLoginActivity() {
@ -8,7 +9,7 @@ class MyAnimeListLoginActivity : BaseOAuthLoginActivity() {
override fun handleResult(data: Uri?) {
val code = data?.getQueryParameter("code")
if (code != null) {
launchIO {
lifecycleScope.launchIO {
trackManager.myAnimeList.login(code)
returnToSettings()
}

View file

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.ui.setting.track
import android.net.Uri
import androidx.lifecycle.lifecycleScope
import eu.kanade.tachiyomi.util.lang.launchIO
class ShikimoriLoginActivity : BaseOAuthLoginActivity() {
@ -8,7 +9,7 @@ class ShikimoriLoginActivity : BaseOAuthLoginActivity() {
override fun handleResult(data: Uri?) {
val code = data?.getQueryParameter("code")
if (code != null) {
launchIO {
lifecycleScope.launchIO {
trackManager.shikimori.login(code)
returnToSettings()
}

View file

@ -1,7 +1,5 @@
package eu.kanade.tachiyomi.util.lang
import com.pushtorefresh.storio.operations.PreparedOperation
import com.pushtorefresh.storio.sqlite.operations.get.PreparedGetObject
import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineStart
@ -10,11 +8,8 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import rx.Completable
import rx.CompletableSubscriber
import rx.Emitter
import rx.Observable
import rx.Observer
@ -53,49 +48,46 @@ suspend fun <T> Single<T>.await(subscribeOn: Scheduler? = null): T {
}
}
suspend fun <T> PreparedOperation<T>.await(): T = asRxSingle().await()
suspend fun <T> PreparedGetObject<T>.await(): T? = asRxSingle().await()
suspend fun Completable.awaitSuspending(subscribeOn: Scheduler? = null) {
return suspendCancellableCoroutine { continuation ->
val self = if (subscribeOn != null) subscribeOn(subscribeOn) else this
lateinit var sub: Subscription
sub = self.subscribe(
{
continuation.resume(Unit) {
sub.unsubscribe()
}
},
{
if (!continuation.isCancelled) {
continuation.resumeWithException(it)
}
}
)
continuation.invokeOnCancellation {
sub.unsubscribe()
}
}
}
suspend fun Completable.awaitCompleted(): Unit = suspendCancellableCoroutine { cont ->
subscribe(
object : CompletableSubscriber {
override fun onSubscribe(s: Subscription) {
cont.unsubscribeOnCancellation(s)
}
override fun onCompleted() {
cont.resume(Unit)
}
override fun onError(e: Throwable) {
cont.resumeWithException(e)
}
}
)
}
// suspend fun Completable.awaitSuspending(subscribeOn: Scheduler? = null) {
// return suspendCancellableCoroutine { continuation ->
// val self = if (subscribeOn != null) subscribeOn(subscribeOn) else this
// lateinit var sub: Subscription
// sub = self.subscribe(
// {
// continuation.resume(Unit) {
// sub.unsubscribe()
// }
// },
// {
// if (!continuation.isCancelled) {
// continuation.resumeWithException(it)
// }
// }
// )
//
// continuation.invokeOnCancellation {
// sub.unsubscribe()
// }
// }
// }
//
// suspend fun Completable.awaitCompleted(): Unit = suspendCancellableCoroutine { cont ->
// subscribe(
// object : CompletableSubscriber {
// override fun onSubscribe(s: Subscription) {
// cont.unsubscribeOnCancellation(s)
// }
//
// override fun onCompleted() {
// cont.resume(Unit)
// }
//
// override fun onError(e: Throwable) {
// cont.resumeWithException(e)
// }
// }
// )
// }
suspend fun <T> Single<T>.await(): T = suspendCancellableCoroutine { cont ->
cont.unsubscribeOnCancellation(
@ -113,27 +105,27 @@ suspend fun <T> Single<T>.await(): T = suspendCancellableCoroutine { cont ->
)
}
suspend fun <T> Observable<T>.awaitFirst(): T = first().awaitOne()
suspend fun <T> Observable<T>.awaitFirstOrDefault(default: T): T =
firstOrDefault(default).awaitOne()
suspend fun <T> Observable<T>.awaitFirstOrNull(): T? = firstOrDefault(null).awaitOne()
suspend fun <T> Observable<T>.awaitFirstOrElse(defaultValue: () -> T): T = switchIfEmpty(
Observable.fromCallable(
defaultValue
)
).first().awaitOne()
suspend fun <T> Observable<T>.awaitLast(): T = last().awaitOne()
// suspend fun <T> Observable<T>.awaitFirst(): T = first().awaitOne()
//
// suspend fun <T> Observable<T>.awaitFirstOrDefault(default: T): T =
// firstOrDefault(default).awaitOne()
//
// suspend fun <T> Observable<T>.awaitFirstOrNull(): T? = firstOrDefault(null).awaitOne()
//
// suspend fun <T> Observable<T>.awaitFirstOrElse(defaultValue: () -> T): T = switchIfEmpty(
// Observable.fromCallable(
// defaultValue
// )
// ).first().awaitOne()
//
// suspend fun <T> Observable<T>.awaitLast(): T = last().awaitOne()
suspend fun <T> Observable<T>.awaitSingle(): T = single().awaitOne()
suspend fun <T> Observable<T>.awaitSingleOrDefault(default: T): T =
singleOrDefault(default).awaitOne()
suspend fun <T> Observable<T>.awaitSingleOrNull(): T? = singleOrDefault(null).awaitOne()
// suspend fun <T> Observable<T>.awaitSingleOrDefault(default: T): T =
// singleOrDefault(default).awaitOne()
//
// suspend fun <T> Observable<T>.awaitSingleOrNull(): T? = singleOrDefault(null).awaitOne()
private suspend fun <T> Observable<T>.awaitOne(): T = suspendCancellableCoroutine { cont ->
cont.unsubscribeOnCancellation(
@ -192,31 +184,31 @@ fun <T : Any> Observable<T>.asFlow(): Flow<T> = callbackFlow {
awaitClose { subscription.unsubscribe() }
}
fun <T : Any> Flow<T>.asObservable(backpressureMode: Emitter.BackpressureMode = Emitter.BackpressureMode.NONE): Observable<T> {
return Observable.create(
{ emitter ->
/*
* ATOMIC is used here to provide stable behaviour of subscribe+dispose pair even if
* asObservable is already invoked from unconfined
*/
val job = GlobalScope.launch(Dispatchers.Unconfined, start = CoroutineStart.ATOMIC) {
try {
collect { emitter.onNext(it) }
emitter.onCompleted()
} catch (e: Throwable) {
// Ignore `CancellationException` as error, since it indicates "normal cancellation"
if (e !is CancellationException) {
emitter.onError(e)
} else {
emitter.onCompleted()
}
}
}
emitter.setCancellation { job.cancel() }
},
backpressureMode
)
}
// fun <T : Any> Flow<T>.asObservable(backpressureMode: Emitter.BackpressureMode = Emitter.BackpressureMode.NONE): Observable<T> {
// return Observable.create(
// { emitter ->
// /*
// * ATOMIC is used here to provide stable behaviour of subscribe+dispose pair even if
// * asObservable is already invoked from unconfined
// */
// val job = GlobalScope.launch(Dispatchers.Unconfined, start = CoroutineStart.ATOMIC) {
// try {
// collect { emitter.onNext(it) }
// emitter.onCompleted()
// } catch (e: Throwable) {
// // Ignore `CancellationException` as error, since it indicates "normal cancellation"
// if (e !is CancellationException) {
// emitter.onError(e)
// } else {
// emitter.onCompleted()
// }
// }
// }
// emitter.setCancellation { job.cancel() }
// },
// backpressureMode
// )
// }
fun <T> runAsObservable(
block: suspend () -> T,