| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 |
- compose
- 옵저버패턴
- 클리어에디트텍스트
- 산전수전겪으니체감된다
- ClearEditText
- 안드로이드
- 커스텀뷰
- ViewModel
- 코틀린
- EditText
- 뷰모델
- 옵저빙
- jetpack compose
- Copose 장점
- x버튼에디트텍스트
- 라이브데이터
- LiveData
- 산전수전 겪어보니 체감함
- Android
- 회고록
- lifecycle
- Kotlin
- 2023년 회고록
- 연간 회고록
- 이제 느껴본 신세계
- Today
- Total
SANDY
[android/kotlin] 라이브데이터(LiveData)와 옵저버 패턴(Observer Pattern) 본문
라이브데이터(LiveData)란?
라이브데이터는 관찰 가능(Observable)한 데이터 홀더 클래스이다.
라이브데이터를 Live라는 단어 때문에 데이터가 살아있는 것처럼 계속 바뀌기 때문에 라이브데이터라고 단편적인 오해를 하기 쉽다. 하지만, 라이브데이터는 Live의 개념보다 Observable의 개념으로 접근해야 한다. 데이터가 변해서(변할 수 있어서) “라이브데이터”를 쓴다는 개념이 아니라, 데이터 관찰이 가능해서 LiveData이다. 생방송을 Live방송 이라고 하는데, 송출된 라이브 방송을 실시간으로 시청한다. 우리는 방송을 보는(observing) 시청자(observer)가 되며, 바로 이 “시청(observe)”의 개념으로 접근해서 라이브데이터를 이해해야 한다.
class MainViewModel : ViewModel() {
private val _totalMoney = MutableLiveData<Int>()
val totalMoney: LiveData<Int>
get() = _totalMoney
}
class MainActivity : AppCompatActivity() {
private val viewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
viewModel.totalMoney.observe(this, {
if(it > 0) money.text = "$it 원"
})
}
}
-라이브데이터 사용 예시-
라이브데이터의 원리와 옵저버패턴
라이브데이터의 형태는 LiveData<T>이다. 제네릭<T>을 이용해서 라이브데이터에 저장되는 타입(T)을 지정해서 명시한다.
라이브데이터에 데이터를 저장하고, 데이터를 업데이트하거나 삭제할 수 있다.
라이브데이터에 저장된 값이 변경될 때, 라이브데이터는 Observer 객체에 알리게 되고(notification), Observe를 통해 새로 바뀐 newValue값을 관찰하게 된다. 이를 옵저버패턴(Obeserver Pattern)이라고 한다.

