[Android] Jetpack ViewModel이란?
Android

[Android] Jetpack ViewModel이란?

728x90

 

 

 

ViewModel이란?

 JetPack의 ViewModel은 액티비티의 수명 주기를 고려하여 UI 관련 데이터를 저장하고 관리하도록 설계되었다. 안드로이드의 액티비티 생명주기와 분리를 시켜, 액티비티가 재실행돼도 데이터가 소멸되지 않도록 한다. 또한 액티비티가 파괴되면 ViewModel의 자원도 자연히 소멸된다.

 

 

ViewModel 배경

 시스템에서 UI 컨트롤러를 제거하거나 다시 만들면 컨트롤러에 저장된 일시적인 모든 UI 관련 데이터가 손실된다. 데이터가 단순한 경우 Activity는 onSaveInstanceState() 메서드를 사용하여 onCreate()의 번들에서 데이터를 복원할 수 있다. 하지만 이 접근 방법은 사용자 목록이나 비트맵과 같은 대용량일 가능성이 높은 데이터가 아니라, 직렬화했다가 다시 역직렬화할 수 있는 소량의 데이터에만 적합하다.

 또 다른 문제는 UI 컨트롤러가 반환하는 데 시간이 걸릴 수 있는 비동기 호출을 자주 해야 한다는 점이다. UI 컨트롤러는 이러한 비동기 호출을 관리해야 하며, 메모리 누출 가능성을 방지하기 위해 시스템에서 호출 폐기 후 호출을 정리하는지 확인해야 한다. 이러한 관리에는 많은 유지보수가 필요하며, 구성 변경 시 개체가 다시 생성되는 경우 개체가 이미 실행된 호출을 다시 해야 할 수 있으므로 리소스가 낭비된다.

 Activity 및 Fragment와 같은 UI 컨트롤러는 주로 UI 데이터를 표시하거나, 사용자 작업에 반응하거나, 권한 요청과 같은 운영체제 커뮤니케이션을 처리하기 위한 것이다. 또한 UI 컨트롤러에 데이터베이스나 네트워크에서 데이터 로드를 담당하도록 요구하면 클래스가 팽창된다. 이런 방법으로 UI 컨트롤러에 과도한 책임을 할당하면 테스트가 훨씬 어려워진다.

UI 컨트롤러 로직에서 뷰 데이터 소유권을 분리하는 방법이 훨씬 더 쉽고 효율적이다.

 

 

ViewModel의 수명 주기

 ViewModel의 범위는 ViewModel을 가져올 때 ViewModelProvider에 전달되는 Lifecycle로 지정된다. 일반적으로 시스템에서 Activity의 onCreate() 메소드를 처음 호출할 때 ViewModel을 요청한다. ViewModel이 처음 요청되었을 때부터 Activity가 끝나고 폐기될 때까지 ViewModel은 존재한다.

 

 

ViewModel 예제

activity_chrono.xml

 Chronometer 객체는 1초마다 1씩 증가하는 시간을 출력해준다.

<Chronometer
    android:id="@+id/chronometer"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerHorizontal="true" />

 

ChronoActivity.kt

class ChronoActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_chrono)
        
        chronometer.start()
    }
}

 

 

 이 상태에서 화면을 회전하게 되면 Configuration Change가 발생하고 액티비티가 재실행된다. 데이터는 따로 저장하지 않기 때문에 0초부터 다시 증가하여 시작하게 된다.

 만약 회전을 해도 시간이 0초로 돌아가지 않도록 하려면 어떻게 해야 할까? 액티비티가 재실행될 때 onPause에서 시간을 잠시 저장했다가 재실행 후 onResume에서 다시 읽은 뒤, chronometer에 전달해주는 귀찮은 방법이 있다.

 ViewModel을 사용한다면 액티비티의 Lifecycle에서 자유롭기 때문에 로테이션이 되도 데이터가 소멸되지 않는다. ViewModel을 사용한다면 onResume, onPause에서 시간을 임시로 저장하는 것을 구현할 필요가 없다.

 

ChronometerViewModel.kt

import androidx.lifecycle.ViewModel

class ChronometerViewModel : ViewModel() {
    var startTime : Long = 0
}

 

ChronoActivity.kt

 ViewModelProviders라는 helper 객체를 이용해 ViewModel 객체를 생성한 뒤 startTime을 가져와 chronometer에 전달해준다. 만약 액티비티가 재실행된다고 해도 ViewModel은 소멸되지 않고 이전에 생성한 것을 사용한다.

class ChronoActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_chrono)
        
        val chronometerViewModel =
            ViewModelProvider(this).get(ChronometerViewModel::class.java)
        
        if (chronometerViewModel.startTime <= 0) {
            chronometerViewModel.startTime = SystemClock.elapsedRealtime()
        }
        
        chronometer.base = chronometerViewModel.startTime
        chronometer.start()
    }
}

 

 

출처 : https://developer.android.com/topic/libraries/architecture/viewmodel

728x90