mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-04 08:08:55 +01:00 
			
		
		
		
	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.
This commit is contained in:
		@@ -19,10 +19,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.ByteArrayInputStream
 | 
			
		||||
import java.io.InputStream
 | 
			
		||||
@@ -60,20 +62,10 @@ 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
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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).
 | 
			
		||||
@@ -82,7 +74,7 @@ class PagerPageHolder(
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        addView(progressIndicator)
 | 
			
		||||
        launchLoadJob()
 | 
			
		||||
        loadJob = scope.launch { loadPageAndProcessStatus() }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -91,75 +83,42 @@ class PagerPageHolder(
 | 
			
		||||
    @SuppressLint("ClickableViewAccessibility")
 | 
			
		||||
    override fun onDetachedFromWindow() {
 | 
			
		||||
        super.onDetachedFromWindow()
 | 
			
		||||
        cancelProgressJob()
 | 
			
		||||
        cancelLoadJob()
 | 
			
		||||
        loadJob?.cancel()
 | 
			
		||||
        loadJob = 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() {
 | 
			
		||||
        val loader = page.chapter.pageLoader ?: return
 | 
			
		||||
        loadJob = scope.launch {
 | 
			
		||||
            loader.loadPage(page)
 | 
			
		||||
        }
 | 
			
		||||
        statusJob = scope.launch {
 | 
			
		||||
            page.statusFlow.collectLatest { processStatus(it) }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun launchProgressJob() {
 | 
			
		||||
        progressJob?.cancel()
 | 
			
		||||
        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.State.QUEUE -> setQueued()
 | 
			
		||||
            Page.State.LOAD_PAGE -> setLoading()
 | 
			
		||||
            Page.State.DOWNLOAD_IMAGE -> {
 | 
			
		||||
                launchProgressJob()
 | 
			
		||||
                setDownloading()
 | 
			
		||||
        supervisorScope {
 | 
			
		||||
            launchIO {
 | 
			
		||||
                loader.loadPage(page)
 | 
			
		||||
            }
 | 
			
		||||
            Page.State.READY -> {
 | 
			
		||||
                setImage()
 | 
			
		||||
                cancelProgressJob()
 | 
			
		||||
            }
 | 
			
		||||
            Page.State.ERROR -> {
 | 
			
		||||
                setError()
 | 
			
		||||
                cancelProgressJob()
 | 
			
		||||
            page.statusFlow.collectLatest { state ->
 | 
			
		||||
                when (state) {
 | 
			
		||||
                    Page.State.QUEUE -> setQueued()
 | 
			
		||||
                    Page.State.LOAD_PAGE -> setLoading()
 | 
			
		||||
                    Page.State.DOWNLOAD_IMAGE -> {
 | 
			
		||||
                        setDownloading()
 | 
			
		||||
                        page.progressFlow.collectLatest { value ->
 | 
			
		||||
                            progressIndicator.setProgress(value)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    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
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun cancelProgressJob() {
 | 
			
		||||
        progressJob?.cancel()
 | 
			
		||||
        progressJob = null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Unsubscribes from the read image header subscription.
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -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,78 +129,36 @@ 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 {
 | 
			
		||||
            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.State.QUEUE -> setQueued()
 | 
			
		||||
            Page.State.LOAD_PAGE -> setLoading()
 | 
			
		||||
            Page.State.DOWNLOAD_IMAGE -> {
 | 
			
		||||
                launchProgressJob()
 | 
			
		||||
                setDownloading()
 | 
			
		||||
        supervisorScope {
 | 
			
		||||
            launchIO {
 | 
			
		||||
                loader.loadPage(page)
 | 
			
		||||
            }
 | 
			
		||||
            Page.State.READY -> {
 | 
			
		||||
                setImage()
 | 
			
		||||
                cancelProgressJob()
 | 
			
		||||
            }
 | 
			
		||||
            Page.State.ERROR -> {
 | 
			
		||||
                setError()
 | 
			
		||||
                cancelProgressJob()
 | 
			
		||||
            page.statusFlow.collectLatest { state ->
 | 
			
		||||
                when (state) {
 | 
			
		||||
                    Page.State.QUEUE -> setQueued()
 | 
			
		||||
                    Page.State.LOAD_PAGE -> setLoading()
 | 
			
		||||
                    Page.State.DOWNLOAD_IMAGE -> {
 | 
			
		||||
                        setDownloading()
 | 
			
		||||
                        page.progressFlow.collectLatest { value ->
 | 
			
		||||
                            progressIndicator.setProgress(value)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    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
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Unsubscribes from the read image header subscription.
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user