Improve Loading Speed When Skipping Pages in a Chapter (#2426)

* cancel queued loads when the page that requested the queue is destroyed

* use page.status for optimizing removal

(cherry picked from commit dd1e6402c9)
This commit is contained in:
MCAxiaz 2020-01-05 09:18:02 -08:00 committed by arkon
parent aa57b1bc77
commit cae04656b9

View File

@ -17,6 +17,7 @@ import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.util.concurrent.PriorityBlockingQueue import java.util.concurrent.PriorityBlockingQueue
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import kotlin.math.min
/** /**
* Loader used to load chapters from an online source. * Loader used to load chapters from an online source.
@ -37,18 +38,20 @@ class HttpPageLoader(
*/ */
private val subscriptions = CompositeSubscription() private val subscriptions = CompositeSubscription()
private val preloadSize = 4
init { init {
subscriptions += Observable.defer { Observable.just(queue.take().page) } subscriptions += Observable.defer { Observable.just(queue.take().page) }
.filter { it.status == Page.QUEUE } .filter { it.status == Page.QUEUE }
.concatMap { source.fetchImageFromCacheThenNet(it) } .concatMap { source.fetchImageFromCacheThenNet(it) }
.repeat() .repeat()
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.subscribe({ .subscribe({
}, { error -> }, { error ->
if (error !is InterruptedException) { if (error !is InterruptedException) {
Timber.e(error) Timber.e(error)
} }
}) })
} }
/** /**
@ -80,13 +83,13 @@ class HttpPageLoader(
*/ */
override fun getPages(): Observable<List<ReaderPage>> { override fun getPages(): Observable<List<ReaderPage>> {
return chapterCache return chapterCache
.getPageListFromCache(chapter.chapter) .getPageListFromCache(chapter.chapter)
.onErrorResumeNext { source.fetchPageList(chapter.chapter) } .onErrorResumeNext { source.fetchPageList(chapter.chapter) }
.map { pages -> .map { pages ->
pages.mapIndexed { index, page -> // Don't trust sources and use our own indexing pages.mapIndexed { index, page -> // Don't trust sources and use our own indexing
ReaderPage(index, page.url, page.imageUrl) ReaderPage(index, page.url, page.imageUrl)
}
} }
}
} }
/** /**
@ -110,29 +113,41 @@ class HttpPageLoader(
val statusSubject = SerializedSubject(PublishSubject.create<Int>()) val statusSubject = SerializedSubject(PublishSubject.create<Int>())
page.setStatusSubject(statusSubject) page.setStatusSubject(statusSubject)
val queuedPages = mutableListOf<PriorityPage>()
if (page.status == Page.QUEUE) { if (page.status == Page.QUEUE) {
queue.offer(PriorityPage(page, 1)) queuedPages += PriorityPage(page, 1).also { queue.offer(it) }
} }
queuedPages += preloadNextPages(page, preloadSize)
preloadNextPages(page, 4)
statusSubject.startWith(page.status) statusSubject.startWith(page.status)
.doOnUnsubscribe {
queuedPages.forEach {
if (it.page.status == Page.QUEUE) {
queue.remove(it)
}
}
}
} }
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
} }
/** /**
* Preloads the given [amount] of pages after the [currentPage] with a lower priority. * Preloads the given [amount] of pages after the [currentPage] with a lower priority.
* @return a list of [PriorityPage] that were added to the [queue]
*/ */
private fun preloadNextPages(currentPage: ReaderPage, amount: Int) { private fun preloadNextPages(currentPage: ReaderPage, amount: Int): List<PriorityPage> {
val pageIndex = currentPage.index val pageIndex = currentPage.index
val pages = currentPage.chapter.pages ?: return val pages = currentPage.chapter.pages ?: return emptyList()
if (pageIndex == pages.lastIndex) return if (pageIndex == pages.lastIndex) return emptyList()
val nextPages = pages.subList(pageIndex + 1, Math.min(pageIndex + 1 + amount, pages.size))
for (nextPage in nextPages) { return pages
if (nextPage.status == Page.QUEUE) { .subList(pageIndex + 1, min(pageIndex + 1 + amount, pages.size))
queue.offer(PriorityPage(nextPage, 0)) .mapNotNull {
} if (it.status == Page.QUEUE) {
} PriorityPage(it, 0).apply { queue.offer(this) }
} else null
}
} }
/** /**
@ -148,7 +163,7 @@ class HttpPageLoader(
/** /**
* Data class used to keep ordering of pages in order to maintain priority. * Data class used to keep ordering of pages in order to maintain priority.
*/ */
private data class PriorityPage( private class PriorityPage(
val page: ReaderPage, val page: ReaderPage,
val priority: Int val priority: Int
): Comparable<PriorityPage> { ): Comparable<PriorityPage> {