Run more db queries inside the IO pool, convert some RxJava references to Coroutines
This commit is contained in:
parent
64eeab7c5e
commit
18f02a85ac
@ -49,7 +49,7 @@ class MdList(private val context: Context, id: Int) : TrackService(id) {
|
|||||||
|
|
||||||
override suspend fun update(track: Track): Track {
|
override suspend fun update(track: Track): Track {
|
||||||
val mdex = mdex ?: throw Exception("Mangadex not enabled")
|
val mdex = mdex ?: throw Exception("Mangadex not enabled")
|
||||||
val mangaMetadata = db.getFlatMetadataForManga(track.manga_id).executeAsBlocking()
|
val mangaMetadata = db.getFlatMetadataForManga(track.manga_id).executeOnIO()
|
||||||
?.raise<MangaDexSearchMetadata>()
|
?.raise<MangaDexSearchMetadata>()
|
||||||
?: throw Exception("Invalid manga metadata")
|
?: throw Exception("Invalid manga metadata")
|
||||||
val followStatus = FollowStatus.fromInt(track.status)
|
val followStatus = FollowStatus.fromInt(track.status)
|
||||||
|
@ -20,6 +20,7 @@ import exh.search.QueryComponent
|
|||||||
import exh.search.SearchEngine
|
import exh.search.SearchEngine
|
||||||
import exh.search.Text
|
import exh.search.Text
|
||||||
import exh.util.cancellable
|
import exh.util.cancellable
|
||||||
|
import exh.util.executeOnIO
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@ -114,7 +115,7 @@ class LibraryCategoryAdapter(view: LibraryCategoryView, val controller: LibraryC
|
|||||||
// Prepare filter object
|
// Prepare filter object
|
||||||
val parsedQuery = searchEngine.parseQuery(savedSearchText)
|
val parsedQuery = searchEngine.parseQuery(savedSearchText)
|
||||||
|
|
||||||
val mangaWithMetaIdsQuery = db.getIdsOfFavoriteMangaWithMetadata().executeAsBlocking()
|
val mangaWithMetaIdsQuery = db.getIdsOfFavoriteMangaWithMetadata().executeOnIO()
|
||||||
val mangaWithMetaIds = LongArray(mangaWithMetaIdsQuery.count)
|
val mangaWithMetaIds = LongArray(mangaWithMetaIdsQuery.count)
|
||||||
if (mangaWithMetaIds.isNotEmpty()) {
|
if (mangaWithMetaIds.isNotEmpty()) {
|
||||||
val mangaIdCol = mangaWithMetaIdsQuery.getColumnIndex(MangaTable.COL_ID)
|
val mangaIdCol = mangaWithMetaIdsQuery.getColumnIndex(MangaTable.COL_ID)
|
||||||
@ -137,8 +138,8 @@ class LibraryCategoryAdapter(view: LibraryCategoryView, val controller: LibraryC
|
|||||||
// No meta? Filter using title
|
// No meta? Filter using title
|
||||||
filterManga(parsedQuery, item.manga)
|
filterManga(parsedQuery, item.manga)
|
||||||
} else {
|
} else {
|
||||||
val tags = db.getSearchTagsForManga(mangaId).executeAsBlocking()
|
val tags = db.getSearchTagsForManga(mangaId).executeOnIO()
|
||||||
val titles = db.getSearchTitlesForManga(mangaId).executeAsBlocking()
|
val titles = db.getSearchTitlesForManga(mangaId).executeOnIO()
|
||||||
filterManga(parsedQuery, item.manga, false, tags, titles)
|
filterManga(parsedQuery, item.manga, false, tags, titles)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -166,7 +167,7 @@ class LibraryCategoryAdapter(view: LibraryCategoryView, val controller: LibraryC
|
|||||||
|
|
||||||
private suspend fun filterManga(queries: List<QueryComponent>, manga: LibraryManga, checkGenre: Boolean = true, searchTags: List<SearchTag>? = null, searchTitles: List<SearchTitle>? = null): Boolean {
|
private suspend fun filterManga(queries: List<QueryComponent>, manga: LibraryManga, checkGenre: Boolean = true, searchTags: List<SearchTag>? = null, searchTitles: List<SearchTitle>? = null): Boolean {
|
||||||
val mappedQueries = queries.groupBy { it.excluded }
|
val mappedQueries = queries.groupBy { it.excluded }
|
||||||
val tracks = if (hasLoggedServices) db.getTracks(manga).executeAsBlocking().toList() else null
|
val tracks = if (hasLoggedServices) db.getTracks(manga).executeOnIO() else null
|
||||||
val source = sourceManager.get(manga.source)
|
val source = sourceManager.get(manga.source)
|
||||||
val genre = if (checkGenre) manga.getGenres().orEmpty() else emptyList()
|
val genre = if (checkGenre) manga.getGenres().orEmpty() else emptyList()
|
||||||
val hasNormalQuery = mappedQueries[false]?.all { queryComponent ->
|
val hasNormalQuery = mappedQueries[false]?.all { queryComponent ->
|
||||||
|
@ -643,7 +643,7 @@ class SettingsEhController : SettingsController() {
|
|||||||
val allMeta = db.getFavoriteMangaWithMetadata().executeAsBlocking().filter {
|
val allMeta = db.getFavoriteMangaWithMetadata().executeAsBlocking().filter {
|
||||||
it.source == EH_SOURCE_ID || it.source == EXH_SOURCE_ID
|
it.source == EH_SOURCE_ID || it.source == EXH_SOURCE_ID
|
||||||
}.mapNotNull {
|
}.mapNotNull {
|
||||||
db.getFlatMetadataForManga(it.id!!).executeAsBlocking()
|
db.getFlatMetadataForManga(it.id!!).executeOnIO()
|
||||||
?.raise<EHentaiSearchMetadata>()
|
?.raise<EHentaiSearchMetadata>()
|
||||||
}.toList()
|
}.toList()
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ object DebugFunctions {
|
|||||||
}.toList()
|
}.toList()
|
||||||
|
|
||||||
allManga.forEach { manga ->
|
allManga.forEach { manga ->
|
||||||
val meta = db.getFlatMetadataForManga(manga.id!!).executeAsBlocking()?.raise<EHentaiSearchMetadata>() ?: return@forEach
|
val meta = db.getFlatMetadataForManga(manga.id!!).executeOnIO()?.raise<EHentaiSearchMetadata>() ?: return@forEach
|
||||||
// remove age flag
|
// remove age flag
|
||||||
meta.aged = false
|
meta.aged = false
|
||||||
db.insertFlatMetadataAsync(meta.flatten()).await()
|
db.insertFlatMetadataAsync(meta.flatten()).await()
|
||||||
@ -95,7 +95,7 @@ object DebugFunctions {
|
|||||||
)?.getMangaDetails(manga.toMangaInfo())?.let { networkManga ->
|
)?.getMangaDetails(manga.toMangaInfo())?.let { networkManga ->
|
||||||
manga.copyFrom(networkManga.toSManga())
|
manga.copyFrom(networkManga.toSManga())
|
||||||
manga.initialized = true
|
manga.initialized = true
|
||||||
db.insertManga(manga).executeAsBlocking()
|
db.insertManga(manga).executeOnIO()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,7 +113,7 @@ object DebugFunctions {
|
|||||||
}.toList()
|
}.toList()
|
||||||
|
|
||||||
allManga.forEach { manga ->
|
allManga.forEach { manga ->
|
||||||
val meta = db.getFlatMetadataForManga(manga.id!!).executeAsBlocking()?.raise<EHentaiSearchMetadata>() ?: return@forEach
|
val meta = db.getFlatMetadataForManga(manga.id!!).executeOnIO()?.raise<EHentaiSearchMetadata>() ?: return@forEach
|
||||||
galleries += "Aged: ${meta.aged}\t Title: ${manga.title}"
|
galleries += "Aged: ${meta.aged}\t Title: ${manga.title}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,7 +123,7 @@ object DebugFunctions {
|
|||||||
fun countAgedFlagInEXHManga(): Int {
|
fun countAgedFlagInEXHManga(): Int {
|
||||||
var agedAmount = 0
|
var agedAmount = 0
|
||||||
runBlocking {
|
runBlocking {
|
||||||
val metadataManga = db.getFavoriteMangaWithMetadata().executeAsBlocking()
|
val metadataManga = db.getFavoriteMangaWithMetadata().executeOnIO()
|
||||||
|
|
||||||
val allManga = metadataManga.asFlow().cancellable().mapNotNull { manga ->
|
val allManga = metadataManga.asFlow().cancellable().mapNotNull { manga ->
|
||||||
if (manga.source != EH_SOURCE_ID && manga.source != EXH_SOURCE_ID) {
|
if (manga.source != EH_SOURCE_ID && manga.source != EXH_SOURCE_ID) {
|
||||||
@ -132,7 +132,7 @@ object DebugFunctions {
|
|||||||
}.toList()
|
}.toList()
|
||||||
|
|
||||||
allManga.forEach { manga ->
|
allManga.forEach { manga ->
|
||||||
val meta = db.getFlatMetadataForManga(manga.id!!).executeAsBlocking()?.raise<EHentaiSearchMetadata>() ?: return@forEach
|
val meta = db.getFlatMetadataForManga(manga.id!!).executeOnIO()?.raise<EHentaiSearchMetadata>() ?: return@forEach
|
||||||
if (meta.aged) {
|
if (meta.aged) {
|
||||||
// remove age flag
|
// remove age flag
|
||||||
agedAmount++
|
agedAmount++
|
||||||
@ -203,7 +203,7 @@ object DebugFunctions {
|
|||||||
EHentaiUpdateWorker.scheduleBackground(app)
|
EHentaiUpdateWorker.scheduleBackground(app)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun listScheduledJobs() = app.jobScheduler.allPendingJobs.map { j ->
|
fun listScheduledJobs() = app.jobScheduler.allPendingJobs.joinToString(",\n") { j ->
|
||||||
"""
|
"""
|
||||||
{
|
{
|
||||||
info: ${j.id},
|
info: ${j.id},
|
||||||
@ -212,7 +212,7 @@ object DebugFunctions {
|
|||||||
intervalMillis: ${j.intervalMillis},
|
intervalMillis: ${j.intervalMillis},
|
||||||
}
|
}
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
}.joinToString(",\n")
|
}
|
||||||
|
|
||||||
fun cancelAllScheduledJobs() = app.jobScheduler.cancelAll()
|
fun cancelAllScheduledJobs() = app.jobScheduler.cancelAll()
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import android.app.job.JobService
|
|||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import com.elvishew.xlog.Logger
|
||||||
import com.elvishew.xlog.XLog
|
import com.elvishew.xlog.XLog
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||||
@ -45,20 +46,18 @@ import uy.kohesive.injekt.Injekt
|
|||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
import kotlin.coroutines.CoroutineContext
|
|
||||||
import kotlin.time.ExperimentalTime
|
import kotlin.time.ExperimentalTime
|
||||||
import kotlin.time.days
|
import kotlin.time.days
|
||||||
import kotlin.time.hours
|
import kotlin.time.hours
|
||||||
|
|
||||||
class EHentaiUpdateWorker : JobService(), CoroutineScope {
|
class EHentaiUpdateWorker : JobService() {
|
||||||
override val coroutineContext: CoroutineContext
|
private val scope = CoroutineScope(Dispatchers.Default + Job())
|
||||||
get() = Dispatchers.Default + Job()
|
|
||||||
|
|
||||||
private val db: DatabaseHelper by injectLazy()
|
private val db: DatabaseHelper by injectLazy()
|
||||||
private val prefs: PreferencesHelper by injectLazy()
|
private val prefs: PreferencesHelper by injectLazy()
|
||||||
private val sourceManager: SourceManager by injectLazy()
|
private val sourceManager: SourceManager by injectLazy()
|
||||||
private val updateHelper: EHentaiUpdateHelper by injectLazy()
|
private val updateHelper: EHentaiUpdateHelper by injectLazy()
|
||||||
private val logger = XLog.tag("EHUpdater")
|
private val logger: Logger = XLog.tag("EHUpdater").build()
|
||||||
|
|
||||||
private val updateNotifier by lazy { LibraryUpdateNotifier(this) }
|
private val updateNotifier by lazy { LibraryUpdateNotifier(this) }
|
||||||
|
|
||||||
@ -86,7 +85,7 @@ class EHentaiUpdateWorker : JobService(), CoroutineScope {
|
|||||||
* to end the job entirely. Regardless of the value returned, your job must stop executing.
|
* to end the job entirely. Regardless of the value returned, your job must stop executing.
|
||||||
*/
|
*/
|
||||||
override fun onStopJob(params: JobParameters?): Boolean {
|
override fun onStopJob(params: JobParameters?): Boolean {
|
||||||
runBlocking { this@EHentaiUpdateWorker.coroutineContext[Job]?.cancelAndJoin() }
|
runBlocking { scope.coroutineContext[Job]?.cancelAndJoin() }
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +121,7 @@ class EHentaiUpdateWorker : JobService(), CoroutineScope {
|
|||||||
* extras configured with [ This object serves to identify this specific running job instance when calling][JobInfo.Builder.setExtras]
|
* extras configured with [ This object serves to identify this specific running job instance when calling][JobInfo.Builder.setExtras]
|
||||||
*/
|
*/
|
||||||
override fun onStartJob(params: JobParameters): Boolean {
|
override fun onStartJob(params: JobParameters): Boolean {
|
||||||
launch {
|
scope.launch {
|
||||||
startUpdating()
|
startUpdating()
|
||||||
logger.d("Update job completed!")
|
logger.d("Update job completed!")
|
||||||
jobFinished(params, false)
|
jobFinished(params, false)
|
||||||
@ -145,7 +144,7 @@ class EHentaiUpdateWorker : JobService(), CoroutineScope {
|
|||||||
return@mapNotNull null
|
return@mapNotNull null
|
||||||
}
|
}
|
||||||
|
|
||||||
val meta = db.getFlatMetadataForManga(manga.id!!).executeAsBlocking()
|
val meta = db.getFlatMetadataForManga(manga.id!!).executeOnIO()
|
||||||
?: return@mapNotNull null
|
?: return@mapNotNull null
|
||||||
|
|
||||||
val raisedMeta = meta.raise<EHentaiSearchMetadata>()
|
val raisedMeta = meta.raise<EHentaiSearchMetadata>()
|
||||||
@ -274,7 +273,7 @@ class EHentaiUpdateWorker : JobService(), CoroutineScope {
|
|||||||
return new to db.getChapters(manga).executeOnIO()
|
return new to db.getChapters(manga).executeOnIO()
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
if (t is EHentai.GalleryNotFoundException) {
|
if (t is EHentai.GalleryNotFoundException) {
|
||||||
val meta = db.getFlatMetadataForManga(manga.id!!).executeAsBlocking()?.raise<EHentaiSearchMetadata>()
|
val meta = db.getFlatMetadataForManga(manga.id!!).executeOnIO()?.raise<EHentaiSearchMetadata>()
|
||||||
if (meta != null) {
|
if (meta != null) {
|
||||||
// Age dead galleries
|
// Age dead galleries
|
||||||
logger.d("Aged %s - notfound", manga.id)
|
logger.d("Aged %s - notfound", manga.id)
|
||||||
@ -345,8 +344,7 @@ class EHentaiUpdateWorker : JobService(), CoroutineScope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun launchBackgroundTest(context: Context): String {
|
fun launchBackgroundTest(context: Context): String {
|
||||||
val jobScheduler = context.jobScheduler
|
return if (context.jobScheduler.schedule(context.testBackgroundJobInfo()) == JobScheduler.RESULT_FAILURE) {
|
||||||
return if (jobScheduler.schedule(context.testBackgroundJobInfo()) == JobScheduler.RESULT_FAILURE) {
|
|
||||||
logger.e("Failed to schedule background test job!")
|
logger.e("Failed to schedule background test job!")
|
||||||
"Failed"
|
"Failed"
|
||||||
} else {
|
} else {
|
||||||
|
@ -2,6 +2,8 @@ package exh.metadata.metadata.base
|
|||||||
|
|
||||||
import com.pushtorefresh.storio.operations.PreparedOperation
|
import com.pushtorefresh.storio.operations.PreparedOperation
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||||
|
import eu.kanade.tachiyomi.util.lang.runAsObservable
|
||||||
|
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||||
import exh.metadata.sql.models.SearchMetadata
|
import exh.metadata.sql.models.SearchMetadata
|
||||||
import exh.metadata.sql.models.SearchTag
|
import exh.metadata.sql.models.SearchTag
|
||||||
import exh.metadata.sql.models.SearchTitle
|
import exh.metadata.sql.models.SearchTitle
|
||||||
@ -9,6 +11,9 @@ import exh.util.executeOnIO
|
|||||||
import kotlinx.coroutines.Deferred
|
import kotlinx.coroutines.Deferred
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.serialization.InternalSerializationApi
|
import kotlinx.serialization.InternalSerializationApi
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.serializer
|
import kotlinx.serialization.serializer
|
||||||
@ -32,23 +37,35 @@ data class FlatMetadata(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun DatabaseHelper.getFlatMetadataForManga(mangaId: Long): PreparedOperation<FlatMetadata?> {
|
interface PreparedSuspendOperation<T> : PreparedOperation<T> {
|
||||||
// We have to use fromCallable because StorIO messes up the thread scheduling if we use their rx functions
|
/**
|
||||||
val single = Single.fromCallable {
|
* Creates a [Flow] that emits the result of of Operation
|
||||||
val meta = getSearchMetadataForManga(mangaId).executeAsBlocking()
|
*
|
||||||
|
* Example:
|
||||||
|
* override fun asFlow(): Flow<T> = flow { emit(operation()) }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
fun asFlow(): Flow<T>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes operation asynchronously in the I/O thread pool.
|
||||||
|
*/
|
||||||
|
suspend fun executeOnIO(): T
|
||||||
|
}
|
||||||
|
|
||||||
|
fun DatabaseHelper.getFlatMetadataForManga(mangaId: Long): PreparedSuspendOperation<FlatMetadata?> =
|
||||||
|
preparedOperationFromSuspend {
|
||||||
|
val meta = getSearchMetadataForManga(mangaId).executeOnIO()
|
||||||
if (meta != null) {
|
if (meta != null) {
|
||||||
val tags = getSearchTagsForManga(mangaId).executeAsBlocking()
|
val tags = getSearchTagsForManga(mangaId).executeOnIO()
|
||||||
val titles = getSearchTitlesForManga(mangaId).executeAsBlocking()
|
val titles = getSearchTitlesForManga(mangaId).executeOnIO()
|
||||||
|
|
||||||
FlatMetadata(meta, tags, titles)
|
FlatMetadata(meta, tags, titles)
|
||||||
} else null
|
} else null
|
||||||
}
|
}
|
||||||
|
|
||||||
return preparedOperationFromSingle(single)
|
private fun <T> preparedOperationFromSuspend(operation: suspend () -> T): PreparedSuspendOperation<T> {
|
||||||
}
|
return object : PreparedSuspendOperation<T> {
|
||||||
|
|
||||||
private fun <T> preparedOperationFromSingle(single: Single<T>): PreparedOperation<T> {
|
|
||||||
return object : PreparedOperation<T> {
|
|
||||||
/**
|
/**
|
||||||
* Creates [rx.Observable] that emits result of Operation.
|
* Creates [rx.Observable] that emits result of Operation.
|
||||||
*
|
*
|
||||||
@ -57,7 +74,7 @@ private fun <T> preparedOperationFromSingle(single: Single<T>): PreparedOperatio
|
|||||||
*
|
*
|
||||||
* @return observable result of operation with only one [rx.Observer.onNext] call.
|
* @return observable result of operation with only one [rx.Observer.onNext] call.
|
||||||
*/
|
*/
|
||||||
override fun createObservable() = single.toObservable()
|
override fun createObservable() = runAsObservable(operation)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes operation synchronously in current thread.
|
* Executes operation synchronously in current thread.
|
||||||
@ -66,11 +83,11 @@ private fun <T> preparedOperationFromSingle(single: Single<T>): PreparedOperatio
|
|||||||
* Notice: Blocking I/O operation should not be executed on the Main Thread,
|
* Notice: Blocking I/O operation should not be executed on the Main Thread,
|
||||||
* it can cause ANR (Activity Not Responding dialog), block the UI and drop animations frames.
|
* it can cause ANR (Activity Not Responding dialog), block the UI and drop animations frames.
|
||||||
* So please, execute blocking I/O operation only from background thread.
|
* So please, execute blocking I/O operation only from background thread.
|
||||||
* See [WorkerThread].
|
* See [androidx.annotation.WorkerThread].
|
||||||
*
|
*
|
||||||
* @return nullable result of operation.
|
* @return nullable result of operation.
|
||||||
*/
|
*/
|
||||||
override fun executeAsBlocking() = single.toBlocking().value()
|
override fun executeAsBlocking() = runBlocking { operation() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates [rx.Observable] that emits result of Operation.
|
* Creates [rx.Observable] that emits result of Operation.
|
||||||
@ -80,7 +97,7 @@ private fun <T> preparedOperationFromSingle(single: Single<T>): PreparedOperatio
|
|||||||
*
|
*
|
||||||
* @return observable result of operation with only one [rx.Observer.onNext] call.
|
* @return observable result of operation with only one [rx.Observer.onNext] call.
|
||||||
*/
|
*/
|
||||||
override fun asRxObservable() = single.toObservable()
|
override fun asRxObservable() = runAsObservable(operation)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates [rx.Single] that emits result of Operation lazily when somebody subscribes to it.
|
* Creates [rx.Single] that emits result of Operation lazily when somebody subscribes to it.
|
||||||
@ -89,7 +106,21 @@ private fun <T> preparedOperationFromSingle(single: Single<T>): PreparedOperatio
|
|||||||
*
|
*
|
||||||
* @return single result of operation.
|
* @return single result of operation.
|
||||||
*/
|
*/
|
||||||
override fun asRxSingle() = single
|
override fun asRxSingle(): Single<T> = runAsObservable(operation).toSingle()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a [Flow] that emits the result of of Operation
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* override fun asFlow(): Flow<T> = flow { emit(operation()) }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
override fun asFlow(): Flow<T> = flow { emit(operation()) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes operation asynchronously in the I/O thread pool.
|
||||||
|
*/
|
||||||
|
override suspend fun executeOnIO(): T = withIOContext { operation() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ import eu.kanade.tachiyomi.data.database.models.Manga
|
|||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import eu.kanade.tachiyomi.source.online.MetadataSource
|
import eu.kanade.tachiyomi.source.online.MetadataSource
|
||||||
import eu.kanade.tachiyomi.util.lang.asFlow
|
|
||||||
import exh.metadata.metadata.base.FlatMetadata
|
import exh.metadata.metadata.base.FlatMetadata
|
||||||
import exh.metadata.metadata.base.RaisedSearchMetadata
|
import exh.metadata.metadata.base.RaisedSearchMetadata
|
||||||
import exh.metadata.metadata.base.getFlatMetadataForManga
|
import exh.metadata.metadata.base.getFlatMetadataForManga
|
||||||
@ -16,7 +15,6 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.plus
|
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
@ -50,6 +48,6 @@ class MetadataViewPresenter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getMangaMetaObservable(): Flow<FlatMetadata?> {
|
private fun getMangaMetaObservable(): Flow<FlatMetadata?> {
|
||||||
return db.getFlatMetadataForManga(manga.id!!).asRxObservable().asFlow()
|
return db.getFlatMetadataForManga(manga.id!!).asFlow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package exh.util
|
package exh.util
|
||||||
|
|
||||||
import com.pushtorefresh.storio.operations.PreparedOperation
|
import android.database.Cursor
|
||||||
|
import com.pushtorefresh.storio.sqlite.operations.get.PreparedGetCursor
|
||||||
import com.pushtorefresh.storio.sqlite.operations.get.PreparedGetListOfObjects
|
import com.pushtorefresh.storio.sqlite.operations.get.PreparedGetListOfObjects
|
||||||
import com.pushtorefresh.storio.sqlite.operations.get.PreparedGetObject
|
import com.pushtorefresh.storio.sqlite.operations.get.PreparedGetObject
|
||||||
import com.pushtorefresh.storio.sqlite.operations.put.PreparedPutCollectionOfObjects
|
import com.pushtorefresh.storio.sqlite.operations.put.PreparedPutCollectionOfObjects
|
||||||
@ -18,4 +19,4 @@ suspend fun <T> PreparedPutObject<T>.executeOnIO(): PutResult = withContext(Disp
|
|||||||
|
|
||||||
suspend fun <T> PreparedPutCollectionOfObjects<T>.executeOnIO(): PutResults<T> = withContext(Dispatchers.IO) { executeAsBlocking() }
|
suspend fun <T> PreparedPutCollectionOfObjects<T>.executeOnIO(): PutResults<T> = withContext(Dispatchers.IO) { executeAsBlocking() }
|
||||||
|
|
||||||
suspend fun <T> PreparedOperation<T>.executeOnIO(): T? = withContext(Dispatchers.IO) { executeAsBlocking() }
|
suspend fun PreparedGetCursor.executeOnIO(): Cursor = withContext(Dispatchers.IO) { executeAsBlocking() }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user