Android LiveData - как ограничить результаты - PullRequest
0 голосов
/ 18 июня 2020

У меня есть liveData, которая выдает некоторые целые числа. но я бы хотел уменьшить выбросы так, чтобы это происходило каждые 5 секунд. но по прошествии первых 5 секунд дросселирование можно убрать.

здесь я наблюдаю такое количество:

count1.observe(this, new Observer() {
      @Override public void onChanged(@Nullable Integer i) {
        //Do something with "integer"
      }
    });

, но я хочу, чтобы обратный вызов onChanged () был ограничен таким образом, чтобы он вызывал максимум 2 раза в течение первых 5 секунд. По прошествии первых 5 секунд троттлинг больше не нужен. Я не уверен, как это сделать, так как я не вижу никаких вариантов для liveata для обработки потоков. Я пробовал Transformation, но у него есть только map и switchMap. Как это может быть сделано ? enter code here

Ответы [ 2 ]

1 голос
/ 19 июня 2020

Вот реализация MediatorLiveData, которая ограничивает использование Handler (в Kotlin, поскольку Java имеет много шаблонов).

Если исходные данные обновляются слишком часто, то выбросы результатов будут отложены:

import android.os.Handler
import android.os.Looper
import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData

/**
 * LiveData throttling value emissions so they don't happen more often than [delayMs].
 */
class ThrottledLiveData<T>(source: LiveData<T>, delayMs: Long) : MediatorLiveData<T>() {
    val handler = Handler(Looper.getMainLooper())
    var delayMs = delayMs
        private set

    private var isValueDelayed = false
    private var delayedValue: T? = null
    private var delayRunnable: Runnable? = null
        set(value) {
            field?.let { handler.removeCallbacks(it) }
            value?.let { handler.postDelayed(it, delayMs) }
            field = value
        }
    private val objDelayRunnable = Runnable { if (consumeDelayedValue()) startDelay() }

    init {
        addSource(source) { newValue ->
            if (delayRunnable == null) {
                value = newValue
                startDelay()
            } else {
                isValueDelayed = true
                delayedValue = newValue
            }
        }
    }

    /** Start throttling or modify the delay. If [newDelay] is `0` (default) reuse previous delay value. */
    fun startThrottling(newDelay: Long = 0L) {
        require(newDelay >= 0L)
        when {
            newDelay > 0 -> delayMs = newDelay
            delayMs < 0 -> delayMs *= -1
            delayMs > 0 -> return
            else -> throw IllegalArgumentException("newDelay cannot be zero if old delayMs is zero")
        }
    }

    /** Stop throttling, if [immediate] emit any pending value now. */
    fun stopThrottling(immediate: Boolean = false) {
        if (delayMs <= 0) return
        delayMs *= -1
        if (immediate) consumeDelayedValue()
    }

    override fun onInactive() {
        super.onInactive()
        consumeDelayedValue()
    }

    // start counting the delay or clear it if conditions are not met
    private fun startDelay() {
        delayRunnable = if (delayMs > 0 && hasActiveObservers()) objDelayRunnable else null
    }

    private fun consumeDelayedValue(): Boolean {
        delayRunnable = null
        return if (isValueDelayed) {
            value = delayedValue
            delayedValue = null
            isValueDelayed = false
            true
        } else false
    }
}

Используйте его, передав исходные данные в реальном времени в качестве первого аргумента и наблюдая за ним вместо этого:

val throttledCount = ThrottledLiveData(count1, 2500L)  // maximum of one update per 2.5 sec
throttledCount.observe(this, Observer { i: Int ->
    //Do something with "integer"
})

Если вы хотите остановить регулирование через 5 секунд, просто отправьте отложенный запуск, который отключит это:

val disableThrottle = Runnable { throttledCount.stopThrottling() }
throttledCount.handler.postDelayed(disableThrottle, 5000L)
1 голос
/ 18 июня 2020

В вашем случае вы должны использовать оператор интервала rx java.

Оператор интервала создает Observable, который испускает последовательность целых чисел, разделенных заданным интервалом времени. Он используется, когда мы хотим выполнять задачу снова и снова через некоторый интервал.

пример:

val disposable =
Observable.interval(0, 2, TimeUnit.SECONDS)
    .flatMap {
        return@flatMap Observable.create<String> { emitter ->
            Log.d("IntervalExample", "Create")
            emitter.onNext("MindOrks")
            emitter.onComplete()
        }
    }
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe {
        Log.d("IntervalExample", it)
    }

композитныйDisposable.add (одноразовый)

Здесь задача будет выполняться снова и снова через 2 секунды.

Одно замечание: это будет продолжаться вечно.

Как это остановить?

Там есть два способа остановиться. Ниже приведены два способа остановить его.

  1. Использование композитногоDisposable.dispose ()
  2. Использование оператора take (n), как показано ниже

    Observable.interval(0, 2, TimeUnit.SECONDS).take(5).flatMap {
    return@flatMap Observable.create<String> { emitter ->
        Log.d("IntervalExample", "Create")
        emitter.onNext("MindOrks")
        emitter.onComplete()
    }
    

    } .observeOn (AndroidSchedulers.mainThread ()) .subscribe {Log.d ("IntervalExample", it)}

Поскольку мы передали 5 в качестве параметра в дубле ( 5) задание будет выполнено всего 5 раз с интервалом 2 секунды.

...