Android Kotlin 기초의 내용을 번역하며 정리한 자료입니다.
LiveData는 수명 주기를 인식하는 관찰 가능한 데이터 홀더 클래스입니다. 예를 들어, GuessTheWord 앱에서 현재 점수 주위에 LiveData를 래핑할 수 있습니다. 여기서는 LiveData의 몇 가지 특성에 대해 알아봅니다.
LiveData는 관찰 가능합니다. 즉, LiveData 개체가 보유한 데이터가 변경될 때 Observer에게 알림이 전송됩니다.
LiveData는 데이터를 보유합니다. LiveData는 모든 데이터와 함께 사용할 수 있는 래퍼입니다.
LiveData는 수명 주기를 인식합니다. LiveData에 Observer를 연결하면 Observer는 LifecycleOwner(일반적으로 Activity 또는 Fragment)와 연결됩니다. LiveData는 STARTED 또는 RESUMED와 같은 활성 수명 주기 상태에 있는 Observer만 업데이트합니다. 여기에서 LiveData 및 Observe에 대해 자세히 알아볼 수 있습니다.
이 작업에서는 GameViewModel의 현재 점수 및 현재 단어 데이터를 LiveData로 변환하여 모든 데이터 유형을 LiveData 개체로 래핑하는 방법을 배웁니다. 이후 작업에서 이러한 LiveData 개체에 Observer를 추가하고 LiveData를 관찰하는 방법을 배웁니다.
var word = MutableLiveData<String>()
var score = MutableLiveData<Int>()
init {
word.value = ""
score.value = 0
...
}
score 및 word 변수는 이제 LiveData 유형입니다. 이 단계에서는 value 속성을 사용하여 이러한 변수에 대한 참조를 변경합니다.
fun onSkip() {
score.value = (score.value)?.minus(1)
nextWord()
}
fun onCorrect() {
score.value = (score.value)?.plus(1)
nextWord()
}
private fun nextWord() {
if (!wordList.isEmpty()) {
//Select and remove a word from the list
word.value = wordList.removeAt(0)
}
}
/** Methods for updating the UI **/
private fun updateWordText() {
binding.wordText.text = viewModel.word.value
}
private fun updateScoreText() {
binding.scoreText.text = viewModel.score.value.toString()
}
private fun gameFinished() {
Toast.makeText(activity, "Game has just finished", Toast.LENGTH_SHORT).show()
val action = GameFragmentDirections.actionGameToScore()
action.score = viewModel.score.value?:0
NavHostFragment.findNavController(this).navigate(action)
}
이 작업은 score와 word 데이터를 LiveData 개체로 변환한 이전 작업과 밀접하게 관련되어 있습니다. 이 작업에서는 Observer 개체를 해당 LiveData 개체에 연결합니다. fragment view(viewLifecycleOwner)를 LifecycleOwner로 사용합니다.
viewLifecycleOwner를 사용하는 이유는 무엇입니까?
프래그먼트 자체가 파괴되지 않더라도 사용자가 프래그먼트에서 멀어지면 프래그먼트 뷰가 파괴됩니다. 이것은 본질적으로 프래그먼트의 라이프사이클과 프래그먼트 뷰의 라이프사이클이라는 두 가지 라이프사이클을 생성합니다. 프래그먼트 뷰의 수명 주기 대신 프래그먼트의 수명 주기를 참조하면 프래그먼트 뷰를 업데이트할 때 미묘한 버그가 발생할 수 있습니다.
viewModel.score.observe(viewLifecycleOwner, Observer { newScore ->
})
viewModel.score.observe(viewLifecycleOwner, Observer { newScore -> binding.scoreText.text = newScore.toString()
})
/** Setting up LiveData observation relationship **/
viewModel.word.observe(viewLifecycleOwner, Observer { newWord ->
binding.wordText.text = newWord
})
점수 또는 단어의 값이 변경되면 화면에 표시되는 점수 또는 단어가 자동으로 업데이트됩니다.
캡슐화는 일부 개체 필드에 대한 직접 액세스를 제한하는 방법입니다. 개체를 캡슐화하면 개인 내부 필드를 수정하는 공용 메서드 집합이 노출됩니다. 캡슐화를 사용하여 다른 클래스가 이러한 내부 필드를 조작하는 방법을 제어합니다.
현재 코드에서 모든 외부 클래스는 value 속성을 사용하여 점수 및 단어 변수를 수정할 수 있습니다(예: viewModel.score.value 사용). 이 코드랩에서 개발 중인 앱에서는 중요하지 않을 수 있지만 프로덕션 앱에서는 ViewModel 개체의 데이터를 제어하려고 합니다.
ViewModel만 앱의 데이터를 편집해야 합니다. 그러나 UI 컨트롤러는 데이터를 읽어야 하므로 데이터 필드가 완전히 비공개일 수는 없습니다. 앱의 데이터를 캡슐화하려면 MutableLiveData 및 LiveData 개체를 모두 사용합니다.
MutableLiveData vs. LiveData:
이 전략을 수행하려면 Kotlin 지원 속성을 사용합니다. 지원 속성을 사용하면 정확한 객체가 아닌 다른 getter에서 무언가를 반환할 수 있습니다. 이 작업에서 당신은 guessTheWord 앱에서 점수와 단어 개체에 대한 지원 속성을 구현합니다.
// The current score
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>
오류를 해결하려면 GameViewModel에서 점수 개체에 대한 get() 메서드를 재정의하고 지원 속성인 _score를 반환합니다.
val score: LiveData<Int>
get() = _score
init {
...
_score.value = 0
...
}
...
fun onSkip() {
_score.value = (score.value)?.minus(1)
...
}
fun onCorrect() {
_score.value = (score.value)?.plus(1)
...
}
// The current word
private val _word = MutableLiveData<String>()
val word: LiveData<String>
get() = _word
...
init {
_word.value = ""
...
}
...
private fun nextWord() {
if (!wordList.isEmpty()) {
//Select and remove a word from the list
_word.value = wordList.removeAt(0)
}
}
LiveData 개체 word와 score를 캡슐화했습니다.
사용자가 게임 종료 버튼을 탭하면 현재 앱이 점수 화면으로 이동합니다. 또한 플레이어가 모든 단어를 순환했을 때 앱이 점수 화면으로 이동하기를 원합니다. 플레이어가 마지막 단어를 끝낸 후 사용자가 버튼을 탭할 필요가 없도록 게임이 자동으로 종료되기를 원합니다.
이 기능을 구현하려면 모든 단어가 표시되었을 때 이벤트가 트리거되고 ViewModel에서 Fragment로 전달되어야 합니다. 이를 위해 LiveData Observer 패턴을 사용하여 게임 종료 이벤트를 모델링합니다.
The observer pattern
observer pattern은 소프트웨어 디자인 패턴입니다. 관찰 대상(관찰의 "주제")과 관찰자 간의 통신을 지정합니다. 옵저버블은 옵저버에게 상태 변화를 알리는 객체입니다.

