프로그래밍/Android

LiveData를 버리고 StateFlow를 써야할까요?

Lou Park 2022. 5. 5. 12:30

Flow

비동기식으로 계산할 수 있는 데이터 스트림. Flow는 값 시퀀스를 생성하는 Iterator와 매우 비슷하지만 정지함수를 사용하여 값을 비동기적으로 생성하고 사용한다. Python의 generator와 비슷하게 이해하면 될 듯.

  • 생산자: 스트림에 추가되는 데이터를 생산. 코루틴 덕분에 비동기적으로 생산도 가능. (Remote Datasource)
  • 중개자(*optional): 스트림에 내보내는 각각의 값이나 스트림 자체를 수정할 수 있다.
  • 소비자: 스트림의 값을 사용한다.

생산자는 저장소, 소비자는 UI 인터페이스라고 받아들일 수 있다. 하지만 UI 레이어가 사용자 입력이벤트의 생산자 역시 될 수 있다.

 

 

StateFlow의 특징

  • 항상 값을 가지고 있다.
  • 단 하나의 값만을 가진다.
  • 여러 옵저버를 가질 수 있다. (Flow가 공유된다.)
  • 활성중인 옵저버의 수와 관계없이 구독시에 최신값을 Replay한다.

위 특성들 때문에 UI 상태를 나타낼때 StateFlow를 사용하면 효율적이다. (

 

Flow.stateIn

Flow를 StateFlow로 바꿔주기 위해서는 stateIn 을 사용한다. 이는 3가지 파라미터를 가진다.

  • scope sharing 시작되는 코루틴 스코프
  • started sharing이 시작되었을때와 중지되었을때의 전략
  • initialValue StateFlow의 초기값

started 는 3가지 값을 가질 수 있다.

One-Shot 작업에는 Lazily 나 Eagerly 를 사용할 수 있다.

  • Lazily 첫번째 구독자가 나타나면 시작되고 scope가 취소되면 중지한다.
  • Eagerly 즉시 시작되고, scope가 취소되면 중지한다.
  • WhileSubscribed: 컬렉터가 없을때 Upstream Flow를 중지한다.
started = WhileSubscribed(5000)
  • 사용자가 Background로 나갔을때 다른 Layer에서의 변경사항은 5초 뒤에 중지되며, 배터리를 절약할 수 있다.
  • 최신 값은 여전히 캐시되어 유저가 다시 돌아왔을때 즉시 데이터를 가질 수 있다.
  • 구독은 새로운 값이 들어오면 다시 시작될 것이며, 가능한 경우 화면을 업데이트한다.

 

View에서 StateFlow를 옵저브하기

  • Activity.lifecycleScope.launch: 코루틴을 즉시 시작하고, Activity가 Destroyed 되면 취소된다.
  • Fragment.lifecycleScope.launch: 코루틴을 즉시 시작하고, Fragment가 파괴될때 취소된다.
  • Fragment.viewLifecycleOwner.launch: 코루틴을 즉시 시작하고, Fragment의 View Lifecycle이 파괴될때 취소된다. UI를 변경한다면 이를 사용해야 한다.

 

LaunchWhen 어쩌고 시리즈

launchWhenX는 lifecycleOwner가 X의 상태가 될때까지 기다리다가 X 상태 아래로 떨어질때까지 코루틴을 일시중단(suspend)한다. 중요한점은 launchWhenX는 lifecycleOwner가 파괴될때까지 코루틴을 취소하지 않는다는 점이다.

 

lifecycle.repeatOnLifecycle

repeatOnLifecycle은 코루틴을 특정상태에 있을때 시작하게 할 수 있고, 특정상태 아래로 떨어지면 중지시킨다.

위에서 말했던 WhileSubscribed(5000)과 repeatOnLifecycle을 같이 사용하면 기기의 자원을 적절히 활용하여 최고의 성능을 낼 수 있다.

 

LiveData를 버리고 StateFlow로 가야하는 이유?

무조건적인 이유는 없다. 필요에 따라 선택할뿐!

영원한건 절대 없다고 지드래곤이 외치고 다녔으니 지겨운 얼굴인 LiveData를 버리고 곧장 StateFlow로 옮겨버릴 수도 있다. 하지만 "왜?"라는 질문을 던지고 싶다. 옛날엔 RxJava가 신인 줄 알았다. MVP 말고는 안드로이드에 맞는 아키텍쳐 패턴은 없는줄 알았다. 새로운게 있으면 마구마구 받아들이고, 프로젝트를 처음부터 다시 뜯어고치고를 반복했다. 그에 따르는 생산성의 저하를 감수할만큼 이게 좋은가?가 나의 논점이 된거같다. 

 

LiveData의 Observe는 뷰가 STOPPED상태일때 자동으로 등록 취소된다. StateFlow를 이처럼 이용하기 위해서 repeatOnLifecycle이 등장했고, 이제 그 이점을 막 따라잡을 수 있게 되었다. LiveData가 StateFlow에 비해 열등한 점은 아래와 같다.

 

  • LiveData는 초기 값을 전달하지 않는다.
  • 클린 아키텍처의 관점에서 LiveData는 안드로이드 플랫폼에 속해 있어 Domain Layer에서 사용하기 적합하지 않다.
  • Background Thread에서 이용하는 postValue는 mainThread가 바쁠때 드랍될 위험이 있다.
  • Nullable이다.

 

다만 LiveData는 프로그래머가 임페러티브하게 데이터를 조작하는 식으로 코드를 짤 수 있다는 점이 있는데, Flow를 이용하면 데이터의 흐름에따라 자연스레 UI가 업데이트 되도록 짤 수 있기때문에 최근의 선언형 UI 패러다임과도 잘 맞으며, 버그를 줄이는데 기여할 수 있을 것 같다. 

 

 

참고자료

https://medium.com/androiddevelopers/migrating-from-livedata-to-kotlins-flow-379292f419fb

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