Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin Android debounce

Is there any fancy way to implement debounce logic with Kotlin Android?

I'm not using Rx in project.

There is a way in Java, but it is too big as for me here.

like image 360
Kyryl Zotov Avatar asked Jun 14 '18 13:06

Kyryl Zotov


2 Answers

I've created a gist with three debounce operators inspired by this elegant solution from Patrick where I added two more similar cases: throttleFirst and throttleLatest. Both of these are very similar to their RxJava analogues (throttleFirst, throttleLatest).

throttleLatest works similar to debounce but it operates on time intervals and returns the latest data for each one, which allows you to get and process intermediate data if you need to.

fun <T> throttleLatest(     intervalMs: Long = 300L,     coroutineScope: CoroutineScope,     destinationFunction: (T) -> Unit ): (T) -> Unit {     var throttleJob: Job? = null     var latestParam: T     return { param: T ->         latestParam = param         if (throttleJob?.isCompleted != false) {             throttleJob = coroutineScope.launch {                 delay(intervalMs)                 latestParam.let(destinationFunction)             }         }     } } 

throttleFirst is useful when you need to process the first call right away and then skip subsequent calls for some time to avoid undesired behavior (avoid starting two identical activities on Android, for example).

fun <T> throttleFirst(     skipMs: Long = 300L,     coroutineScope: CoroutineScope,     destinationFunction: (T) -> Unit ): (T) -> Unit {     var throttleJob: Job? = null     return { param: T ->         if (throttleJob?.isCompleted != false) {             throttleJob = coroutineScope.launch {                 destinationFunction(param)                 delay(skipMs)             }         }     } } 

debounce helps to detect the state when no new data is submitted for some time, effectively allowing you to process a data when the input is completed.

fun <T> debounce(     waitMs: Long = 300L,     coroutineScope: CoroutineScope,     destinationFunction: (T) -> Unit ): (T) -> Unit {     var debounceJob: Job? = null     return { param: T ->         debounceJob?.cancel()         debounceJob = coroutineScope.launch {             delay(waitMs)             destinationFunction(param)         }     } } 

All these operators can be used as follows:

val onEmailChange: (String) -> Unit = throttleLatest(             300L,              viewLifecycleOwner.lifecycleScope,              viewModel::onEmailChanged         ) emailView.onTextChanged(onEmailChange) 
like image 174
Terenfear Avatar answered Sep 20 '22 19:09

Terenfear


For a simple approach from inside a ViewModel, you can just launch a job within the viewModelScope, keep track of the job, and cancel it if a new value arises before the job is complete:

private var searchJob: Job? = null  fun searchDebounced(searchText: String) {     searchJob?.cancel()     searchJob = viewModelScope.launch {         delay(500)         search(searchText)     } } 
like image 26
Chris Chute Avatar answered Sep 19 '22 19:09

Chris Chute