라이브데이터의 장점
- UI와 데이터 상태의 일치 보장
LiveData는 옵저버패턴을 따르며, LiveData는 기본 데이터가 변경될 때 Observer 객체에 알린다. 이 때, Observer 객체에서 UI를 업데이트할 수 있다. 이렇게 하면 앱 데이터가 변경될 때마다 관찰자가 대신 UI를 업데이트하므로 개발자가 업데이트할 필요가 없다. - 메모리 누수 문제
Observer는 Lifecycle 객체에 결합되어 있으며 연결된 라이프사이클이 끝나면 자동으로 삭제된다. - 중지된 액티비티로 인한 비정상 종료 문제
액티비티가 백스택에 있을 때를 비롯하여 Observer의 라이플사이클이 비활성 상태에 있으면 Observer는 어떤 LiveData 이벤트도 받지 않는다. - 라이프사이클을 더이상 수동으로 처리하지 않음
UI 구성요소는 관련 데이터를 관찰하기만 할 뿐 관찰을 중지하거나 다시 시작하지 않는다. LiveData는 관찰하는 동안 관련 라이플사이클 상태의 변경을 인식하므로 이 모든 것을 자동으로 관리한다. - 최신 데이터 유지
라이프사이클 주기가 비활성화되면 다시 활성화될 때 최신 데이터를 수신한다. 예를 들어 백그라운드에 있었던 액티비티는 포그라운드로 돌아온 직후 최신 데이터를 받는다. - 적절한 구성 변경
기기 회전과 같은 구성 변경으로 인해 액티비티 또는 프래그먼트가 다시 생성되면 사용 가능한 최신 데이터를 즉시 받게 된다.
출처 : android developer 문서
LiveData vs MutableLiveData
라이브데이터는 ①LiveData와 ②MutableLiveData라는 두가지 종류가 있다.
① LiveData : 초기화 이후로 라이브데이터값(value)을 변경할 수 없는 라이브데이터
② MutableLiveData : 라이브데이터값(value)을 변경할 수 있는 라이브데이터
라이브데이터 형태 : LiveData<T> / MutableLiveData<T>
제네릭<T>을 이용해서 라이브데이터에 저장되는 타입(T)을 지정해서 명시한다.
private val mutableLiveData = MutableLiveData<Int>()
val liveData : LiveData<Int> get() = mutableLiveData
* 라이브데이터에 저장할 수 있는 타입은 Int, Boolean, Float, Long과 같은 원시타입(primitive type) 자료형 뿐만 아니라, String, List, DataClass와 같은 객체 타입도 모두 가능(Any)하다.
권장사항) getter 프로퍼티를 이용하여 외부에서 LiveData 객체를 통해서만 라이브데이터에 접근할 수 있게한다.
외부에서 내부의 라이브데이터를 접근할 때 LiveData객체를 통해서만 접근할 수 있게 하고 내부에서만 MutableLiveData객체를 이용하여 LiveData의 value를 업데이트해야 한다.
class MainViewModel : ViewModel() {
private val _totalMoney = MutableLiveData<Int>()
val totalMoney: LiveData<Int>
get() = _totalMoney
}
class MainActivity : AppCompatActivity() {
private val viewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
viewModel.totalMoney.observe(this, {
if(it > 0) money.text = "$it 원"
})
}
}
외부에서 totalMoney에 대한 라이브데이터 객체에 접근할 때,
_totalMoney객체(mutableLiveData)가 아닌 totalMoney객체(liveData)를 통해 접근한다.
getValue() : 라이브데이터에 현재 저장된 value값 가져오기
MutableLiveData, LiveData 모두 getValue()을 이용하여 라이브데이터에 저장되어있는 현재 value값을 가져올 수 있다.
val mutableLiveDataValue = mutableLiveData.value
val liveDataValue = liveData.value
setValue(value), postValue(value) : 라이브데이터 value값 업데이트 하기
라이브데이터 값의 변경은 무조건 MutableLiveData만 가능하다.
setValue(value)와 postValue(value)두가지 함수를 이용하여 값을 변경할 수 있다.
초기 라이브데이터값 설정은 setValue(value)로, 이후 변화된 라이브데이터값 저장은 postValue(value) 함수를 이용하여 코드 가독성을 올리는 것을 권장하고 있다. 코틀린의 경우 프로퍼티가 명료하여 postValue, setValue 구분하지 않고 그냥 프로퍼티로 쓰는 경우가 많았다.
private val mutableLiveData = MutableLiveData<Int>()
mutableLiveData.postValue(3)
private val mutableLiveData = MutableLiveData<Int>()
mutableLiveData.value = 3
라이브데이터 옵저빙하기
라이브데이터를 observe하여 라이브데이터를 관찰 할 수 있다. 라이브 데이터를 옵저빙하기 위해서는 ①옵저빙의 LifeCycleOwner와 ②Observer객체(람다 익명함수로 사용 가능)이 필요하다.
- 액티비티 : this
- 프래그먼트 : viewLifecycleOwner
② Observer 객체 : 라이브데이터 변화가 있을 때 실행되어야 하는 명령구문을 가지고 있음
observe : LifecycleOwner가 반드시 필요함
- 방법 1) 람다 익명함수를 이용한 옵저버 객체 구현
LiveData.observe(LifecycleOwner, { it : T ->
라이브 데이터 값이 변화가 있을 때 실행되어야 할 동작
})
class MainActivity : AppCompatActivity() {
...
totalMoney.observe(this, {
if(it > 0) money.text = "$totalMoney 원"
})
...
}
class BlankFragment : Fragment() {
...
totalMoney.observe(viewLifecycleOwner, {
if(it > 0) money.text = "$totalMoney 원"
})
...
}
- 방법 2) 옵저버 객체를 따로 만들어서 구현
- 옵저버 객체 만들기
Observer객체는 옵저빙의 대상이 되는 자료형을 제네릭으로 명시해줘야 한다. 옵저빙 해야 할 라이브데이터 데이터의 자료형과 동일하게 명시해주면 된다.val liveDataObserver = Observer<T> { it : T -> 라이브 데이터 값이 변화가 있을 때 실행되어야 할 동작 } - observe 달아주기
옵저빙의 라이프사이클의 오너와 옵저버 객체만 넣어주면 됩니다. (방법1과 동일)liveData.observe(lifecycleOwner, liveDataObserver)
class MainActivity : AppCompatActivity() {
private val liveData = MutableLiveData<Int>()
lateinit var money : TextView
val liveDataObserver = Observer<Int> {
if(it > 0) money.text = "$totalMoney 원"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
money = findViewById<TextView>(R.id.money)
liveData.observe(this, liveDataObserver)
}
override fun onPause() {
super.onPause()
liveData.removeObserver(liveDataObserver)
}
}
removeObserver()를 통해 라이브데이터에 연결된 옵저버 객체의 라이프사이클을 강제로 종료할 수 있다. 옵저빙을 강제로 종료해야 할 상황을 구현해야 할 때는 이처럼 옵저버 객체를 따로 만들어서 활용할 수 있다.
observeForever : LifecycleOwner가 없음
화면(액티비티, 프래그먼트)가 아닌 다른 일반 클래스에서는 라이프사이클이 존재하지 않아 LifecycleOwner가 없다. 이 경우 observeForever를 이용하여 라이브데이터를 옵저빙할 수 있다.
- 방법 1) 람다 익명함수를 이용한 옵저버 객체 구현
LiveData.observeForever { it : T ->
라이브 데이터값이 변화가 있을 때 실행되어야 할 동작
}
- 방법 2) 옵저버 객체를 따로 만들어서 구현
val liveDataObserver = Observer<T> { it : T ->
라이브 데이터 값이 변화가 있을 때 실행되어야 할 동작
}
LiveData.observeForever(liveDataObserver)
class MainViewModel : ViewModel() {
val number = MutableLiveData<Int>()
val text = MutableLiveData<String>()
private val numberObserver = Observer<Int> { it : Int? ->
text.value = if(it > 0) "0보다 큽니다" else "0보다 크지 않습니다"
}
init {
number.observeForever(numberObserver)
}
public override fun onCleared() {
super.onCleared()
number.removeObserver(numberObserver)
}
}
observeForever 사용시 메모리 누수 방지를 위해 반드시 removeObserver()동작을 구현해줘야 한다. 따라서 람다함수로 Observer를 구현하는 것보다 Observer객체를 따로 만드는 것을 더 지향한다.