이번에는 알아볼 것은 플로우에서 시작, 완료, 에러 등의 상태 처리 등을 하는 함수들이다.
시작할 때
flowOf(1, 2, "String", 3).onStart {
emit("Start1!!!")
}.onStart {
emit("Start2!!!")
}.collect { println(it) }
result :
Start1!!!
Start2!!!
1
2
String
3
onStart
함수는 Flow 가 실행을 시작할 때 특정 데이터를 방출할 수 있도록 지원한다.
선언 위치는 사용전이면 상관없으며 여러 번 사용하면 순서대로 전부 실행된다.
비어있을 때
emptyFlow<String>().onEmpty {
emit("is Not Empty")
}.collect { println(it) }
result :
is Not Empty
onEmpty
함수는 Flow 가 비어있을 때 특정 데이터를 방출할 수 있도록 지원한다.
선언 위치는 상관이 없으나 여러 번 사용하면 처음에 사용한 함수만 적용이 된다.
( 만약, 첫 번째 onEmpty에서 어떤 데이터도 방출하지 않으면 두 번째 onEmpty의 로직은 실행이 된다. )
emptyFlow<String>()
.onEach { if (it !is String) throw IllegalArgumentException() }
.withIndex()
.onEmpty {
emit(IndexedValue(0,"is Not Empty"))
}
.collect {
println(it)
}
예제처럼 타입이 변할 수 있는 withIndex
, map
, tranform
등의 뒤에
onEmpty
를 선언하면 방출해야 하는 타입이 잘 확인해야 한다.
에러 발생했을 때
flowOf(1, 2, "Three", 3)
.onEach { check(it !is String) { "It is Not Number" } }
.catch { println(it) }
.collect {
println(it)
}
result :
1
2
java.lang.IllegalStateException: It is Not Number
flowOf(1, 2, "Three", 3)
.catch { println(it) }
.onEach { check(it !is String) { "It is Not Number" } }
.collect {
println(it)
}
result :
1
2
FATAL EXCEPTION: DefaultDispatcher-worker-1
Process: com.onetwothree.flowsample, PID: 19986
java.lang.IllegalStateException: It is Not Number
catch
함수는 try / catch와 동일한 역할로
예외 발생 시 해당 예외처리를 위하여 사용할 수 있다.
인자로는 throwable 이 들어오며 예외처리 후 Flow는 알아서 종료된다.
catch
함수는 upStream 만 집어낼 수 있기에 사용 위치가 매우 중요하다.
첫 번째 예제는 "에러 발생 시점 이후"에 catch
를 사용하여 예외 처리가 가능했고,
두 번째 예제는 "에러 발생 시점 이전"에 사용하여 예외 처리를 못하고 에러가 발생하는 것을 확인할 수 있다.
완료되었을 때
flowOf(1, 2, "String", 3).onEach {
check(it !is String) { "It is String" }
}.onCompletion {
println(it)
}.collect {
println(it)
}
result :
1
2
FATAL EXCEPTION: DefaultDispatcher-worker-2
Process: com.onetwothree.flowsample, PID: 21013
java.lang.IllegalStateException: It is String
onCompletion
함수는 try / finnaly와 동일한 역할로
Flow 가 완료되었을 때의 동작을 지정할 수 있다.
정상적인 완료와 에러로 인한 완료 두 가지 케이스 모두 해당 함수를 실행시킨다.
인자로는 Throwable 이 들어오며, null 이면 정상종료 또는 catch
를 통해 이미 예외 처리된 경우이고,
null 이 아니면 에러로 인한 종료이다.
onCompletion
함수는 여러 번 사용할 수 있으며, 작성한 순서대로 동작한다.
인자도 동일한 것이 반복적으로 들어온다.
재시도가 필요할 때
flowOf(1, 2, "Three", 3)
.onEach { if (it is String) throw IllegalArgumentException() }
.retry(2) {
it is IllegalArgumentException
}
.catch { println(it) }
.collect {
println(it)
}
result :
1
2
1
2
1
2
FATAL EXCEPTION: DefaultDispatcher-worker-1
Process: com.onetwothree.flowsample, PID: 16765
java.lang.IllegalArgumentException
retry
는 에러로 인해 Flow 가 제대로 완료되지 않았을 때 동작한다.
에러가 날 때마다 Flow의 방출을 처음부터 다시 시작한다.
생성자를 통해서 재시도 횟수를 지정할 수 있으며,
횟수를 다 채우고도 에러가 발생한다면 그때 크래시가 발생한다.
( 횟수를 0 보다 작게 지정하면 에러가 발생한다. )
인자로는 throwable 이 넘어오고 boolean 값을 반환해야 한다.
반환 값이 true 인 경우에만 재시도를 진행한다.
flowOf(1, 2, "Three", 3)
.onEach { if (it is String) throw IllegalArgumentException() }
.catch { println(it) }
.retry(2) {
it is IllegalArgumentException
}
.collect {
println(it)
}
result :
1
2
java.lang.IllegalArgumentException
위의 retry
예제와 동일한 코드이다.
다른 점은 catch
를 추가한 것이다.
retry
는 에러를 받아야만 재시도를 진행하는 함수이다.
그런데 예제에선 catch
가 먼저 에러를 처리해버려서 retry
가 실행되지 않는다.
그러니 재시도와 예외 처리를 같이 진행하려면 retry
뒤에 catch
를 사용하거나,
위 코드의 catch
에서 새로운 에러를 던져서 retry
가 동작하도록 해야 한다.
flowOf(1, 2, "Three", 3)
.onEach { if (it is String) throw IllegalArgumentException() }
.retryWhen { cause, attempt ->
cause is IllegalArgumentException && attempt < 2
}
.collect {
println(it)
}
result :
1
2
1
2
1
2
FATAL EXCEPTION: DefaultDispatcher-worker-1
Process: com.onetwothree.flowsample, PID: 16881
java.lang.IllegalArgumentException
retryWhen
은 retry
와 동일한 기능을 한다.
대신 생성자에서 재시도 횟수를 지정하지 않고, 인자에서 현재 시도 횟수가 들어온다.
역시나 true를 리턴하면 재시도를 진행한다.
'코틀린' 카테고리의 다른 글
이펙티브 코틀린 - 음..? (1) | 2022.03.29 |
---|---|
Kotlin Channel - 코루틴간 데이터 통신 (0) | 2020.07.30 |
Kotlin Flow #3 - Zip And Combine (0) | 2020.07.10 |
Kotlin Flow #2 - 연산자들 (0) | 2020.07.08 |
Kotlin Flow #1 - 기본 사용법 (1) | 2020.07.08 |