Remove RxJava in PageHolder (#9103)
Inline readImageHeaderSubscription in PageHolder Inline readImageHeaderSubscription in PagerPageHolder and WebtoonPageHolder by converting setImage() into a suspend function. The image processing runs in the loadPageAndProcessStatus continuation. Use suspendCancellableCoroutine as a substitute for doOnUnsubscribe in WebtoonPageHolder. Closing openStream after the frame.setImage but before the PageHolder is recycled causes the page display to fail for reasons that are not currently understood. Remove subscription handling from WebtoonViewer/WebtoonBaseHolder as it is no longer used.
This commit is contained in:
parent
0ef7650c1a
commit
ffa8c8fd07
@ -20,11 +20,9 @@ import kotlinx.coroutines.MainScope
|
|||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.supervisorScope
|
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 tachiyomi.core.util.lang.launchIO
|
||||||
|
import tachiyomi.core.util.lang.withIOContext
|
||||||
|
import tachiyomi.core.util.lang.withUIContext
|
||||||
import java.io.BufferedInputStream
|
import java.io.BufferedInputStream
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
@ -66,12 +64,6 @@ class PagerPageHolder(
|
|||||||
*/
|
*/
|
||||||
private var loadJob: Job? = null
|
private var loadJob: 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).
|
|
||||||
*/
|
|
||||||
private var readImageHeaderSubscription: Subscription? = null
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
addView(progressIndicator)
|
addView(progressIndicator)
|
||||||
loadJob = scope.launch { loadPageAndProcessStatus() }
|
loadJob = scope.launch { loadPageAndProcessStatus() }
|
||||||
@ -85,7 +77,6 @@ class PagerPageHolder(
|
|||||||
super.onDetachedFromWindow()
|
super.onDetachedFromWindow()
|
||||||
loadJob?.cancel()
|
loadJob?.cancel()
|
||||||
loadJob = null
|
loadJob = null
|
||||||
unsubscribeReadImageHeader()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -119,14 +110,6 @@ class PagerPageHolder(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Unsubscribes from the read image header subscription.
|
|
||||||
*/
|
|
||||||
private fun unsubscribeReadImageHeader() {
|
|
||||||
readImageHeaderSubscription?.unsubscribe()
|
|
||||||
readImageHeaderSubscription = null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the page is queued.
|
* Called when the page is queued.
|
||||||
*/
|
*/
|
||||||
@ -154,19 +137,16 @@ class PagerPageHolder(
|
|||||||
/**
|
/**
|
||||||
* Called when the page is ready.
|
* Called when the page is ready.
|
||||||
*/
|
*/
|
||||||
private fun setImage() {
|
private suspend fun setImage() {
|
||||||
progressIndicator.setProgress(0)
|
progressIndicator.setProgress(0)
|
||||||
errorLayout?.root?.isVisible = false
|
errorLayout?.root?.isVisible = false
|
||||||
|
|
||||||
unsubscribeReadImageHeader()
|
|
||||||
val streamFn = page.stream ?: return
|
val streamFn = page.stream ?: return
|
||||||
|
|
||||||
readImageHeaderSubscription = Observable
|
val (bais, isAnimated, background) = withIOContext {
|
||||||
.fromCallable {
|
streamFn().buffered(16).use { stream ->
|
||||||
val stream = streamFn().buffered(16)
|
process(item, stream).use { itemStream ->
|
||||||
val itemStream = process(item, stream)
|
|
||||||
val bais = ByteArrayInputStream(itemStream.readBytes())
|
val bais = ByteArrayInputStream(itemStream.readBytes())
|
||||||
try {
|
|
||||||
val isAnimated = ImageUtil.isAnimatedAndSupported(bais)
|
val isAnimated = ImageUtil.isAnimatedAndSupported(bais)
|
||||||
bais.reset()
|
bais.reset()
|
||||||
val background = if (!isAnimated && viewer.config.automaticBackground) {
|
val background = if (!isAnimated && viewer.config.automaticBackground) {
|
||||||
@ -176,14 +156,10 @@ class PagerPageHolder(
|
|||||||
}
|
}
|
||||||
bais.reset()
|
bais.reset()
|
||||||
Triple(bais, isAnimated, background)
|
Triple(bais, isAnimated, background)
|
||||||
} finally {
|
|
||||||
stream.close()
|
|
||||||
itemStream.close()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.subscribeOn(Schedulers.io())
|
}
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
withUIContext {
|
||||||
.doOnNext { (bais, isAnimated, background) ->
|
|
||||||
bais.use {
|
bais.use {
|
||||||
setImage(
|
setImage(
|
||||||
it,
|
it,
|
||||||
@ -201,7 +177,6 @@ class PagerPageHolder(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.subscribe({}, {})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun process(page: ReaderPage, imageStream: BufferedInputStream): InputStream {
|
private fun process(page: ReaderPage, imageStream: BufferedInputStream): InputStream {
|
||||||
|
@ -4,7 +4,6 @@ import android.content.Context
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup.LayoutParams
|
import android.view.ViewGroup.LayoutParams
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import rx.Subscription
|
|
||||||
|
|
||||||
abstract class WebtoonBaseHolder(
|
abstract class WebtoonBaseHolder(
|
||||||
view: View,
|
view: View,
|
||||||
@ -21,21 +20,6 @@ abstract class WebtoonBaseHolder(
|
|||||||
*/
|
*/
|
||||||
open fun recycle() {}
|
open fun recycle() {}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a subscription to a list of subscriptions that will automatically unsubscribe when the
|
|
||||||
* activity or the reader is destroyed.
|
|
||||||
*/
|
|
||||||
protected fun addSubscription(subscription: Subscription?) {
|
|
||||||
viewer.subscriptions.add(subscription)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes a subscription from the list of subscriptions.
|
|
||||||
*/
|
|
||||||
protected fun removeSubscription(subscription: Subscription?) {
|
|
||||||
subscription?.let { viewer.subscriptions.remove(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extension method to set layout params to wrap content on this view.
|
* Extension method to set layout params to wrap content on this view.
|
||||||
*/
|
*/
|
||||||
|
@ -25,11 +25,10 @@ import kotlinx.coroutines.MainScope
|
|||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.supervisorScope
|
import kotlinx.coroutines.supervisorScope
|
||||||
import rx.Observable
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import rx.Subscription
|
|
||||||
import rx.android.schedulers.AndroidSchedulers
|
|
||||||
import rx.schedulers.Schedulers
|
|
||||||
import tachiyomi.core.util.lang.launchIO
|
import tachiyomi.core.util.lang.launchIO
|
||||||
|
import tachiyomi.core.util.lang.withIOContext
|
||||||
|
import tachiyomi.core.util.lang.withUIContext
|
||||||
import java.io.BufferedInputStream
|
import java.io.BufferedInputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
||||||
@ -79,12 +78,6 @@ class WebtoonPageHolder(
|
|||||||
*/
|
*/
|
||||||
private var loadJob: Job? = null
|
private var loadJob: 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).
|
|
||||||
*/
|
|
||||||
private var readImageHeaderSubscription: Subscription? = null
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
refreshLayoutParams()
|
refreshLayoutParams()
|
||||||
|
|
||||||
@ -121,7 +114,6 @@ class WebtoonPageHolder(
|
|||||||
override fun recycle() {
|
override fun recycle() {
|
||||||
loadJob?.cancel()
|
loadJob?.cancel()
|
||||||
loadJob = null
|
loadJob = null
|
||||||
unsubscribeReadImageHeader()
|
|
||||||
|
|
||||||
removeErrorLayout()
|
removeErrorLayout()
|
||||||
frame.recycle()
|
frame.recycle()
|
||||||
@ -159,14 +151,6 @@ class WebtoonPageHolder(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Unsubscribes from the read image header subscription.
|
|
||||||
*/
|
|
||||||
private fun unsubscribeReadImageHeader() {
|
|
||||||
removeSubscription(readImageHeaderSubscription)
|
|
||||||
readImageHeaderSubscription = null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the page is queued.
|
* Called when the page is queued.
|
||||||
*/
|
*/
|
||||||
@ -197,26 +181,22 @@ class WebtoonPageHolder(
|
|||||||
/**
|
/**
|
||||||
* Called when the page is ready.
|
* Called when the page is ready.
|
||||||
*/
|
*/
|
||||||
private fun setImage() {
|
private suspend fun setImage() {
|
||||||
progressIndicator.setProgress(0)
|
progressIndicator.setProgress(0)
|
||||||
removeErrorLayout()
|
removeErrorLayout()
|
||||||
|
|
||||||
unsubscribeReadImageHeader()
|
|
||||||
val streamFn = page?.stream ?: return
|
val streamFn = page?.stream ?: return
|
||||||
|
|
||||||
var openStream: InputStream? = null
|
val (openStream, isAnimated) = withIOContext {
|
||||||
readImageHeaderSubscription = Observable
|
|
||||||
.fromCallable {
|
|
||||||
val stream = streamFn().buffered(16)
|
val stream = streamFn().buffered(16)
|
||||||
openStream = process(stream)
|
val openStream = process(stream)
|
||||||
|
|
||||||
ImageUtil.isAnimatedAndSupported(stream)
|
val isAnimated = ImageUtil.isAnimatedAndSupported(stream)
|
||||||
|
Pair(openStream, isAnimated)
|
||||||
}
|
}
|
||||||
.subscribeOn(Schedulers.io())
|
withUIContext {
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.doOnNext { isAnimated ->
|
|
||||||
frame.setImage(
|
frame.setImage(
|
||||||
openStream!!,
|
openStream,
|
||||||
isAnimated,
|
isAnimated,
|
||||||
ReaderPageImageView.Config(
|
ReaderPageImageView.Config(
|
||||||
zoomDuration = viewer.config.doubleTapAnimDuration,
|
zoomDuration = viewer.config.doubleTapAnimDuration,
|
||||||
@ -225,12 +205,10 @@ class WebtoonPageHolder(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// Keep the Rx stream alive to close the input stream only when unsubscribed
|
// Suspend the coroutine to close the input stream only when the WebtoonPageHolder is recycled
|
||||||
.flatMap { Observable.never<Unit>() }
|
suspendCancellableCoroutine<Nothing> { continuation ->
|
||||||
.doOnUnsubscribe { openStream?.close() }
|
continuation.invokeOnCancellation { openStream.close() }
|
||||||
.subscribe({}, {})
|
}
|
||||||
|
|
||||||
addSubscription(readImageHeaderSubscription)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun process(imageStream: BufferedInputStream): InputStream {
|
private fun process(imageStream: BufferedInputStream): InputStream {
|
||||||
|
@ -22,7 +22,6 @@ import eu.kanade.tachiyomi.ui.reader.viewer.BaseViewer
|
|||||||
import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation.NavigationRegion
|
import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation.NavigationRegion
|
||||||
import kotlinx.coroutines.MainScope
|
import kotlinx.coroutines.MainScope
|
||||||
import kotlinx.coroutines.cancel
|
import kotlinx.coroutines.cancel
|
||||||
import rx.subscriptions.CompositeSubscription
|
|
||||||
import tachiyomi.core.util.system.logcat
|
import tachiyomi.core.util.system.logcat
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
@ -74,11 +73,6 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr
|
|||||||
*/
|
*/
|
||||||
private var currentPage: Any? = null
|
private var currentPage: Any? = null
|
||||||
|
|
||||||
/**
|
|
||||||
* Subscriptions to keep while this viewer is used.
|
|
||||||
*/
|
|
||||||
val subscriptions = CompositeSubscription()
|
|
||||||
|
|
||||||
private val threshold: Int =
|
private val threshold: Int =
|
||||||
Injekt.get<ReaderPreferences>()
|
Injekt.get<ReaderPreferences>()
|
||||||
.readerHideThreshold()
|
.readerHideThreshold()
|
||||||
@ -196,7 +190,6 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr
|
|||||||
override fun destroy() {
|
override fun destroy() {
|
||||||
super.destroy()
|
super.destroy()
|
||||||
scope.cancel()
|
scope.cancel()
|
||||||
subscriptions.unsubscribe()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user