이 앱의 LiveData의 경우 옵저버블(subject)은 LiveData 객체이고 옵저버는 프래그먼트와 같은 UI 컨트롤러의 메서드입니다. 상태 변경은 LiveData 내부에 래핑된 데이터가 변경될 때마다 발생합니다. LiveData 클래스는 ViewModel에서 Fragment로 통신하는 데 중요합니다.
이 작업에서는 LiveData 관찰자 패턴을 사용하여 게임 종료 이벤트를 모델링합니다.
// Event which triggers the end of the game
private val _eventGameFinish = MutableLiveData<Boolean>()
val eventGameFinish: LiveData<Boolean>
get() = _eventGameFinish
/** Method for the game completed event **/
fun onGameFinish() {
_eventGameFinish.value = true
}
private fun nextWord() {
if (wordList.isEmpty()) {
onGameFinish()
} else {
//Select and remove a _word from the list
_word.value = wordList.removeAt(0)
}
}
단어 목록이 비어 있으면 eventGameFinish가 설정되고 GameFragment의 관련 관찰자 메서드가 호출되며 앱은 TitleFragment로 이동합니다.
private fun gameFinished() {
Toast.makeText(activity, "Game has just finished", Toast.LENGTH_SHORT).show()
// val action = GameFragmentDirections.actionGameToScore()
// action.score = viewModel.score.value?:0
// NavHostFragment.findNavController(this).navigate(action)
}
이제 장치 또는 에뮬레이터를 회전합니다. 토스트가 다시 등장! 기기를 몇 번 더 돌리면 매번 토스트가 표시될 것입니다. 토스트는 게임이 끝나면 한 번만 표시되어야 하기 때문에 이것은 버그입니다. 프래그먼트가 다시 생성될 때마다 알림이 표시되지 않아야 합니다. 다음 작업에서 이 문제를 해결합니다.
일반적으로 LiveData는 데이터가 변경될 때만 관찰자에게 업데이트를 전달합니다. 이 동작의 예외는 관찰자가 비활성 상태에서 활성 상태로 변경될 때 관찰자도 업데이트를 수신한다는 것입니다.
이것이 게임 종료 토스트가 앱에서 반복적으로 실행되는 이유입니다. 게임 프래그먼트가 화면 회전 후 다시 생성되면 비활성 상태에서 활성 상태로 이동합니다. 프래그먼트의 옵저버는 기존 ViewModel에 다시 연결되어 현재 데이터를 수신합니다. gameFinished() 메서드가 다시 트리거되고 알림이 표시됩니다.
이 작업에서는 GameViewModel에서 eventGameFinish 플래그를 재설정하여 이 문제를 수정하고 토스트를 한 번만 표시합니다.
/** Method for the game completed event **/
fun onGameFinishComplete() {
_eventGameFinish.value = false
}
private fun gameFinished() {
...
viewModel.onGameFinishComplete()
}
private fun gameFinished() {
Toast.makeText(activity, "Game has just finished", Toast.LENGTH_SHORT).show()
val action = GameFragmentDirections.actionGameToScore()
action.score = viewModel.score.value?:0
findNavController(this).navigate(action)
viewModel.onGameFinishComplete()
}
앱은 LiveData를 사용하여 게임 종료 이벤트를 트리거하여 GameViewModel에서 단어 목록이 비어 있음을 GameFragment으로 전달합니다. 그런 다음 GameFragment은 ScoreFragment으로 이동합니다.
이 작업에서는 ScoreViewModel에서 score를 LiveData 개체로 변경하고 관찰자를 연결합니다. 이 작업은 LiveData를 GameViewModel에 추가할 때 수행한 작업과 유사합니다.
앱의 모든 데이터가 LiveData를 사용하도록 하기 위해 ScoreViewModel을 변경합니다.
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>
get() = _score
init {
_score.value = finalScore
}
// Add observer for score
viewModel.score.observe(viewLifecycleOwner, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})
// remove code
binding.scoreText.text = viewModel.score.toString()
이 작업에서는 점수 화면에 Play Again 버튼을 추가하고 LiveData 이벤트를 사용하여 clickListener를 구현합니다. 버튼은 점수 화면에서 게임 화면으로 이동하는 이벤트를 트리거합니다.
<Button
android:id="@+id/play_again_button"
...
android:visibility="visible"
/>
private val _eventPlayAgain = MutableLiveData<Boolean>()
val eventPlayAgain: LiveData<Boolean>
get() = _eventPlayAgain
fun onPlayAgain() {
_eventPlayAgain.value = true
}
fun onPlayAgainComplete() {
_eventPlayAgain.value = false
}
// Navigates back to game when button is pressed
viewModel.eventPlayAgain.observe(viewLifecycleOwner, Observer { playAgain ->
if (playAgain) {
findNavController().navigate(ScoreFragmentDirections.actionRestart())
viewModel.onPlayAgainComplete()
}
})
binding.playAgainButton.setOnClickListener { viewModel.onPlayAgain() }
LiveData
LiveData는 Android 아키텍처 구성 요소 중 하나인 수명 주기를 인식하는 관찰 가능한 데이터 홀더 클래스입니다.
LiveData를 사용하여 데이터가 업데이트될 때 UI가 자동으로 업데이트되도록 할 수 있습니다.
LiveData는 관찰 가능합니다. 즉, LiveData 개체가 보유하고 있는 데이터가 변경될 때 Activity나 Fragment와 같은 관찰자에게 알릴 수 있습니다.
LiveData는 데이터를 보유합니다. 모든 데이터와 함께 사용할 수 있는 래퍼입니다.
LiveData는 수명 주기를 인식하므로 STARTED 또는 RESUMED와 같은 활성 수명 주기 상태에 있는 관찰자만 업데이트합니다.
To add LiveData
ViewModel의 데이터 변수 유형을 LiveData 또는 MutableLiveData로 변경합니다.
LiveData가 보유한 데이터의 값을 변경하려면 LiveData 변수에서 setValue() 메소드를 사용하십시오.
To encapsulate LiveData
ViewModel 내부의 LiveData는 편집 가능해야 합니다. ViewModel 외부에서 LiveData를 읽을 수 있어야 합니다. 이것은 Kotlin 지원 속성을 사용하여 구현할 수 있습니다.
Kotlin 지원 속성을 사용하면 정확한 객체가 아닌 getter에서 무언가를 반환할 수 있습니다.
LiveData를 캡슐화하려면 ViewModel 내부에서 비공개 MutableLiveData를 사용하고 ViewModel 외부에서 LiveData 지원 속성을 반환합니다.
Observable LiveData
LiveData는 관찰자 패턴을 따릅니다. "Observable"은 LiveData 개체이고 관찰자는 Fragment 같은 UI 컨트롤러의 메서드입니다. LiveData 내부에 래핑된 데이터가 변경될 때마다 UI 컨트롤러의 관찰자 메서드에 알림이 전송됩니다.
LiveData를 Observable하게 만들려면 관찰자(예: 액티비티 및 프래그먼트) 메서드를 사용하여 관찰자의 LiveData 참조에 관찰자 객체를 연결합니다.
이 LiveData 관찰자 패턴은 ViewModel에서 UI 컨트롤러로 통신하는 데 사용할 수 있습니다.