Simplify PageHolder load Job (#9086)
Inline statusJob into loadJob, using supervisorScope to load the page and track status changes in parallel. - supervisorScope does not complete until both the child loadPage coroutine and statusFlow.collectLatest have completed. - Cancelling supervisorScope cancels the child loadPage coroutine and statusFlow.collectLatest. - Use supervisorScope instead of coroutineScope to let status collection continue if loadPage fails. Inline progressJob into loadJob, using collectLatest's cancellation to avoid cancelling the progressFlow collection explicitly. - collectLatest cancels the previous action block when the flow emits a new value. This means the DOWNLOAD_IMAGE progressFlow.collectLatest gets automatically cancelled when statusFlow emits a new state. Convert launchLoadJob to suspend function, move job launch to caller, and rename as loadPageAndProcessStatus. (cherry picked from commit 4635e58405eedce0b49fc69f7ccb190a7c600da9) # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt
This commit is contained in:
parent
57113014ec
commit
288fe0d888
@ -20,18 +20,19 @@ import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.supervisorScope
|
||||
import logcat.LogPriority
|
||||
import rx.Observable
|
||||
import rx.Subscription
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.schedulers.Schedulers
|
||||
import tachiyomi.core.util.lang.launchIO
|
||||
import tachiyomi.core.util.system.logcat
|
||||
import tachiyomi.decoder.ImageDecoder
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.InputStream
|
||||
import kotlin.math.max
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* View of the ViewPager that contains a page of a chapter.
|
||||
@ -67,35 +68,15 @@ class PagerPageHolder(
|
||||
private val scope = MainScope()
|
||||
|
||||
/**
|
||||
* Job for loading the page.
|
||||
* Job for loading the page and processing changes to the page's status.
|
||||
*/
|
||||
private var loadJob: Job? = null
|
||||
|
||||
/**
|
||||
* Job for status changes of the page.
|
||||
*/
|
||||
private var statusJob: Job? = null
|
||||
|
||||
/**
|
||||
* Job for progress changes of the page.
|
||||
*/
|
||||
private var progressJob: Job? = null
|
||||
|
||||
/**
|
||||
* Job for loading the page.
|
||||
*/
|
||||
private var extraLoadJob: Job? = null
|
||||
|
||||
/**
|
||||
* Job for status changes of the page.
|
||||
*/
|
||||
private var extraStatusJob: Job? = null
|
||||
|
||||
/**
|
||||
* Job for progress changes of the page.
|
||||
*/
|
||||
private var extraProgressJob: Job? = null
|
||||
|
||||
/**
|
||||
* Subscription used to read the header of the image. This is needed in order to instantiate
|
||||
* the appropiate image view depending if the image is animated (GIF).
|
||||
@ -104,7 +85,8 @@ class PagerPageHolder(
|
||||
|
||||
init {
|
||||
addView(progressIndicator)
|
||||
launchLoadJob()
|
||||
loadJob = scope.launch { loadPageAndProcessStatus(1) }
|
||||
extraLoadJob = scope.launch { loadPageAndProcessStatus(2) }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -113,130 +95,45 @@ class PagerPageHolder(
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
cancelProgressJob(1)
|
||||
cancelProgressJob(2)
|
||||
cancelLoadJob(1)
|
||||
cancelLoadJob(2)
|
||||
loadJob?.cancel()
|
||||
loadJob = null
|
||||
extraLoadJob?.cancel()
|
||||
extraLoadJob = null
|
||||
unsubscribeReadImageHeader()
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts loading the page and processing changes to the page's status.
|
||||
* Loads the page and processes changes to the page's status.
|
||||
*
|
||||
* @see processStatus
|
||||
* Returns immediately if the page has no PageLoader.
|
||||
* Otherwise, this function does not return. It will continue to process status changes until
|
||||
* the Job is cancelled.
|
||||
*/
|
||||
private fun launchLoadJob() {
|
||||
loadJob?.cancel()
|
||||
statusJob?.cancel()
|
||||
|
||||
private suspend fun loadPageAndProcessStatus(pageIndex: Int) {
|
||||
// SY -->
|
||||
val page = if (pageIndex == 1) page else extraPage
|
||||
page ?: return
|
||||
// SY <--
|
||||
val loader = page.chapter.pageLoader ?: return
|
||||
loadJob = scope.launch {
|
||||
supervisorScope {
|
||||
launchIO {
|
||||
loader.loadPage(page)
|
||||
}
|
||||
statusJob = scope.launch {
|
||||
page.statusFlow.collectLatest { processStatus(it) }
|
||||
}
|
||||
|
||||
// SY -->
|
||||
val extraPage = extraPage ?: return
|
||||
val loader2 = extraPage.chapter.pageLoader ?: return
|
||||
extraLoadJob = scope.launch {
|
||||
loader2.loadPage(extraPage)
|
||||
}
|
||||
extraStatusJob = scope.launch {
|
||||
extraPage.statusFlow.collectLatest { processStatus2(it) }
|
||||
}
|
||||
// SY <--
|
||||
}
|
||||
|
||||
private fun launchProgressJob() {
|
||||
progressJob?.cancel()
|
||||
progressJob = scope.launch {
|
||||
page.progressFlow.collectLatest { value -> progressIndicator.setProgress(value) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchProgressJob2() {
|
||||
extraProgressJob?.cancel()
|
||||
val extraPage = extraPage ?: return
|
||||
extraProgressJob = scope.launch {
|
||||
extraPage.progressFlow.collectLatest { value -> progressIndicator.setProgress(((page.progressFlow.value + value) / 2 * 0.95f).roundToInt()) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the status of the page changes.
|
||||
*
|
||||
* @param status the new status of the page.
|
||||
*/
|
||||
private fun processStatus(status: Page.State) {
|
||||
when (status) {
|
||||
page.statusFlow.collectLatest { state ->
|
||||
when (state) {
|
||||
Page.State.QUEUE -> setQueued()
|
||||
Page.State.LOAD_PAGE -> setLoading()
|
||||
Page.State.DOWNLOAD_IMAGE -> {
|
||||
launchProgressJob()
|
||||
setDownloading()
|
||||
page.progressFlow.collectLatest { value ->
|
||||
progressIndicator.setProgress(value)
|
||||
}
|
||||
Page.State.READY -> {
|
||||
if (extraPage?.status == Page.State.READY || extraPage == null) {
|
||||
setImage()
|
||||
}
|
||||
cancelProgressJob(1)
|
||||
}
|
||||
Page.State.ERROR -> {
|
||||
setError()
|
||||
cancelProgressJob(1)
|
||||
Page.State.READY -> setImage()
|
||||
Page.State.ERROR -> setError()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the status of the page changes.
|
||||
*
|
||||
* @param status the new status of the page.
|
||||
*/
|
||||
private fun processStatus2(status: Page.State) {
|
||||
when (status) {
|
||||
Page.State.QUEUE -> setQueued()
|
||||
Page.State.LOAD_PAGE -> setLoading()
|
||||
Page.State.DOWNLOAD_IMAGE -> {
|
||||
launchProgressJob2()
|
||||
setDownloading()
|
||||
}
|
||||
Page.State.READY -> {
|
||||
if (page.status == Page.State.READY) {
|
||||
setImage()
|
||||
}
|
||||
cancelProgressJob(2)
|
||||
}
|
||||
Page.State.ERROR -> {
|
||||
setError()
|
||||
cancelProgressJob(2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels loading the page and processing changes to the page's status.
|
||||
*/
|
||||
private fun cancelLoadJob(page: Int) {
|
||||
if (page == 1) {
|
||||
loadJob?.cancel()
|
||||
loadJob = null
|
||||
statusJob?.cancel()
|
||||
statusJob = null
|
||||
} else if (page == 2) {
|
||||
extraLoadJob?.cancel()
|
||||
extraLoadJob = null
|
||||
extraStatusJob?.cancel()
|
||||
extraStatusJob = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun cancelProgressJob(page: Int) {
|
||||
val job = if (page == 1) progressJob else extraProgressJob
|
||||
job?.cancel()
|
||||
if (page == 1) progressJob = null else extraProgressJob = null
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -24,10 +24,12 @@ import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.supervisorScope
|
||||
import rx.Observable
|
||||
import rx.Subscription
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.schedulers.Schedulers
|
||||
import tachiyomi.core.util.lang.launchIO
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.InputStream
|
||||
|
||||
@ -77,16 +79,6 @@ class WebtoonPageHolder(
|
||||
*/
|
||||
private var loadJob: Job? = null
|
||||
|
||||
/**
|
||||
* Job for status changes of the page.
|
||||
*/
|
||||
private var statusJob: Job? = null
|
||||
|
||||
/**
|
||||
* Job for progress changes of the page.
|
||||
*/
|
||||
private var progressJob: Job? = null
|
||||
|
||||
/**
|
||||
* Subscription used to read the header of the image. This is needed in order to instantiate
|
||||
* the appropriate image view depending if the image is animated (GIF).
|
||||
@ -106,7 +98,8 @@ class WebtoonPageHolder(
|
||||
*/
|
||||
fun bind(page: ReaderPage) {
|
||||
this.page = page
|
||||
launchLoadJob()
|
||||
loadJob?.cancel()
|
||||
loadJob = scope.launch { loadPageAndProcessStatus() }
|
||||
refreshLayoutParams()
|
||||
}
|
||||
|
||||
@ -126,8 +119,8 @@ class WebtoonPageHolder(
|
||||
* Called when the view is recycled and added to the view pool.
|
||||
*/
|
||||
override fun recycle() {
|
||||
cancelLoadJob()
|
||||
cancelProgressJob()
|
||||
loadJob?.cancel()
|
||||
loadJob = null
|
||||
unsubscribeReadImageHeader()
|
||||
|
||||
removeErrorLayout()
|
||||
@ -136,76 +129,34 @@ class WebtoonPageHolder(
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts loading the page and processing changes to the page's status.
|
||||
* Loads the page and processes changes to the page's status.
|
||||
*
|
||||
* @see processStatus
|
||||
* Returns immediately if there is no page or the page has no PageLoader.
|
||||
* Otherwise, this function does not return. It will continue to process status changes until
|
||||
* the Job is cancelled.
|
||||
*/
|
||||
private fun launchLoadJob() {
|
||||
cancelLoadJob()
|
||||
|
||||
private suspend fun loadPageAndProcessStatus() {
|
||||
val page = page ?: return
|
||||
val loader = page.chapter.pageLoader ?: return
|
||||
loadJob = scope.launch {
|
||||
supervisorScope {
|
||||
launchIO {
|
||||
loader.loadPage(page)
|
||||
}
|
||||
statusJob = scope.launch {
|
||||
page.statusFlow.collectLatest { processStatus(it) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes the progress of the page and updates view.
|
||||
*/
|
||||
private fun launchProgressJob() {
|
||||
cancelProgressJob()
|
||||
|
||||
val page = page ?: return
|
||||
|
||||
progressJob = scope.launch {
|
||||
page.progressFlow.collectLatest { value -> progressIndicator.setProgress(value) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the status of the page changes.
|
||||
*
|
||||
* @param status the new status of the page.
|
||||
*/
|
||||
private fun processStatus(status: Page.State) {
|
||||
when (status) {
|
||||
page.statusFlow.collectLatest { state ->
|
||||
when (state) {
|
||||
Page.State.QUEUE -> setQueued()
|
||||
Page.State.LOAD_PAGE -> setLoading()
|
||||
Page.State.DOWNLOAD_IMAGE -> {
|
||||
launchProgressJob()
|
||||
setDownloading()
|
||||
page.progressFlow.collectLatest { value ->
|
||||
progressIndicator.setProgress(value)
|
||||
}
|
||||
Page.State.READY -> {
|
||||
setImage()
|
||||
cancelProgressJob()
|
||||
}
|
||||
Page.State.ERROR -> {
|
||||
setError()
|
||||
cancelProgressJob()
|
||||
Page.State.READY -> setImage()
|
||||
Page.State.ERROR -> setError()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels loading the page and processing changes to the page's status.
|
||||
*/
|
||||
private fun cancelLoadJob() {
|
||||
loadJob?.cancel()
|
||||
loadJob = null
|
||||
statusJob?.cancel()
|
||||
statusJob = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribes from the progress subscription.
|
||||
*/
|
||||
private fun cancelProgressJob() {
|
||||
progressJob?.cancel()
|
||||
progressJob = null
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user