Address coroutine scope leaks in custom views

This commit is contained in:
arkon 2021-01-07 19:16:26 -05:00
parent b18a794eca
commit 8e613d03e3
5 changed files with 33 additions and 21 deletions

View File

@ -10,15 +10,10 @@ import eu.davidea.flexibleadapter.items.IFlexible
import eu.davidea.viewholders.FlexibleViewHolder import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.Filter
import kotlinx.coroutines.MainScope import eu.kanade.tachiyomi.widget.SimpleTextWatcher
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.widget.textChanges
open class TextItem(val filter: Filter.Text) : AbstractFlexibleItem<TextItem.Holder>() { open class TextItem(val filter: Filter.Text) : AbstractFlexibleItem<TextItem.Holder>() {
private val scope = MainScope()
override fun getLayoutRes(): Int { override fun getLayoutRes(): Int {
return R.layout.navigation_view_text return R.layout.navigation_view_text
} }
@ -30,9 +25,11 @@ open class TextItem(val filter: Filter.Text) : AbstractFlexibleItem<TextItem.Hol
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>, holder: Holder, position: Int, payloads: List<Any?>?) { override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>, holder: Holder, position: Int, payloads: List<Any?>?) {
holder.wrapper.hint = filter.name holder.wrapper.hint = filter.name
holder.edit.setText(filter.state) holder.edit.setText(filter.state)
holder.edit.textChanges() holder.edit.addTextChangedListener(object : SimpleTextWatcher() {
.onEach { filter.state = it.toString() } override fun onTextChanged(text: CharSequence, start: Int, before: Int, count: Int) {
.launchIn(scope) filter.state = text.toString()
}
})
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {

View File

@ -97,7 +97,7 @@ class LibraryAdapter(private val controller: LibraryController) : RecyclerViewPa
fun onDestroy() { fun onDestroy() {
for (view in boundViews) { for (view in boundViews) {
if (view is LibraryCategoryView) { if (view is LibraryCategoryView) {
view.unsubscribe() view.onDestroy()
} }
} }
} }

View File

@ -20,6 +20,7 @@ import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.inflate import eu.kanade.tachiyomi.util.view.inflate
import eu.kanade.tachiyomi.widget.AutofitRecyclerView import eu.kanade.tachiyomi.widget.AutofitRecyclerView
import kotlinx.coroutines.MainScope import kotlinx.coroutines.MainScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.recyclerview.scrollStateChanges import reactivecircus.flowbinding.recyclerview.scrollStateChanges
@ -155,7 +156,12 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
unsubscribe() unsubscribe()
} }
fun unsubscribe() { fun onDestroy() {
unsubscribe()
scope.cancel()
}
private fun unsubscribe() {
subscriptions.clear() subscriptions.clear()
} }

View File

@ -7,10 +7,6 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.widget.LinearLayout import android.widget.LinearLayout
import eu.kanade.tachiyomi.databinding.DownloadCustomAmountBinding import eu.kanade.tachiyomi.databinding.DownloadCustomAmountBinding
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.widget.textChanges
import timber.log.Timber import timber.log.Timber
/** /**
@ -35,8 +31,6 @@ class DialogCustomDownloadView @JvmOverloads constructor(context: Context, attrs
*/ */
private var max = 0 private var max = 0
private val scope = MainScope()
private val binding: DownloadCustomAmountBinding private val binding: DownloadCustomAmountBinding
init { init {
@ -71,16 +65,16 @@ class DialogCustomDownloadView @JvmOverloads constructor(context: Context, attrs
} }
// When user inputs custom number set amount equal to input. // When user inputs custom number set amount equal to input.
binding.myNumber.textChanges() binding.myNumber.addTextChangedListener(object : SimpleTextWatcher() {
.onEach { override fun onTextChanged(text: CharSequence, start: Int, before: Int, count: Int) {
try { try {
amount = getAmount(Integer.parseInt(it.toString())) amount = getAmount(text.toString().toInt())
} catch (error: NumberFormatException) { } catch (error: NumberFormatException) {
// Catch NumberFormatException to prevent parse exception when input is empty. // Catch NumberFormatException to prevent parse exception when input is empty.
Timber.e(error) Timber.e(error)
} }
} }
.launchIn(scope) })
} }
/** /**

View File

@ -0,0 +1,15 @@
package eu.kanade.tachiyomi.widget
import android.text.Editable
import android.text.TextWatcher
open class SimpleTextWatcher : TextWatcher {
override fun beforeTextChanged(text: CharSequence, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(text: CharSequence, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(text: Editable) {
}
}