들어가며
안드로이드 개발에는 DataBinding을 위해 데이터의 변경을 관찰할 수 있는 다양한 자료구조를 제공하고 있습니다. 이를 Data Holder라고 하는데, LiveData, MutableState 등등 DataBinding 시 사용할 수 있는 데이터 홀더가 너무 많습니다. 이번 포스트에서 저와 같이 어떤 데이터 홀더가 있는지 알아보고, 무엇을 사용해야 할지 결정해 봅시다.
Data Holder의 종류
제가 찾아본 바로는 안드로이드에서 자주 사용하는 데이터 홀더 클래스는 LiveData, MutableLiveData, StateFlow, MutableStateFlow, MutableState가 있는 것 같았습니다.
데이터 홀더마다 개발된 맥락은 다르지만 모두 같은 역할을 수행하기 때문에 비교 우위가 생기고 더 일반적으로 사용할 수 있는 데이터 홀더를 찾는 것 같습니다.
1. LiveData ( MutableLiveData )
LiveData는 앱의 생명 주기를 인식하는 관찰 가능한 데이터 홀더 클래스입니다.
생명 주기를 인식함으로써 활성화 상태인 요소에 대해서만 알림을 받는다고 합니다.
장점
1. UI와 데이터의 일관성 보장
LiveData는 옵저버 패턴을 따르며, 데이터가 변경되면 Observer가 자동으로 UI를 업데이트할 수 있음.
또한, 가장 최신의 데이터를 유지함.
2. 메모리 누수 없음
수명 주기가 끝나면 같이 소멸하기 때문에 메모리 누수가 없음.
3. 중지된 Activity로 인한 비정상 종료 없음
LiveData가 활성 상태인 Observer에 의해서만 값이 변경되기 때문에 안정적임.
4. 자동적인 수명 주기 처리
UI는 구독 상태에 영향을 주지 못하며, LiveData에서 수명 주기에 따른 구독 상태를 자동으로 관리함.
LiveData vs MutableLiveData
LiveData는 추상 클래스이며, 상태를 업데이트할 수 있는 공개된 함수가 존재하지 않습니다.
MutableLiveData는 LiveData의 실체화 클래스이며, LiveData의 상태 업데이트 함수를 사용할 수 있습니다.
사용 방법
1. LiveData 객체를 생성한다.
LiveData는 생명 주기로부터 독립성을 보장받기 위해 ViewModel 객체에서 선언되는 것이 권장됩니다.
class MyViewModel : ViewModel() {
val myLiveData: MutableLiveData<String> by lazy {
MutableLiveData<String>()
}
...
}
2. Observer 객체를 생성하여 LiveData 객체에 등록한다.
상태 변화 시 실행될 onChanged() 콜백 메서드를 구현하여 Observer 객체를 생성합니다.
그리고 observe() 함수를 통해 전에 생성했던 LiveData객체에 등록합니다.
이때, Observer 객체는 onCreate() 메서드 내에서 생성되는 것이 권장되며 그 이유는 중복 호출을 방지하고 Activty나 Fragment가 활성화되자마자 표시할 수 있는 데이터를 보장하기 위함입니다.
class MyActivity : AppCompatActivity() {
private val viewModel: MyViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val newObserver = Observer<String> { newName ->
nameTextView.text = newName
}
viewModel.myLiveData.observe(this, newObserver)
}
}
3. setValue(), postValue() 메서드를 통해 값을 갱신한다.
setValue() 함수를 이용하여 값을 갱신할 수 있으며, Observer들의 onChanged() 함수를 작동시킵니다.
button.setOnClickListener {
val newData = "John Doe"
viewModel.myLiveData.setValue(newData)
}
postValue() 함수를 사용하면 Worker Thread에서 LiveData 객체를 갱신할 수 있습니다.
button.setOnClickListener {
val newData = "John Doe"
viewModel.myLiveData.postValue(newData)
}
3-1. Jetpack Compose에서 LiveData를 사용하는 방법
observeAsState() 함수를 이용하여 LiveData 객체를 관찰하여 컴포저블을 업데이트할 수 있습니다.
@Composable
fun StopwatchApp(viewModel: StopwatchViewModel = viewModel()) {
val elapsedTime by viewModel.timeInMillis.observeAsState(0L)
StopwatchScreen(
elapsedTime = elapsedTime,
...
추가 정보
https://developer.android.com/topic/libraries/architecture/livedata
LiveData 개요 | Android Developers
LiveData를 사용하여 수명 주기를 인식하는 방식으로 데이터를 처리합니다.
developer.android.com
2. StateFlow ( MutableStateFlow )
StateFlow는 Kotlin Coroutine에서 사용하기 위한 관찰 가능한 데이터 홀더 클래스입니다.
순수 코틀린으로 작성되어 Android에 종속적이지 않지만, 안드로이드 생명주기를 인식할 수 없습니다.
클린 아키텍처를 위해 많이 사용하는 것 같습니다.
특징
독립성
순수 코틀린 언어로 작성되었기 때문에 Domain Layer에서 사용할 수 있다.
코틀린을 사용하는 다른 플랫폼에서도 동일한 코드를 사용할 수 있도록 만들 수 있다고 이해하면 됩니다.
주의할 점
메모리 누수
UI의 생명 주기가 끝나도 구독을 자동으로 취소할 수 없기 때문에 생명 주기에 따라 구독이 종료되도록 조작해줘야 한다.
StateFlow vs MutableStateFlow
StateFlow 또한 추상 클래스로 값을 수정하는 기능이 비공개되어 있습니다. 따라서 StateFlow 클래스의 구현체인 MutableStateFlow를 통해 값을 수정할 수 있으며, 보통 read-only 기능을 사용하기 위해 둘 다 선언하여 사용합니다.
사용 방법
1. StateFlow 객체를 생성한다.
디폴드 값이 필요합니다.
뷰에서 직접 값을 변경할 수 없도록 StateFlow 객체를 사용합니다.
class MyViewModel : ViewModel() {
private val _myData = MutableStateFlow(0L)
val myData : StateFlow<Long> = _myData
...
}
2. stateflow 객체의 value에 값을 대입하여 객체의 상태를 갱신한다.
viewModelScope를 사용하면 StateFlow의 생명 주기를 ViewModel의 생명 주기와 동기화할 수 있습니다.
...
fun onClick() {
viewModelScope.launch {
_myData.value ++
}
}
3. collect() 함수를 이용해 갱신된 값을 사용한다.
lifeCycleScope를 사용하면 StateFlow의 생명 주기를 Activty/Fragment의 생명 주기와 동기화할 수 있습니다.
...
lifecycleScope.launch {
myViewModel.myData.collect {
textView.text = it.toString()
}
}
3-1. Jetpack Compose에서 StateFlow를 사용하는 방법
collectAsStateWithLifecycle() 함수를 통해 StateFlow 객체가 뷰의 생명 주기에 동기화되도록 만들고 컴포저블을 갱신할 수 있습니다.
@Composable
fun StopwatchApp(viewModel: StopwatchViewModel) {
val elapsedTime by viewModel.timeInMillis.collectAsStateWithLifecycle()
StopwatchScreen(
elapsedTime = elapsedTime,
...
collectAsState() 함수를 사용하여 안드로이드 플랫폼에 대한 의존성 없이 컴포저블을 갱신할 수 있습니다.
@Composable
fun StopwatchApp(viewModel: StopwatchViewModel) {
val elapsedTime by viewModel.timeInMillis.collectAsState()
StopwatchScreen(
elapsedTime = elapsedTime,
...
추가 정보
https://developer.android.com/kotlin/flow/stateflow-and-sharedflow?hl=ko
StateFlow 및 SharedFlow | Kotlin | Android Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. StateFlow 및 SharedFlow 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. StateFlow와 SharedFlow는 흐름에서 최적
developer.android.com
https://medium.com/androiddevelopers/consuming-flows-safely-in-jetpack-compose-cde014d0d5a3
Consuming flows safely in Jetpack Compose
Collecting flows in a lifecycle-aware manner is the recommended way to collect flows on Android. Use collectAsStateWithLifecycle in…
medium.com
3. MutableState
MutableState는 Jetpack Compose 전용 관찰 가능한 데이터 홀더 클래스입니다.
컴포저블에 대해 제일 간편하게 사용할 수 있습니다.
사용 방법
1. MutableState 객체를 생성합니다.
생성을 위한 3가지 방법이 있으며, 디폴드 값이 필요합니다.
val mutableState = remember { mutableStateOf(default) }
var value by remember { mutableStateOf(default) }
val (value, setValue) = remember { mutableStateOf(default) }
2. 컴포저블에서 MutableState 객체를 읽고 씁니다.
일반 변수를 사용하듯이 사용하면 됩니다.
@Composable
fun HelloContent() {
var name by rememeber { mutableStateOf("") }
Text(
text = name,
}
Button(
onclick = {name = ""}
) {
Text("Reset")
}
}
추가 정보
https://developer.android.com/develop/ui/compose/state?hl=ko
상태 및 Jetpack Compose | Android Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. 상태 및 Jetpack Compose 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 앱의 상태는 시간이 지남에 따라
developer.android.com
결론
1. StateFlow 쓰자
2. Only Jetpack Compose면 MutableState도 좋다
StateFlow가 메모리 누수만 주의하면 의존성이 낮기 때문에 LiveData를 대체하여 많이 사용하는 것 같습니다.
Only Compose를 사용하여 개발하는 경우 MutableState를 이용하여 개발하는 것이 더 편해보이며, 범용적인 상황에서 필요한 경우 StateFlow를 이용하여 개발하면 될 것 같습니다.
마치며
안드로이드에서 Kotlin을 지원하면서 점점 플랫폼의 종속성이 낮아지는 것 같습니다.
코루틴에 대해 추가로 알아보면서 상태 관리 데이터 홀더에 대해 더 깊이 공부해보시면 좋을 것 같습니다. 특히 StateFlow에 관해서는 알려드리지 않은 내용이 많습니다.
'프로그래밍 > 안드로이드' 카테고리의 다른 글
[안드로이드] Jetpack Compose에 MVVM 적용하기 (1) | 2024.07.24 |
---|---|
[안드로이드] Jetpack Compose (1) | 2024.07.23 |