10월 3주차에는 Flow에서 데이터의 자동갱신에 대한 글입니다.
Strategies for automatically refreshing data on Android using Kotlin Flow
Making timers lifecycle-aware
bladecoder.medium.com
소개
이 글은 Kotlin Flow를 사용하여 안드로이드 애플리케이션에서 데이터를 효율적으로 로드하는 방법에 관한 시리즈의 세 번째 부분입니다. 이전 글인 “Smarter Shared Kotlin Flows”에 이어 이번 글에서는 사용자 인터페이스의 자동 주기적 새로고침을 다루고 있습니다. 이는 UI에 표시되는 데이터 세트가 자주 변경되거나 변경 시점을 정확히 알 수 없을 때 유용한 전략입니다.
내용
- 간단한 주기적 새로고침: tickerFlow 함수를 사용하여 일정한 간격으로 데이터를 새로고침하는 방법을 제시합니다. 이 함수는 무한 루프 내에서 delay()를 호출하여 Flow를 생성합니다.
예시 코드: tickerFlow 함수를 사용하여 정해진 간격으로 데이터를 새로고침합니다.
fun tickerFlow(period: Duration): Flow<Unit> = flow {
while (true) {
emit(Unit) // Tick
delay(period)
}
}
- 데이터 로딩 최적화: map() 또는 mapLatest() 연산자를 사용하여 각 틱(tick)마다 데이터 로딩 작업을 수행하고 결과를 반환합니다. 이들 연산자 사이의 행동 차이에 대해서도 설명합니다.
예시 코드: map() 또는 mapLatest() 연산자를 사용하여 각 틱에 데이터 로딩을 수행합니다.
tickerFlow(REFRESH_INTERVAL)
.map {
repository.loadSomeData()
}
- 캐싱을 활용한 똑똑한 새로고침: 긴 새로고침 간격과 리소스 소모가 큰 로딩 작업의 경우, StateFlow를 사용하여 최신 값 캐싱과 다중 구독자 간 공유를 활용하는 방법을 제시합니다.
예시 코드: StateFlow와 flowWhileShared() 연산자를 사용하여 캐싱을 활용합니다.
fun synchronizedTickerFlow(
period: Duration,
subscriptionCount: StateFlow<Int>,
timeSource: TimeSource = ElapsedRealTimeSource
): Flow<Unit> {
return flow {
var nextEmissionTimeMark: TimeMark? = null
flow {
nextEmissionTimeMark?.let { delay(-it.elapsedNow()) }
while (true) {
emit(Unit)
nextEmissionTimeMark = timeSource.markNow() + period
delay(period)
}
}
.flowWhileShared(subscriptionCount, SharingStarted.WhileSubscribed())
.collect(this)
}
}
- synchronizedTickerFlow 구현: 생명주기를 고려하여 값을 방출하는 synchronizedTickerFlow를 구현하여, 필요할 때만 데이터를 새로고침하는 방법을 설명합니다.
- 시간 참조 공유: 복수의 데이터 소스를 주기적으로 쿼리하고 업데이트하는 고급 사용 사례를 설명하며, 결과가 일관되게 유지되도록 동일한 시간 참조를 사용하는 방법을 제시합니다.
예시 코드: 여러 데이터 소스를 주기적으로 쿼리하고 업데이트하는 고급 사용 사례.
private val timeReferenceFlow: Flow<Instant> = stateFlow(viewModelScope, null) { subscriptionCount ->
synchronizedTickerFlow(REFRESH_PERIOD, subscriptionCount)
.map { Instant.now() }
}.filterNotNull()
val results1: StateFlow<Result> = stateFlow(viewModelScope, Result.Empty) { subscriptionCount ->
timeReferenceFlow
.flowWhileShared(subscriptionCount, SharingStarted.WhileSubscribed())
.distinctUntilChanged()
.map { timeReference: Instant ->
repository.loadDataForTime(timeReference)
}
}
정리
지금까지 ViewModel에서 업데이트 하기위해 init이나 delay를 쓰거나, View단에서 Launched Effect를 조금 길게 작성했는데, 그것보다 조금더 고급스러운(?) 방식을 알게된 글입니다. 데이터 새로고침의 정확성과 효율성이 중요한 애플리케이션에서 유리하게 작용할 것 같습니다.
** Kotlin Weekly **
kotlinweekly.net
'Kotlin Weekly' 카테고리의 다른 글
Kotlin Weekly # -378 "Compose Canvas API로 계기판 만들기" (0) | 2023.12.12 |
---|---|
Kotlin Weekly # -377 "Kotlin 제네릭 변성" (0) | 2023.12.11 |
Kotlin Weekly # -375 "Koin vs Hilt 의존성주입 전략 비교" (0) | 2023.12.08 |
Kotlin Weekly # -374 "얕은 복사 vs 깊은 복사" (0) | 2023.12.04 |
Kotlin Weekly # -373 "Compose Settings library" (0) | 2023.09.25 |