새소식

android studio

[android] Livedata란? (feat.ViewModel)

  • -

 

이 그림은 안드로이드 공식문서에서 가져온 그림이다.

앱을 설계할 때 UI와 관련된 데이터는 ViewModel + LiveData로 관리하도록 권장하고 있다. 

 

이번 게시글은 LiveData에 대해 이야기해보겠다. 

 

 

 

LiveData 는 Observer 패턴을 활용하여, Lifecycle 에 따르며 데이터를 관리
이름 그대로 '실시간' 그 자체의 데이터, 즉 항상 최신 데이터를 보증한다는 특징이 있다.

 

Observer? 

위에서 말한 observer가 무엇인지 궁금할 수 있다.

나는 livedata를 몰랐을 때는 데이터를 일일이 UI에 업데이트 해주었다..(이건 클린 아키텍처가 아니다.)

 

그렇다면 매번 데이터를 관찰해주는 관찰자 역할이 있다면 어떨까?

안드로이드에서 Observer가 그런 개념이다.

데이터가 변경되는지 감시하고 있다가 UI 컨트롤러(Acitivity or Fragment)에게 알려준다. 

알림을 들어 UI 컨트롤러가 변경된 데이터를 UI에 업데이트를 한다. 

 

Observer는 livedata 개념에서 파생되어 LiveData라는 데이터 홀더 클래스를 가지고 있는 데이터만 감시할 수 있다.

Activity, Fragment, Service와 같은 안드로이드 생명주기(LifeCycle)를 인식하고

이에 따라 LiveData는 활성상태(Active)일 때만 데이터를 업데이트한다. 

 

LiveData? 

var height = 183

키를 저장하는 변수 height라는 값이 변경되는지 감시하고 싶다고 해보자.

 

var height = MutableLiveData<Int>() // int형 값을 넣을 거라는 걸 명시해주어야 한다.
 
height.value = 183

이 코드를 보면 변수 height에 MutableLiveData<> 를 생성하였다. 

LiveData에 대해 공부하고 있는 갑자기 MutableLiveData?라는 생각이 들 수도 있다.

 

LiveData에는 저장된 데이터를 업데이트하는 데 공개적으로 사용할 수 있는 메서드가 없다. (불변 객체)

MutableLiveData 클래스는 setValue(T) 및 postValue(T) 메서드를 공개 메서드로 노출하며, LiveData 객체에 저장된 값을 수정하려면 이러한 메서드를 사용해야 한다.

일반적으로 MutableLiveData는 ViewModel에서 사용되며 ViewModel은 변경이 불가능한 LiveData 객체만 관찰자에게 노출한다.

 

LiveData 객체 안에 있는 데이터는 수정이 불가능 하고 읽기만 가능하다.

즉, 데이터를 직접 업데이트 할 수 없다.

하지만 MutableLiveData는 데이터를 변경 수정할 수 있게 해준다.

(또한 Livedata는 추상클래스이므로 이를 상속한 MutableLiveData로 선언해주는 것이다.)

 

public class MutableLiveData<T> extends LiveData<T> {
 
    public MutableLiveData(T value) {
        super(value);
    }
 
    public MutableLiveData() {
        super();
    }
 
    @Override
    public void postValue(T value) {
        super.postValue(value);
    }
 
    @Override
    public void setValue(T value) {
        super.setValue(value);
    }
}

MutableLiveData 클래스는 LiveData를 상속받는 클래스이다.

LiveData에 postValue와 setValue (값을 넣는 메서드)를 추가한 클래스라고 할 수 있다.

-> LiveData 는 항상 UI 업데이트를 목적으로

LiveData 의 데이터는 setValue() 와 postValue() 를 통해 변경할 수 있다.

  • setValue() : MainThread 가 보장될 경우 활용 (MainThread 에서의 값 업데이트)하는데 동기적으로 처리된다.
  • postValue() : MainThread 가 아닌 IO 스케줄러 활용 시 활용 (MainThread 로 값 전달)하는데 비동기적으로 처리된다.

 

 

 

LiveData 사용의 이점

1) UI와 데이터 상태의 일치 보장

