[Android / Kotlin] LiveData

Subeen·2023년 12월 25일
0

Android

목록 보기
24/73

Observer Pattern이란?

Observer Pattern(옵저버 패턴)이란 서브젝트의 상태 변호를 관찰하는 옵저버들을 객체와 연결하고 서브젝트의 상태 변화를 초래하는 이벤트가 발생하면 객체가 그 이벤트를 직접 옵저버에게 통제하는 구조로 디자인 패턴의 한 종류이다.

Observable vs LiveData

  • 옵저버에 의해 값의 변경을 감시할 수 있는 안드로이드 Data Holder로는 ObservableLiveData가 있다.

Observable

Observable Boolean, Observable Byte, Observable Char 등 기본형은 이미 준비되어 있는데 특수한 타입을 사용하고 싶다면 ObservableField를 사용해 직접 구현할 수도 있다.

  • Obervable : CallBack 등록
    • lifecycle을 알 수 없으므로 등록한 콜백이 상시 작동되어야 한다.
    • 작동이 필요 없어지면 removeOnPropertyChangedCallback을 호출하여 콜백을 수동으로 직접 제거 해야 한다.
private val observableString = ObservableField<String>("Default value")
observableString.addOnPropertyChangedCallback(object: Observable.OnPropertyChangedCallback() {  // 콜백을 등록하는 과정이 있음
    override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
        // Do Somthing
    }
})

LiveData

LiveData값의 변경을 감지할 수 있는 Data Holder로 라이브 데이터를 사용할 경우 값의 변경을 감지하여 UI의 변화를 자동으로 반영되게 할 수 있다.
예를 들어, 정수 1을 담고 있는 라이브 데이터의 값이 정수 2로 변하는 순간을 시스템이 감지할 수 있다.

  • LiveData : lifecycleOwner 전달
    • lifecycle이 STARTED 혹은 RESUMED로 활성화 상태일 때만 Observe를 수행한다.
    • 나머지 상태에서는 자동으로 비활성화 된다.
private val observableString = MutableLiveData<String>("Default value")
observableString.observe(lifecycleOwner, Observer {  //  LifecycleOwner를 전달하는 과정이 있음
    // Do Somthing
})

LiveData의 이점

LiveData를 사용하면 다음의 이점이 있다.

  • UI와 데이터 상태의 일치 보장
  • 메모리 누수 없음
  • 중지된 활동으로 인한 비정상 종료 없음
  • 수명 주기를 더 이상 수동으로 처리하지 않음
  • 최신 데이터 유지
  • 적절한 구성 변경
  • 리소스 공유

LiveData 사용

build.gradle (:app)

  • build.gradle(앱 수준)에서 라이브러리의 종속 항목을 추가한다.
dependencies {
	...
    // viewModel 사용을 위한 dependency
    implementation("androidx.lifecycle:lifecycle-viewmodel:2.6.2")
    // by viewModels()
    implementation("androidx.activity:activity-ktx:1.8.2")
    implementation("androidx.fragment:fragment-ktx:1.6.2")
    // save state
    implementation("androidx.compose.runtime:runtime-saved-instance-state:1.0.0-alpha11")
    
    // LiveData
    implementation("android.arch.lifecycle:livedata:1.1.1")  // add
}

ViewModel

var liveCounter: MutableLiveData<Int> = MutableLiveData(_counter)  // LiveData 선언
/*
counter를 저장하는 ViewModel
 */
class MyViewModel(
    _counter: Int,
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {  // 생성자로 savedStateHandle을 받도록 함

    // LiveData는 변경 불가능한 타입이라서 MutableLiveData를 사용
    var liveCounter: MutableLiveData<Int> = MutableLiveData(_counter)

    var counter = savedStateHandle.get<Int>(SAVE_STATE_KEY)
        ?: _counter  // savedStateHandle에서 값을 가져오게 하는데 만약 null이면 전달 받은 초기값을 사용하도록 함

    fun saveState() {
        savedStateHandle.set(SAVE_STATE_KEY, counter)  // count 값을 저장하는 SaveState를 정의
    }

    companion object {
        private const val SAVE_STATE_KEY =
            "counter"  // savedStateHandle은 Key Value 형태로 값을 저장하기 때문에 저장과 복원에 사용할 Key를 정해줌
    }

}

AbstractSavedStateViewModelFactory(owner, defaultArgs)

  • SavedStateHandle을 종속 항목으로 전달해야 하는 경우 ViewModel에 대해 AbstractSavedStateViewModelFactory를 사용한다.
class MyViewModelFactory(
    private val counter: Int,
    owner: SavedStateRegistryOwner,
    defaultArgs: Bundle? = null
) : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
    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  // ViewModel을 반환할 때 핸들을 함께 반환하도록 함
        }
        throw IllegalArgumentException("Viewmodel class not found")
    }
}

Observer 등록

myViewModel.liveCounter.observe(this) { counter ->  // counter LiveData에 대한 Observer 등록
	// Do Somthing
}
    private val binding: ActivityMainBinding by lazy {
        ActivityMainBinding.inflate(layoutInflater)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)
        

        // ViewModel의 카운터 값을 save state에 연결해서 시스템에 의한 종료가 발생해도 값이 유지 되도록
        val factory = MyViewModelFactory(100, this)

        val myViewModel by viewModels<MyViewModel> { factory }

        binding.button.setOnClickListener {
            myViewModel.liveCounter.value = myViewModel.liveCounter.value?.plus(1)
        }

        // liveCounter를 옵저빙 함으로써 UI에 표시하는 로직을 더이상 버튼에 클릭 리스너 안에 둘 필요가 없음
        myViewModel.liveCounter.observe(this) { counter ->  // counter LiveData에 대한 Observer 등록
            binding.textView.text = counter.toString()
        }
    }
}

참조
냉동코더의 알기 쉬운 Modern Android Development 입문 강의를 듣고 정리 했습니다.

profile
개발 공부 기록 🌱

0개의 댓글