파고들기의 제 2화!! 두 번째 이야기! Android Architecture Components(AAC)에 대해서 다뤄보자.
안드로이드 개발을 하다보면 AAC라는 키워드를 자주 보게 된다.
기업들의 채용공고들을 보면 MVVM, AAC에 대한 이해 및 서비스 적용 및 경험이 있는 분을 모집한다는 글도 자주 볼 수 있다.
안드로이드 공식문서를 살펴보면 다음과 같은 안드로이드 앱 아키텍쳐 패턴으로 개발하기를 권장하고 있다.
위와 같이 Android Architecture Components를 활용하여 관심사를 분리하고 개발하기를 권장하고 있다.
안드로이드는 여러 컴포넌트들이 있고, 생명주기가 다르게 얽혀있어 앱을 만들기 위해서는 위와 같은 컴포넌트들을 연결해주어야 하는데 이는 개발자의 역량에 따라서 달라질 수 밖에 없었다. 따라서 구글에서 AAC를 공개했다.
- DataBinding
- LifeCycle
- Navigation
- Room
- Paging
- WorkManager
- LiveData
- ViewModel
블로그 JETPACK 카테고리에서 Paging빼고는 모두 다뤄보고 정리하는 시간을 가졌다.
이번 글은 파고들기인 만큼 해당 기술이 왜 사용되고, 어떻게 활용되는지 파고들어보자!!
일단 내용이 많은 만큼 이번 글에서 DataBinding, LifeCycle, LiveData, ViewModel 에 대해 다뤄볼 예정이다!!
먼저 가장 기초가 될 수 있은 ViewBinding부터 살짝 짚어보자.
[ViewBinding]
뷰바인딩 이전에는 우리는 뷰의 요소를 불러오기 위해서는 findViewById를 사용하였다.
이도 kotlin-android-extensions를 사용하면 findViewById를 생략하고 간편하게 쓸 수 있었다.
단순히 뷰의 id로만 접근하여 사용할 수 있는데 왜 문제가 되었을까?
바로 서로다른 xml파일에서 id를 동일하게 사용할 수 있기 때문에 위와 같은 방법으로 사용한다면 코드가 헷갈릴 위험이 있다
그래서 구글에서는 kotlin-android-extensions의 지원을 중단하고 뷰바인딩을 사용하도록 안내하고 있다.
그럼 뷰 바인딩이란?
뷰 바인딩을 활성화하게되면 xml파일에 대해서 ViewBinding클래스를 상속받는 개별 뷰 바인딩 클래스가 자동으로 생성된다.
따라서 우리는 onCreate() 안에서 뷰 바인딩 클래스의 인스턴스를 생성하고,
binding.root로 xml의 가장 높은 부분을 참조하여 그 밑의 부분들을 프로퍼티처럼 사용할 수 있게 된다!
[LifeCycle]
안드로이드는 모바일 앱 특성상, 앱을 실행하다가 갑자기 전화가 오거나, 갑자기 핸드폰이 꺼지거나, 앱 화면이 돌려지는 현상이 발생할 수 있다. 이 때 다시 앱을 활성화 시키면 앱이 다시 실행되거나, 데이터가 사라지는 문제가 생길 수 있다.
따라서 구글은 안드로이드 액티비티 상태를 용이하게 관리할 수 있도록 생명주기를 만들었다.
LifeCycle에 따르면 액티비티가 실행되면 onCreate()가 실행되고, 앱이 다른 화면에 밀려나면 onPause가 실행되고 앱이 꺼지면 onDestroy이 발생한다.
예를 들어, 버튼을 누르면 숫자가 올라가는 앱이라고 가정하였을 때 ViewModel 없이 액티비티 안에서 초기화가 이루어지고 숫자가 올라가는 기능까지 포함되어 있다고 한다면 화면을 돌려서 전환하였을 때 숫자가 초기값으로 리셋되는 경우가 발생한다.
왜냐하면 위의 말했던 이유처럼 액티비티가 파괴되고 다시 실행되기 때문에 초기값이 다시 초기화가 이루어지는 것이다.
이 휘발성인 데이터를 onSaveInstanceState를 사용하여 이를 방지할 수 있다.
데이터를 저장하고 null체크 한 뒤, 그 뒤에 값을 가져올 수 있다. 하지만! bundle형식으로 가져오게 되는데 이는 구글 공식문서에 따르면 거대한 데이터를 다루기 위한 포맷이 아니며 50k로 제한하는 것을 권장하고 있다.
그래서 ViewModel이라는 것이 탄생하였다!
[ViewModel]
그림을 한 번 살펴보자.
위 그림을 살펴보면 액티비티랑은 독립된 생명주기를 가지고 있다. 따라서 액티비티가 재 생성되는 동안에도 값을 유지하고 가지고 있을 수 있다.
위에서 예시를 살펴보았던 숫자가 올라가는 프로그램이 있다고 가정해보자.
val myViewModel = ViewModelProvider(this).get(MyViewModel::class.java)
// myViewModel.counter = 100
// binding.textView.text = myViewModel.counter.toString()
//
// binding.button.setOnClickListener {
// myViewModel.counter += 1
// binding.textView.text = myViewModel.counter.toString()
// }
뷰모델을 적용하여 이렇게 값을 넘겨줄 수 있지만, 위와 같은 코드가 있을 때에는 주의할 점이 있다.
액티비티가 다시 실행되게 될 때 어차피 viewModel에 있는 숫자 값을 초기화 해주기 때문에 다음과 같이 팩토리패턴을 이용할 수 있다.
override fun <T : ViewModel> create(
key: String,
modelClass: Class<T>,
handle: SavedStateHandle,
): T {
if (modelClass.isAssignableFrom(MyViewModel::class.java)) {
return MyViewModel(counter, handle) as T
}
throw IllegalArgumentException("Viewmodel class not found")
}
val factory = MyViewModelFactory(100, this)
// val myViewModel = ViewModelProvider(this, factory).get(MyViewModel::class.java)
뷰모델 팩토리를 이용해서 팩토리에 값을 던진다음, 팩토리에서 뷰모델에게 값을 전달하는 형식이다.
이렇게 뷰모델에게 전달된 데이터는 액티비티의 생명주기가 달라져도 값을 유지할 수 있다.
[LiveData]
LiveData는 값의 변경을 감지할 수 있는 데이터 홀더이다. 말 그대로 값이 변경되면 시스템이 그 값의 변경을 감지할 수 있으며
이 특징은 뷰모델과 결합했을 때 시너지효과를 얻을 수 있다.
LiveData없이 viewModel을 사용할 때에는 ui에 표시할 데이터를 viewModel에서 단순히 가져왔지만,
LiveData를 이용하게 되면 Observer를 통해 자동으로 값이 변경된다.
여기서 Observer Pattern이 등장하게 된다.
옵저버 패턴의 개념은 다음과 같다.
- Subject의 상태변화를 관찰하는 Observer들을 객체와 연결하고
- Subject의 상태변화를 초래하는 변화가 발생하면 객체가 그 변화를 직접 Observer에게 통지한다.
예를 들어, 유튜브 채널 운영자가 Subject, 구독자가 Observer라고 생각하면 편하다.
채널 운영자가 새로운 동영상을 등록하면 구독자에게 알림이벤트가가고, 이를 자동으로 알게된다.
안드로이드 공식문서에 따르면 LiveData의 장점은 다음과 같다.
- UI와 데이터 상태의 일치 보장
- 메모리 누수 없음
- 리소스 공유
- 최신 데이터 유지 등 다양한 장점이 존재한다.
[DataBinding]
데이터바인딩은 쉽게 말해 코틀린 코드와 xml에 ui컴포넌트를 연결하는 라이브러리이다.
특징을 먼저 말해보자면,
findViewById를 없앨 수 있고, 레이아웃과 연결되기 때문에 라이브데이터를 관찰하고 있지 않더라도, 데이터바인딩을 통해 값이 변경될 때 마다 뷰의 요소를 변경할 수 있다.
또한, 커스텀 바인딩 어댑터를 만들어 활용할 수 있다.
예를 들어, xml안에 progressBar코드가 있다고 가정했을 때 xml파일에 layout태그를 추가한 후 다음과 같이 불러올 수 있다.
app:progressScaled="@{viewmodel.liveCounter}"
viewModel에 대한 관찰할 필요 없이 viewModel에 있는 값을 불러올 수 있다.
@BindingAdapter("app:progressScaled")
//fun setProgress(progressBar: ProgressBar, counter: Int) {
// progressBar.progress = counter
//}
또한, 위와 같이 custom binding adapter를 만들어 해당 progressBar의 progress를 변경시켜 줄 수 있다.
길이 좀 많이 길어지니, Navigation, Room, Paging, WorkManager는 다음에 포스팅해야겠다!
Ref.
'Android > 파고들기' 카테고리의 다른 글
[Android/파고들기] 1화 Android Architecture Pattern (0) | 2022.09.11 |
---|