본문 바로가기

Android Library

[Android Library] Flow (2) - 사용하기

안녕하세요.

오늘은 Jetpack Library 중 하나인 Flow의 사용 방법에 대해 알아보겠습니다.


Flow

https://developer.android.com/kotlin/flow

Flow에 3가지 개념이 존재합니다.

 

Producer (생산자)

data를 stream에 보내는 개체입니다.

(stream : data stream으로써 위의 그림에서 막대바로 표현한 flow를 의미합니다.)

 

flow block 안에 코드를 작성해야하며,

emit function을 이용해 data를 stream에 보냅니다.

fun simpleFlow() = flow {
    for (i in 1..5) {
        delay(100)
        emit(i)
    }
}

 

Intermediary (중개자)

Producer와 Consumer 사이의 중간 개체입니다.

 

Producer가 만든 data를 수정해서 Consumer에게 보내줄 수도 있으며,

원본 그대로 보낼 수도 있습니다.

 

즉, Intermediary는 optional한 개체입니다. (필요할 때만 있으면 됨)

val filteredFlow = simpleFlow()
        .filter { it % 2 == 0 } // 짝수만 필터링
        .map { it * 2} // 각 요소를 2배로 변환

 

Consumer (소비자)

stream에서 data를 가져다 쓰는 개체입니다.

collection function을 이용해 stream에서 data를 가져옵니다.

fun main() = runBlocking {
    val startTime = System.currentTimeMillis()

    println("simpleFlow result :")

    simpleFlow().collect {
        println("receive ${it} after ${System.currentTimeMillis() - startTime}")
    }


    println("\nfilteredFlow result :")

    filteredFlow.collect {
        println("receive ${it} after ${System.currentTimeMillis() - startTime}")
    }
}

simpleFlow는 Producer 였습니다.

Producer가 만든 데이터를 그대로 가져온 모습입니다.

 

filteredFlow는 Intermediary로,

Producer가 만든 데이터를 가공해서 달라진 모습입니다.


Flow with Other Jetpack Libraries

다른 jetpack library 들과 함께 사용되는 경우가 많습니다.

 

Room

Dao 안의 메소드의 반환값으로 Flow가 쓰인 모습입니다.

@Dao
abstract class ExampleDao {
    @Query("SELECT * FROM Example")
    abstract fun getExamples(): Flow<List<Example>>
}

 

LiveData

LiveData의 장점으로 인해, Flow로 들고온 데이터를 LiveData로 바꿔서 쓰는 경우가 있습니다.

그럴 때 asLiveData로 쉽게 LiveData로 형변환이 가능합니다.

fun fetchWeatherFlow() = flow<Int> {

}

val currentWeatherLiveData: LiveData<String> =
    fetchWeatherFlow()
        .map { heavyTransformation(it) }
        .asLiveData()

예외 처리

emitter나 code에서 throw를 던질 수 있습니다.

 

try, catch 문으로 에러 처리가 가능합니다.

fun simple() = flow {
    for (i in 1..3) {
        println("Emitting $i")
        emit(i) // emit next value
    }
}

fun main() = runBlocking {
    try {
        simple().collect { value ->
            println(value)
            check(value <= 1) { "Collected $value" }
        }
    } catch (e: Throwable) {
        println("Caught $e")
    }
}

 

그리고 try 문 없이도 catch 만으로 에러 처리가 가능합니다.

.catch로 쉽게 에러 처리하는 것을 볼 수 있습니다.

fun simple() = flow {
    for (i in 1..3) {
        println("Emitting $i")
        emit(i) // emit next value
    }
}

fun main() = runBlocking {
    simple()
        .onEach { value ->
            check(value <= 1) { "Collected ${value}" }
            println(value)
        }
        .catch { e -> println("Caught $e") } // emit on exception
        .collect()
}

 

여기서 주의할 점은 catch 문 위에 throw 구문을 넣어줘야 합니다.

catch 코드 아래에 throw 구문이 있다면 제대로 에러 처리를 하지 못합니다.


여기까지 Flow의 기본 사용 방법에 대해 알아보았습니다.

다음 포스팅에서는 Flow의 종류에 대해 알아보겠습니다.


참고 자료

https://kotlinlang.org/docs/flow.html#flows

 

Asynchronous Flow | Kotlin

 

kotlinlang.org

https://developer.android.com/kotlin/flow

 

Android의 Kotlin 흐름  |  Android Developers

Android의 Kotlin 흐름 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 코루틴에서 흐름은 단일 값만 반환하는 정지 함수와 달리 여러 값을 순차적으로 내보낼

developer.android.com