LiveData
LiveData는 관찰 가능한 데이터 홀더 클래스이다. 일반 클래스와 달리 수명주기를 인식한다.
Observer 클래스로 표현되는 관찰자의 수명 주기가 STARTED, RESUMED 상태이면 LiveData는 관찰자를 활성 상태로 간주하고, 활성 상태인 관찰자에게만 업데이트 정보를 알린다.
LifeCyclerOwner 인터페이스를 구현하는 객체와 페어링 된 관찰자를 등록할 수 있다. 이 관계를 사용하면 관찰자에 대응되는 LifeCycle 객체의 상태가 Destroyed로 변경될 때 관찰자를 삭제할 수 있다.
이는 특히, 액티비티 및 프래그먼트에 유용한데, LiveData를 안전하게 관찰할 수 있고 수명 주기가 끝나는 즉시 수신 거부되어 메모리 누수에 대한 이슈가 없기 때문이다.
LiveData의 이점
- UI와 데이터 상태의 일치 보장
: LiveData는 관찰자 패턴을 따르므로, LiveData는 기본 데이터가 변경될 때 Observer 객체에 알린다. 코드를 통합하여 이러한 Observer 객체에 UI를 업데이트할 수 있다. 앱 데이터가 변경될 때마다 관찰자가 대신 UI를 업데이트하므로 개발자가 업데이트할 필요가 없다. - 메모리 누수 없음
: 관찰자는 LifeCycle 객체에 결합되어 있으므로 수명 주기가 끝나면 자동으로 삭제된다. - 중지된 액티비티로 인한 비정상 종료 없음
: 활동이 백 스택에 있을 때를 비롯하여 관찰자의 수명 주기가 비활성 상태에 있으면 관찰자는 어떤 LiveData 이벤트도 받지 않는다. - 수명 주기를 더 이상 수동으로 처리하지 않음
: UI 구성요소는 관련 데이터를 관찰하기만 할 뿐 관찰을 중지하거나 다시 시작하지 않는다. LiveData는 관찰하는 동안 관련 수명 주기 상태의 변경을 인식하므로 이 모든 것을 자동으로 관리한다. - 최신 데이터 유지
: 수명 주기가 비활성화되면 다시 활성화될 때 최신 데이터를 수신한다. 예를 들어 백그라운드에 있었던 액티비티는 포그라운드로 돌아온 직후 최신 데이터를 받는다. - 적절한 구성 변경
: 기기 회전과 같은 구성 변경으로 인해 활동 또는 프래그먼트가 다시 생성되면 사용 가능한 최신 데이터를 즉시 받게 된다. - 리소스 공유
: 앱에서 시스템 서비스를 공유할 수 있도록 싱글톤 패턴을 사용하는 LiveData를 확장하여 시스템 서비스를 래핑 할 수 있다. LiveData 객체가 시스템 서비스에 한번 연결되면 리소스가 필요한 모든 관찰자가 LiveData 객체를 볼 수 있다.
LiveData의 활용
LiveData는 Collections를 구현하는 List와 같은 객체를 비롯하여 모든 데이터와 함께 사용할 수 있는 래퍼이다.
LiveData 객체는 일반적으로 ViewModel 객체 내에 저장된다. 보통 LiveData는 데이터 바인딩, 뷰 모델과 함께 사용되는 경우가 많다.
아래의 코드는 간단하게 DataBinding, ViewModel, LiveData를 사용하여 단어가 랜덤 하게 바뀌는 프로그램이다.
build.gradle
buildFeatures {
dataBinding true
}
먼저, dataBinding을 사용하기 위한 설정을 해주자
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="vm"
type="com.pys.ex_viewmodel_livedata.MainViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<TextView
android:text="@{vm.liveWord}"
android:textSize="50sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text="@{vm.randomLiveWord}"
android:textSize="50sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text="@{vm.newData}"
android:textSize="50sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/btnNext"
android:text="next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
MainViewModel.kt
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModel
class MainViewModel : ViewModel() {
private var _mutableWord = MutableLiveData("")
val liveWord : LiveData<String>
get() = _mutableWord
private var _randomMutableWord = MutableLiveData("")
val randomLiveWord : LiveData<String>
get() = _randomMutableWord
val newData = Transformations.switchMap(liveWord) {
getRandomWordShuffled(it)
}
init {
getNextData()
}
fun getNextData() {
val currentWord = testDataList.random()
val randomWord = currentWord.toCharArray()
randomWord.shuffle()
_mutableWord.value = currentWord
_randomMutableWord.value = String(randomWord)
}
fun getRandomWordShuffled(word : String) : MutableLiveData<String> {
val liveData = MutableLiveData("")
val randomTextWord = word.toCharArray()
randomTextWord.shuffle()
liveData.value = String(randomTextWord)
return liveData
}
}
MainActivity.kt
// LiveData + DataBinding + ViewModel
class MainActivity : AppCompatActivity() {
private lateinit var binding : ActivityMainBinding
private lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
viewModel = ViewModelProvider(this)[MainViewModel::class.java]
binding.vm = viewModel
binding.lifecycleOwner = this
binding.btnNext.setOnClickListener {
viewModel.getNextData()
}
viewModel.liveWord.observe(this, Observer {
})
}
}
TestData.kt
val testDataList : List<String> = listOf(
"apple",
"strawberry",
"pineapple",
"peach",
"grape",
"melon",
"mango"
)
참고 링크
LiveData 개요 | Android 개발자 | Android Developers
LiveData를 사용하여 수명 주기를 인식하는 방식으로 데이터를 처리합니다.
developer.android.com