안드로이드 샘플 앱인 nowinandroid 앱을 참고하였다.
nowinandroid 앱의 MainActivity를 보면 다음과 같은 코드가 있다
override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen()
super.onCreate(savedInstanceState)
var uiState: MainActivityUiState by mutableStateOf(Loading)
// Update the uiState
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState
.onEach {
uiState = it
}
.collect()
}
}
// Keep the splash screen on-screen until the UI state is loaded. This condition is
// evaluated each time the app needs to be redrawn so it should be fast to avoid blocking
// the UI.
splashScreen.setKeepOnScreenCondition {
when (uiState) {
Loading -> true
is Success -> false
}
}
}
viewModel의 uiState를 collect하고 이는 splashScreen이 데이터를 가져오기 전까지 유지되도록 한다.
ViewModel 코드는 다음과 같이 되어 있다.
val uiState: StateFlow<MainActivityUiState> = userDataRepository.userData.map {
Success(it)
}.stateIn(
scope = viewModelScope,
initialValue = Loading,
started = SharingStarted.WhileSubscribed(5_000),
)
sealed interface MainActivityUiState {
data object Loading : MainActivityUiState
data class Success(val userData: UserData) : MainActivityUiState
}
userDataRepository.userData까지는 일반 Flow<> 타입이다. 이는 stateIn을 통해서 StateFlow가 된다.
fun main() {
val coldFlow: Flow<Int> = flow {
emit(1)
emit(2)
emit(3)
}
coldFlow.collect { value -> println("Received: $value") }
coldFlow.collect { value -> println("Received Again: $value") }
}
//results
Received: 1
Received: 2
Received: 3
Received Again: 1
Received Again: 2
Received Again: 3
만약 flow로 네트워크에서 받아온 데이터나 오래 걸리는 작업을 값으로 생산한다면 collector가 생길 때마다 요청하게 될 것이다
하지만 hot stream으로 하면 생산한 값을 메모리에 가지고 있어서 새로운 collector가 생기면 저장해놓은 데이터를 전해줄 수 있다.
public fun <T> MutableSharedFlow(
replay: Int = 0,
extraBufferCapacity: Int = 0,
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND
):
여기서 replay값이 새로운 consumer가 생겼을 때 다시 전해줄 값의 개수이다.
아까 ViewModel에 있던 코드를 다시 가져와보자
.stateIn(
scope = viewModelScope,
initialValue = Loading,
started = SharingStarted.WhileSubscribed(5_000),
)
public fun WhileSubscribed(
stopTimeoutMillis: Long = 0,
replayExpirationMillis: Long = Long.MAX_VALUE
)
stopTimeoutMillis 옵션이 있는데 이는 더이상 consumer가 있을 때 바로 stop하지 않고 몇 초 이따가 멈추는 옵션이다.
위 코드에서는 WhileSubscribed(5_000)으로 설정했는데 5초 동안은 upstream이 살아있을 것이다.
- 왜 5초 동안 살아있게 하는걸까?
- configuration change처럼 잠깐 view가 없어졌다 다시 생기는 경우에는 upstream을 멈췄다 다시 생성하는 것보다는 기존 것을 그대로 사용하는게 더 이득이기 때문이다.





lifecycleScope.launch {
locationProvider.locationFlow()
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.collect {
// New location! Update the map
}
}
https://medium.com/androiddevelopers/things-to-know-about-flows-sharein-and-statein-operators-20e6ccb2bc74
https://medium.com/androiddevelopers/migrating-from-livedata-to-kotlins-flow-379292f419fb
https://medium.com/androiddevelopers/a-safer-way-to-collect-flows-from-android-uis-23080b1f8bda