각 애플리케이션에는 레이어가 두 개 이상 있어야 한다.
UI 레이어
UI 레이어의 역할은 화면에 애플리케이션 데이터를 표시하는 것이다. 사용자 상호작용(ex: 버튼 누르기) 또는 외부입력(ex: 네트워크 응답)으로 인해 데이터가 변할 때마다 변경사항을 반영하도록 UI가 업데이트되어야 한다.
UI 레이어는 다음 두가지로 구성된다.
Data 레이어
Data 레이어에는 비즈니스 로직이 포함되어 있다.
Data 레이어는 0개부터 여러 개의 데이터 소스를 각각 포함할 수 있는 저장소로 구성된다. 앱에서 처리하는 다양한 유형의 데이터마다 저장소 클래스를 만들어야 한다. 예를 들어 영화 관련 데이터는 MoviesRepository
클래스, 결제 관련 데이터에는 PaymentsRepository
클래스를 만들 수 있다.
저장소 클래스에서 담당하는 작업은 다음과 같다.
ViewModel 클래스는 수명 주기를 고려하여 UI 관련 데이터를 저장하고 관리하도록 설계되었다.
ViewModel 클래스를 사용하면 화면 전환과 같이 구성을 변경할 때도 데이터를 유지할 수 있다.
UI 컨트롤러 로직에서 뷰 데이터 소유권을 분리하는 방법이 쉽고 효율적인 이유는 아래와 같다.
LiveData는 Data의 변경을 관찰할 수 있는 Data Holder 클래스이다.
Activity나 Fragment, 서비스 등과 같은 안드로이드 컴포넌트의 생명주기를 인식하며 그에따라 LiveData는 활성상태(active)일때만 데이터를 업데이트(Update) 한다.
*활성상태란? STARTED 또는 RESUMED를 의미한다.
또한, LiveData 객체는 Observer 객체와 함께 사용된다. LiveData가 가지고 있는 데이터에 변화가 일어날 경우, LiveData에 등록된 Observer 객체에 변화를 알려주고, Observer의 onChanged() 메서드가 실행되게 된다.
LiveData의 장점
NumberViewModel
class NumverViewModel : ViewModel() {}
ViewModel 클래스를 상속하는 서브 클래스를 정의한다.
MainActivity
class MainActivity : AppCompatActivity() {
lateinit var numberViewModel: NumberViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
numberViewModel = ViewModelProvider(this).get(NumberViewModel::class.java)
ViewModel을 생성하기 위해서는 ViewModel Provider 객체가 필요하다. 매개변수에는 lifecycle을 가지고 있는 것을 넣어준다. (즉, 자기 자신)
그리고 가져오고 싶은 ViewModel 클래스를 넣어 ViewModel을 가져온다.
메인 액티비티가 실행되고 나면 ViewModel Provider를 통해 ViewModel을 가져오면서 생성자가 호출된다.
NumberViewModel
class NumberViewModel : ViewModel() {
// 내부에서 설정하는 자료형은 mutable로 변경 가능하도록 설정
private val _currentValue = MutableLiveData<Int>()
// 변경되지 않는 데이터를 가져올 땐 이름을 _언더스코어 없이 설정
val currentValue: LiveData<Int>
get() = _currentValue
// 초기값 설정
init {
_currentValue.value = 0
}
// ViewModel이 가지고 있는 값을 변경하기 위해 updateValue 메서드를 구현
fun updateValue(actionType: ActionType, input: Int) {
when(actionType) {
ActionType.PLUS ->
_currentValue.value = _currentValue.value?.plus(input)
ActionType.MINUS ->
_currentValue.value = _currentValue.value?.minus(input)
}
}
}
생성자가 호출되면서 ViewModel이 가지고 있는 Mutable 데이터의 값을 설정한다.
MainActivity
class MainActivity : AppCompatActivity(), View.OnClickListener {
private lateinit var binding: ActivityMainBinding
lateinit var numberViewModel: NumberViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
numberViewModel = ViewModelProvider(this).get(NumberViewModel::class.java)
numberViewModel.currentValue.observe(this, Observer {
binding.numberTextVIew.text = it.toString()
})
// 리스너 연결
binding.plusButton.setOnClickListener(this)
binding.minusButton.setOnClickListener(this)
}
// 클릭
override fun onClick(view: View?) {
val userInput = binding.inputNumberEditText.text.toString().toInt()
// ViewModel에 LiveData 값을 변경하는 메서드 실행
when(view) {
binding.plusButton ->
numberViewModel.updateValue(actionType = ActionType.PLUS, userInput)
binding.minusButton ->
numberViewModel.updateValue(actionType = ActionType.MINUS, userInput)
}
}
}
그리고 나서 메인 액티비티쪽에서 plusButton을 누룰 때 ViewModel이 가지고 있는 updateValue() 메서드를 실행한다. 그렇게 되면 현재 Mutable 데이터가 가지고 있는 값에 input만큼 값을 추가해주게 된다.
값이 변경됐을 때 ViewModel이 가지고 있는 LiveData가 변경되었을 때 값을 받겠다는 것을 observe() 메서드를 통해 설정한다.
numberViewModel.currentValue.observe(this, Observer {
findViewById<TextView>(R.id.numberTextVIew).text = it.toString()
})
참고