Kotlin Weekly

Kotlin Weekly # -376 "Kotlin Flow의 데이터 자동 갱신"

베블렌 2023. 12. 8. 02:26

10월 3주차에는 Flow에서 데이터의 자동갱신에 대한 글입니다.

 

https://bladecoder.medium.com/strategies-for-automatically-refreshing-data-on-android-using-kotlin-flow-cd23ba7cfbe0

 

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를 조금 길게 작성했는데, 그것보다 조금더 고급스러운(?) 방식을 알게된 글입니다. 데이터 새로고침의 정확성과 효율성이 중요한 애플리케이션에서 유리하게 작용할 것 같습니다.

 

 

 

 

 

http://kotlinweekly.net/

 

** Kotlin Weekly **

 

kotlinweekly.net