ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 안드로이드 ViewModel (Android Developer 설명 참고)
    안드로이드 2021. 7. 27. 15:01
    반응형

    개요

    ViewModel은 UI컨트롤러와 데이터를 분리하여 화면 회전과 같이 구성이 변경할때도 데이터가 유지되도록 설계되었다.

     

    UI컨트롤러에서 데이터를 관리하게 되어있었는데 그렇게 되면 UI구성이 변경되면(화면 회면과 같은 동작) UI컨트롤러가 제거됐다가 다시 생성된다. 그렇게 되면 UI컨트롤러에 저장된 일시적인 UI 관련 데이터가 삭제된다. 이럴 경우 onSavedInstanceState() Method를 사용해서 onCreate()에서 데이터를 복원할 수 있다. 그런데 비트맵이나 리스트 데이터와 같은 대용량의 데이터에는 부적합하다.

     

    그리고 비동기 호출 처리를 해야한다면, 비동기 호출이 자주 발생하게 되어 이미 수행된 호출을 다시 호출해야하므로 리소스가 낭비된다.

     

    그래서 ViewModel을 통해 UI컨트롤러에서 UI에 표시할 데이터를 떼어내어 따러 관리함으로써 불필요한 리소스 낭비를 하지 않도록한다.

     

    ViewModel 구현

    ViewModel을 아래와 같이 생성해준다.

    class MyViewModel : ViewModel() {
        private val users: MutableLiveData<List<User>> by lazy {
            MutableLiveData<List<User>>().also {
                loadUsers()
            }
        }
    
        fun getUsers(): LiveData<List<User>> {
            return users
        }
    
        private fun loadUsers() {
            // Do an asynchronous operation to fetch users.
        }
    }

    그 후 아래와 같이 Activity에서 ViewModel에서 정의한 List에 액세스할 수 있다. observe를 통해 데이터가 변경되면 UI를 업데이트 하도록 구현할 수 있다.

    class MyActivity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            // Create a ViewModel the first time the system calls an activity's onCreate() method.
            // Re-created activities receive the same MyViewModel instance created by the first activity.
    
            // Use the 'by viewModels()' Kotlin property delegate
            // from the activity-ktx artifact
            val model: MyViewModel by viewModels()
            model.getUsers().observe(this, Observer<List<User>>{ users ->
                // update UI
            })
        }
    }

     

    Activity가 다시 생성되면 첫번째 Activity 생성 때 생성된 MyViewModel 인스턴스를 받아와 데이터가 유지된다. 그리고 사용자의 활동이 끝나면 ViewModel의 onCleared() 를 호출한다.

     

    ViewModel Lifecycle(수명주기)

    ViewModel 객체의 범위는 ViewModel을 가져올 때 ViewModelProvider에 전달되는 Lifecycle로 지정된다. ViewModel은 Activity 또는 Fragment의 Lifecycle이 끝날 때 까지 메모리에 남아있는다.

    Activity, ViewModel Lifecycle

    위 그림에서 처럼 Activity가 onCreate()가 처음 호출되면서 부터 finish에 의해 종료될 때 까지 ViewModel은 계속 메모리에 남아있고 Activity가 종료된 후 ViewModel의 onCleared()가 호출되고 ViewModel은 메모리에서 해제된다.

     

    Fragment 간 데이터 공유

    Activity에 포함된 둘 이상의 Fragment는 서로 통신이 필요한 경우가 있다. 이런 경우 Activity 범위의 ViewModel을 공유할 수 있다. 그 예제는 아래와 같다.

    class SharedViewModel : ViewModel() {
        val selected = MutableLiveData<Item>()
    
        fun select(item: Item) {
            selected.value = item
        }
    }
    
    class MasterFragment : Fragment() {
    
        private lateinit var itemSelector: Selector
    
        // Use the 'by activityViewModels()' Kotlin property delegate
        // from the fragment-ktx artifact
        private val model: SharedViewModel by activityViewModels()
    
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            itemSelector.setOnClickListener { item ->
                // Update the UI
            }
        }
    }
    
    class DetailFragment : Fragment() {
    
        // Use the 'by activityViewModels()' Kotlin property delegate
        // from the fragment-ktx artifact
        private val model: SharedViewModel by activityViewModels()
    
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            model.selected.observe(viewLifecycleOwner, Observer<Item> { item ->
                // Update the UI
            })
        }
    }

    위 예제를 보면 두 Fragment에서 공유할 ViewModel을 생성할 때 by activityViewModels() 를 사용하여 Activity 범위로 가져오면서 Fragment가 종료되어도 Activity의 Lifecycle이 종료되지 않으면 이 ViewModel은 메모리에 계속 남아있게 된다. 

    이렇게 ViewModel을 공유하면서 아래와 같은 이점이 있다.

    • Activity는 아무것도 할 필요 없고, 이 통신에 관해서도 알 필요가 없다.
    • Fragment는 SharedViewMdoel 외에 ViewModel을 공유하는 Fragment나 Activity에 대해서 몰라도 된다.
    • Fragment는 각각 생명주기가 있으며, 다른 Fragment가 종료되어도 현재 Fragment에 영향을 끼치지 않는다.

     

    마치며

    안드로이드 앱을 개발하면서 UI컨트롤러에 너무 많이 데이터가 있으면 코드도 복잡해지고 관리가 힘들었는데 ViewModel을 사용하면 이를 해소할 수 있을 것 같다.

    반응형

    댓글

Designed by Tistory.