이번 포스팅은 App Architecture 가이드 두 번째 포스팅입니다.
예제를 통해 좀 더 자세히 알아보도록 하겠습니다.
사용자 인터페이스 시작
UI는 fragment UserProfileFragment
와 관련 레이아웃 파일 user_profile_layout.xml
로 구성됩니다.
UI를 만들려면 데이터 모델에 다음 데이터 요소가 있어야 합니다.
▶ 사용자 ID: 사용자의 식별자입니다. fragment 인수를 사용하여 이 정보를 fragment에 전달하는 것이 좋습니다. Android OS에서 프로세스를 제거해도 이 정보가 유지되므로, 앱을 다시 시작할 때 ID를 사용할 수 있습니다.
▶ 사용자 개체: 사용자에 관한 세부정보를 보유하는 데이터 클래스입니다.
이 정보를 유지하기 위해 ViewModel 아키텍처 구성요소를 기반으로 하는 UserProfileViewModel
을 사용합니다.
ViewModel
개체는 fragment나 activity 같은 특정 UI 구성요소에 대한 데이터를 제공하고 Model과 커뮤니케이션하기 위한 데이터 처리 비즈니스 로직을 포함합니다. 예를 들어 ViewModel
은 데이터를 로드하기 위해 다른 구성요소를 호출하고 사용자 요청을 전달하여 데이터를 수정할 수 있습니다. ViewModel
은 UI 구성요소에 관해 알지 못하므로 구성 변경(예: 기기 회전 시 activity 재생성)의 영향을 받지 않습니다.
지금까지 다음 파일을 정의했습니다.
▶ user_profile.xml
: 화면의 UI 레이아웃 정의
▶ UserProfileFragment
: 데이터를 표시하는 UI 컨트롤러
▶ UserProfileViewModel
: UserProfileFragment
에서 볼 수 있도록 데이터를 준비하고 사용자 상호작용에 반응하는 클래스
다음 소스 코드는 이러한 파일의 시작 콘텐츠를 보여줍니다.
편의를 위해 레이아웃 파일은 생략합니다.
UserProfileViewModel
class UserProfileViewModel:ViewModel(){
val userId :String= TODO()
val user :User= TODO()
}
UserProfileFragment
class UserProfileFragment:Fragment(){
override fun onCreateView(inflater:LayoutInflater, container:ViewGroup?, savedInstanceState:Bundle?):View{
return inflater.inflate(R.layout.main_fragment, container,false)
}
}
user
를 가져오려면 ViewModel
에서 Fragment 인수에 액세스해야 합니다. Fragment에서 인수를 전달할 수도 있고, 더 나은 방법으로 SavedState 모듈을 사용해 ViewModel에서 직접 인수를 읽을 수 있습니다.
★ 참고: SavedStateHandle을 사용하면 ViewModel에서 관련 Fragment 또는 Activity의 저장된 상태와 인수에 액세스할 수 있습니다.
// UserProfileViewModel
class UserProfileViewModel( savedStateHandle:SavedStateHandle ):ViewModel(){
val userId :String= savedStateHandle["uid"]?: throwIllegalArgumentException("missing user id")
val user :User= TODO()
}
// UserProfileFragment
privateval viewModel:UserProfileViewModel by viewModels(factoryProducer ={SavedStateVMFactory(this)}
...
)
이제 user 개체가 확보되면 Fragment에 알려야 합니다. 여기에서 LiveData 아키텍처 구성요소가 사용됩니다.
LiveData는 식별 가능한 데이터 홀더입니다. 앱의 다른 구성요소에서는 이 홀더를 사용하여 상호 간에 명시적이고 엄격한 종속성 경로를 만들지 않고도 개체 변경사항을 모니터링할 수 있습니다. 또한 LiveData 구성요소는 activity, fragment, 서비스와 같은 앱 구성요소의 수명 주기 상태를 고려하고, 개체 유출과 과도한 메모리 소비를 방지하기 위한 정리 로직을 포함합니다.
★ 참고: RxJava와 같은 라이브러리를 이미 사용하고 있다면 LiveData 대신 계속 사용해도 됩니다. 그러나 이러한 라이브러리와 방법을 사용하는 경우 앱의 수명 주기를 올바르게 처리해야 합니다. 특히 관련 LifecycleOwner
가 중지되면 데이터 스트림이 일시중지되고, 관련 LifecycleOwner
가 제거되면 이러한 스트림이 제거되도록 해야 합니다. android.arch.lifecycle:reactivestreams
아티팩트를 추가하여 LiveData
를 다른 반응형 스트림 라이브러리(예: RxJava2)와 함께 사용할 수도 있습니다.
LiveData 구성요소를 앱에 통합하기 위해 UserProfileViewModel
의 필드 유형을 LiveData<User>
로 변경합니다. 이제 데이터가 업데이트되면 UserProfileFragment
에 정보가 전달됩니다. 또한 이 LiveData
필드는 수명 주기를 인식하기 때문에 더 이상 필요하지 않은 참조를 자동으로 정리합니다.
UserProfileViewModel
class UserProfileViewModel( savedStateHandle:SavedStateHandle):ViewModel(){
val userId :String= savedStateHandle["uid"]?: throw IllegalArgumentException("missing user id")
val user :LiveData<User>= TODO()
}
이제 데이터를 관찰하고 UI를 업데이트하도록 UserProfileFragment
를 수정합니다.
UserProfileFragment
override fun onViewCreated(view:View, savedInstanceState:Bundle?){
super.onViewCreated(view, savedInstanceState)
viewModel.user.observe(viewLifecycleOwner){
// update UI
}
}
사용자 프로필 데이터가 업데이트될 때마다 onChanged()
콜백이 호출되고 UI가 새로고침됩니다.