아래 작성된 내용은 flow를 사용하지 않고 livedata를 Observer로 사용으로 생명주기에 따라 데이터가 연속적으로 호출이 되는 이슈가 있어 해당 현상을 처리할 필요성이 있어 해당 내용을 검토 중 luciofm의 Debounce 작성 코드를 발견하여 해당 내용을 공유하기 위해 작성 합니다.
Debounce란?
debounce는 연속적으로 발생하는 이벤트 스트림에서 마지막 이벤트만을 선택하여 방출하는 연산자입니다. 특정 시간 동안 새로운 이벤트가 발생하지 않으면 그동안 받은 마지막 이벤트를 방출합니다. 이를 통해 짧은 시간 내에 여러 번 발생하는 이벤트를 하나로 축소할 수 있습니다.
Debounce의 활용 사례
검색 제안 (Autocomplete): 사용자가 입력할 때마다 API 호출을 줄이기 위해 debounce를 사용하여 입력이 멈춘 후에만 검색 요청을 보냅니다. 버튼 클릭 방지: 사용자가 버튼을 빠르게 여러 번 클릭할 경우, debounce를 사용하여 마지막 클릭만 처리하거나 일정 시간 동안 여러 클릭을 무시할 수 있습니다. 스크롤 이벤트 최적화: 무한 스크롤이나 스크롤 위치 감지 시, 너무 많은 이벤트를 처리하지 않도록 debounce를 적용할 수 있습니다.
결론
debounce 연산자는 연속적인 이벤트 스트림에서 노이즈를 줄이고 유의미한 이벤트만을 처리하는 데 매우 유용한 도구입니다. 특히 사용자 입력 처리와 같은 상황에서 성능 최적화와 사용자 경험 향상에 크게 기여할 수 있습니다
사용방법
1
2
3
4
5
6
private var _values = MutableLiveData<String>()
val values: LiveData<String> = _values
values.debounce(1000, TimeUnit.MILLISECONDS).observe(activity) {
//1초 후 이벤트 발생
}
전체코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class DebounceLiveData<Source>(
private val source: LiveData<Source>,
private val debounceMs: Long
) : LiveData<Source>(), CoroutineScope {
private val job = SupervisorJob()
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job
private var debounceJob: Job? = null
private val observer = Observer<Source> { source ->
debounceJob?.cancel()
debounceJob = launch {
delay(debounceMs)
value = source
}
}
override fun onActive() {
source.observeForever(observer)
}
override fun onInactive() {
debounceJob?.cancel()
source.removeObserver(observer)
}
}
fun <Source> LiveData<Source>.debounce(
time: Long,
unit: TimeUnit = TimeUnit.MILLISECONDS
): LiveData<Source> {
val timeMs = when (unit) {
TimeUnit.NANOSECONDS -> unit.toMillis(time)
TimeUnit.MICROSECONDS -> unit.toMillis(time)
TimeUnit.MILLISECONDS -> time
TimeUnit.SECONDS -> unit.toMillis(time)
TimeUnit.MINUTES -> unit.toMillis(time)
TimeUnit.HOURS -> unit.toMillis(time)
TimeUnit.DAYS -> unit.toMillis(time)
}
return DebounceLiveData(this, timeMs)
}