LiveData는 관찰자 패턴을 따른다. LiveData는 기본 데이터가 변경될 때 Observer 객체에 알린다.

코드를 통합하여 이러한 Observer 객체에 UI를 업데이트할 수 있다.

이렇게 하면 앱 데이터가 변경될 때마다 관찰자가 대신 UI를 업데이트하므로 개발자가 업데이트할 필요가 없다.

2) 메모리 누수 없음

관찰자는 Lifecycle 객체에 결합되어 있으며 연결된 수명 주기가 끝나면 자동으로 삭제된다.

3) 중지된 활동으로 인한 비정상 종료 없음

활동이 백 스택에 있을 때를 비롯하여 관찰자의 수명 주기가 비활성 상태에 있으면 관찰자는 어떤 LiveData 이벤트도 받지 않는다.

4) 수명 주기를 더 이상 수동으로 처리하지 않음

UI 구성요소는 관련 데이터를 관찰하기만 할 뿐 관찰을 중지하거나 다시 시작하지 않는다. LiveData는 관찰하는 동안 관련 수명 주기 상태의 변경을 인식하므로 이 모든 것을 자동으로 관리한다.

5) 최신 데이터 유지

수명 주기가 비활성화되면 다시 활성화될 때 최신 데이터를 수신한다. 예를 들어 백그라운드에 있었던 활동은 포그라운드로 돌아온 직후 최신 데이터를 받는다.

6) 적절한 구성 변경

기기 회전과 같은 구성 변경으로 인해 활동 또는 프래그먼트가 다시 생성되면 사용 가능한 최신 데이터를 즉시 받게 된다.

7) 리소스 공유

앱에서 시스템 서비스를 공유할 수 있도록 싱글톤 패턴을 사용하는 LiveData 객체를 확장하여 시스템 서비스를 래핑할 수 있다. LiveData 객체가 시스템 서비스에 한 번 연결되면 리소스가 필요한 모든 관찰자가 LiveData 객체를 볼 수 있습니다. 

 

 

 

사용 예제 

1. gradle 

 def lifecycle_version = "2.3.0"
    // ViewModel
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
    // LiveData
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"

ViewModel은 보통 LiveData와 같이 사용하기 때문에 추가해준다.

버전 정보는 공식문서에서 확인하자.

 

2. Livedata 생성 

class BaeminRepository {
 
    var _baeminNotice = MutableLiveData<자료형>() // MutableLiveData 객체 생성
 
    // ViewModel에서 이 메서드를 호출하면 다음 페이지 공지사항을 불러온다.
    fun loadBaeminNotice() {
        .
        .
        .
        (웹 사이트에서 공지사항을 불러오는 코드)
        .
        .
        .
        
        _baeminNotice.value = (불러온 공지사항) // 값의 변경이 일어난다.
    }
}

 

class MainViewModel : ViewModel() {
    // repository 객체 생성
    private val baeminRepository = BaeminRepository()
 
    // repository에 있는 MutableLiveData를 ViewModel의 LiveData에 넣는다.
    private val baeminNotice: LiveData<자료형>
        get() = baeminRepository._baeminNotice
 
    fun loadBaeminNotice(){
        baeminRepository.loadBaeminNotice() // repository에 있는 메서드를 호출함으로써 다음 공지사항을 불러온다.
    }
 
    fun getAll(): LiveData<자료형> {
        return baeminNotice
    }
}

 

 

3. Observer 객체 생성 

class MainActivity : AppCompatActivity() {
 
    private val model: MainViewModel by viewModels()
 
    override fun onCreate(savedInstanceState: Bundle?) {
        .
        .
        .
        // Observer를 생성해서 LiveData와 연결하는 작업
        model.getAll().observe(this, Observer{ notice ->
            // notice에 공지사항이 들어있음
            // 데이터의 변경이 이루어졌을 때 실행할 작업
        })
        .
        .
        .
        
        // 버튼 누를때마다 ViewModel의 loadBaeminNotice가 호출하도록 해놓음
        binding.btnLoadNextPage.setOnClickListener {
            model.loadBaeminNotice()
        }
    }
}

 

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.