1. StateFlow를 사용하여 Splash화면에서 알맞은 화면으로 navigation을 하거나 대기하는 역할을 수행해야 되는데 화면의 이동이 원할이 되지 않았습니다.
이전 코드
@HiltViewModel
class PlanfitSplashViewModel @Inject constructor(
private val getPlanfitLoginType: GetPlanfitLoginType,
) : ViewModel() {
private val _isLoginFlow = MutableStateFlow<LoginEvent>(LoginEvent.UnChecked)
val isLoginFlow: StateFlow<LoginEvent>
get() = _isLoginFlow
fun checkLoginState() = viewModelScope.launch {
val loginType = getPlanfitLoginType()
delay(3000L)
_isLoginFlow.emit(LoginEvent.Login(loginType))
if (loginType == PlanfitLoginType.None)
_isLoginFlow.emit(LoginEvent.UnChecked)
}
}
sealed class LoginEvent {
data object UnChecked : LoginEvent()
data class Login(
val loginType: PlanfitLoginType,
) : LoginEvent()
}
이후 코드
@HiltViewModel
class PlanfitSplashViewModel @Inject constructor(
private val getPlanfitLoginTypeUseCase: GetPlanfitLoginTypeUseCase,
) : ViewModel() {
private val _isLoginChanel = Channel<LoginEvent>(Channel.BUFFERED)
val isLoginFlow: Flow<LoginEvent> = _isLoginChanel.receiveAsFlow()
fun checkLoginState() = viewModelScope.launch {
_isLoginChanel.send(LoginEvent.UnChecked)
delay(3000L)
val loginType = getPlanfitLoginTypeUseCase()
Log.d(TAG, "checkLoginState: ${loginType.name}")
_isLoginChanel.send(LoginEvent.Login(loginType))
if (loginType == PlanfitLoginType.None)
_isLoginChanel.send(LoginEvent.UnChecked)
}
companion object {
private const val TAG = "PlanfitSplashViewModel"
}
}
sealed class LoginEvent {
data object UnChecked : LoginEvent()
data class Login(
val loginType: PlanfitLoginType,
) : LoginEvent()
}
처음에는 해당 값의 Value를 가지고 있던 StateFlow를 이용하였습니다. 이때 isLoginFlow의 경우 mutableStateFlow로 값을 받고 해당값을 액티비티에서 collect 하게 되는데 값이 연속적으로 빠르게 emit 된 어 첫 번째 값이 Activity에서 감지되지 못하였습니다.
해결
Chanel 과 flow를 사용하여 이벤트가 중간에 손실되지 않도록 하였습니다.
Channel은 코루틴 간 데이터를 비동기적으로 전송할 수 있습니다. 즉, 하나의 코루틴이 데이터를 보내면 다른 코루틴이 이를 비동기적으로 받을 수 있습니다. 이때 Channel의 버퍼에 무리가 될 정도로 많은 전송이 있지 않다면 해당 이벤트가 중간에 손실되지않습니다
다른 해결 방안
지금 생각해 보면 SharedFlow를 사용할 수도 있었을 것 같습니다. ViewModel에서는 flow보다는 StateFlow나 SharedFlow를 사용합니다. Flow는 구독자가 생기기 전까지 데이터를 방출하지 않는 Cold Flow로, 주로 데이터 요청에 적합합니다. 반면, StateFlow는 구독자에게 항상 최신 상태를 제공하는 Hot Flow로, 상태 관리에 유용하지만 빠른 연속 상태 변경을 놓칠 수 있습니다. SharedFlow는 상태 대신 이벤트를 처리하며, 다수의 구독자에게 동시에 이벤트를 전송하거나 연속적인 이벤트를 안전하게 처리하는 데 적합합니다.
@HiltViewModel
class PlanfitSplashViewModel @Inject constructor(
private val getPlanfitLoginTypeUseCase: GetPlanfitLoginTypeUseCase,
) : ViewModel() {
private val _isLoginFlow = MutableSharedFlow<LoginEvent>(replay = 0)
val isLoginFlow: SharedFlow<LoginEvent> = _isLoginFlow
fun checkLoginState() = viewModelScope.launch {
_isLoginFlow.emit(LoginEvent.UnChecked)
delay(3000L)
val loginType = getPlanfitLoginTypeUseCase()
Log.d(TAG, "checkLoginState: ${loginType.name}")
_isLoginFlow.emit(LoginEvent.Login(loginType))
if (loginType == PlanfitLoginType.None) {
_isLoginFlow.emit(LoginEvent.UnChecked)
}
}
companion object {
private const val TAG = "PlanfitSplashViewModel"
}
}
sealed class LoginEvent {
data object UnChecked : LoginEvent()
data class Login(
val loginType: PlanfitLoginType,
) : LoginEvent()
}
2. Navigation Animation 중 Alpha값이 0 이 될 때 흰색화면이 나옵니다
액티비티의 백그라운드가 지정되있지 않아 theme에 따른 default 배경값인 흰색배경이 보였던 거였습니다. 액티비티의 백그라운드 값을 지정하여 해결하였습니다.
3. Widget의 크기에 따른 자동 줄 바꿈을 구현하고 싶습니다
버튼의 글씨가 커지거나 화면의 비율이 달라진다면 버튼의 모양이 이상해질 것 같습니다. 그래서 해당 화면을 구현할 수 있는 방법을 찾았습니다.
해결
flexbox-layout을 사용하여 해결하였습니다. FlexboxLayout은 CSS Flexible Box Layout 모듈과 비슷한 기능을 Android에 제공하는 라이브러리 프로젝트입니다.
https://github.com/google/flexbox-layout
'안드로이드 > 안드로이드' 카테고리의 다른 글
TBA(Ticket-Booking-app) 프로젝트하면서 생긴 이슈 및 해결 ,기술 정리 (3) | 2024.09.22 |
---|---|
Ticket Booking app - UiLover Android 클론코딩 회고 (4) | 2024.09.21 |
Planfit onBoarding 페이지 클론 코딩 회고 (1) | 2024.09.13 |
[Android/Kotlin] flatMapLatest 로 리팩토링 (2) | 2023.11.10 |
[Android/Kotlin] 액티비티의 종료로 ViewModel이 죽어버렸다! (1) | 2023.10.29 |