ViewModel, LiveData

2022. 6. 1. 00:00·개발/Android
반응형

ViewModel

  • ViewModel은 Activity 및 Fragment의 생명주기를 고려하여 UI 관련 데이터를 저장하고 관리한다.
  • Activity는 Activity가 끝날 때까지 그리고 Fragment는 Fragment가 분리될 때까지 데이터를 메모리에서 유지한다.
  • Activity 및 Fragment의 생명 주기와 관계없이 데이터를 유지한다.
  • 화면 회전과 같이 구성을 변경 할때도 데이터를 유지할 수 있다.

ViewModel lifecycle

  • Activity의 onCreate()메서드에 처음 호출할 때 ViewModel을 요청 및 생성한다.
  • 시스템은 액티비티 기간 내내(예: 기기 화면이 회전될 때) onCreate() 메서드를 여러 번 호출한다.
  • ViewModel은 액티비티가 소멸될 때까지 존재한다.

ViewModel LifeCycle

LiveData

  • LiveData는 Observable data holder class, 즉 데이터를 저장하고 변화를 관찰할 수 있는 객체다.
  • UI객체는 LiveData에 옵저버(observer)를 등록할 수 있으며, 데이터가 변경될 때 UI 갱신한다.
  • LiveData가 보유하고 있는 데이터에 변화가 일어나면, LiveData는 등록된 옵저버 객체에 변화를 알려주고, 옵저버 객체의 onChanged() 메서드를 실행한다.
  • LiveData는 컴포넌트의 생명주기를 알고 있기 때문에 액티비티가 onStart(), onResume() 상태일 때는 콜백을 실행하고, 다른 액티비티로 넘어가는 onStop()일 때는 실행하지 않는다.
  • 즉, 컴포넌트들의 생명주기 상태가 activity(활성화) 상태일 때만 data에 대한 update를 제공한다.

livedata update 시기

LiveData 객체 생성

  • LiveData는 객체 뿐만 아니라 모든 데이터와 함께 사용할 수 있는 래퍼(wrapper) 객체로서, ViewModel 객체 에 저장되며, getter/setter 메서드를 통해 액세스를한다.

LiveData 형식

  1. MutableLiveData: 값의 변경/읽기 모두 가능한 LiveData (게터, 세터 모두 사용 가능)
    • MutableLiveData 형식의 LiveData를 변경할 때는 setValue(), postValue() 메서드를 사용한다.
    • setValue(): Main 스레드에서 즉시 값을 변경하고, 옵저버로 데이터 변경을 알려준다.
    • postValue(): 백그라운드 스레드에서 라이브데이터를 변경하고 싶을 때 사용한다. 
      내부적으로 핸들러를 통해 Main Looper 보내기 때문에 실제 변경은 Main 스레드에서 처리한다고 한다.
  2. LiveData: 값의 변경이 불가능하여 읽기만 가능한 LiveData (게터만 사용 가능)

LiveData 객체 Observe

  • LiveData는 데이터가 변경될 때만 액티비티의 관찰자에게 업데이트를 전달한다.
  1. ViewModel 가져오기
  2. LiveData에 변화가 발생했을 LiveData를 가져와서 처리할 로직을 정의하는 Observer 객체를 생성한다.
  3. observe() 메서드를 사용하여 LiveData 객체에 Observer 객체를 연결한다.
    • observe(LifecycleOwner, Observer)
    • LifeCycle: 컴포넌트의 생명주기 상태정보를 가지는 객체
    • LifeCycleOwner: LifeCycle 객체에 컴포넌트의 생명주기 상태정보를 제공하는 객체
      Activity에서는 곧 자신이기 때문에 this, Fragment는 viewLifecycleOwner로 전달,
      LiveData는 LifecycleOwner를 통해 해당 View의 Lifecycle을 관찰한다.
    • Observer: 변경된 LiveData를 수신하여 처리할 로직을 정의하는 인터페이스로 LiveData 객체를 구독하여 변경사항의 알림을 받는 객체다.

예제코드

이제 예제 코드로 본격적인 사용법을 살펴보자.

아래는 layout viewmodel_examl.xml 코드다.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/counter_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="80sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab_add"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:layout_marginBottom="16dp"
        android:clickable="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:srcCompat="@drawable/ic_add_black_24dp" />

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab_remove"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginBottom="16dp"
        android:clickable="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:srcCompat="@drawable/ic_remove_black_24dp" />

</androidx.constraintlayout.widget.ConstraintLayout>

다음은 사용할 ViewModel 클래스다.

class ViewModelMainActivity : AppCompatActivity() {

    private val binding by lazy{
        ViewmodelExamBinding.inflate(layoutInflater)
    }

    //ViewModel 가져오기
    //ViewModelProvider(context).get(ViewModel class)
    private val viewModel by lazy{
        ViewModelProvider(this).get(CounterViewModel::class.java)
    }

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

        val counterObserver = Observer<Int>{
            binding.counterTv.text = it.toString()
        }

        viewModel.getCounter().observe(this,counterObserver)

        init()
    }

    fun init(){
        with(binding){
            binding.fabAdd.setOnClickListener{
                viewModel.increaseOrDecreaseValue(1)
            }
            binding.fabRemove.setOnClickListener{
                viewModel.increaseOrDecreaseValue(-1)
            }
        }
    }
}

마지막은 액티비티 클래스다.

class ViewModelMainActivity : AppCompatActivity() {

    private val binding by lazy{
        ViewmodelExamBinding.inflate(layoutInflater)
    }

    //ViewModel 가져오기
    //ViewModelProvider(context).get(ViewModel class)
    private val viewModel by lazy{
        ViewModelProvider(this).get(CounterViewModel::class.java)
    }

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

        val counterObserver = Observer<Int>{
            binding.counterTv.text = it.toString()
        }

        viewModel.getCounter().observe(this,counterObserver)

        init()
    }

    fun init(){
        with(binding){
            binding.fabAdd.setOnClickListener{
                viewModel.increaseOrDecreaseValue(1)
            }
            binding.fabRemove.setOnClickListener{
                viewModel.increaseOrDecreaseValue(-1)
            }
        }
    }
}

그럼 이제 아래와 같이 에뮬레이터가 동작하고 에뮬레이터의 화면을 좌우로 돌려도 값이 변하지 않는 것을 확인할 수 있다.

 

이상으로 포스팅을 마칩니다. 감사합니다!

 

참고자료

https://developer.android.com/topic/libraries/architecture/viewmodel?hl=ko

반응형
'개발/Android' 카테고리의 다른 글
  • 코루틴 Coroutine
  • 네비게이션 컴포넌트
  • Fragment 프래그먼트
  • 선택위젯(어댑터뷰(AdapterView))
Debin
Debin
공부 기록을 남기며 게시글 리팩토링을 진행하는 블로그입니다.
  • Debin
    리팩토링하는 블로그
    Debin
  • 전체
    오늘
    어제
    • 분류 전체보기
      • DB
        • DB 기초
        • MySQL
        • SQL 튜닝
      • OS
      • Network
      • Git
      • 디지털콘텐츠기획
      • 소프트웨어공학
      • 코딩테스트
        • 프로그래머스
        • 백준
        • 인프런
      • 공부 일지
      • 독서
        • 클린코드
        • 일상 속 사물이 알려주는 웹 API 디자인
        • 토비의 스프링
        • 객체지향의 사실과 오해
        • 자바 잘 읽는 법
      • 기록 및 회고
      • Cloud
        • AWS
      • 개발
        • Java
        • Spring Core
        • Spring MVC
        • Spring DB
        • Spring Boot
        • Spring Security
        • Spring Batch
        • JPA
        • Test
        • Android
      • 대외활동
        • UMC SERVER
        • 카엔프 SW 아카데미
      • 프로젝트
      • Docker
      • Gradle
      • ELK
      • 실무 이야기
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • 깃허브
  • 공지사항

    • 본인 깃허브입니다!
  • 인기 글

  • 태그

    JPA
    innodb
    docker
    프록시
    객체지향
    redis
    객체
    코딩 #개발자 #노마드북클럽 #노개북
    운영체제
    spring
    Java
    인덱스
    container
    데이터베이스
    test
    spring mvc
    컨테이너
    ORM
    스프링
    SQL
    토비의 스프링
    자바
    AOP
    도커
    spring boot
    mysql
    AWS
    트랜잭션
    스프링 부트
    리눅스
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
Debin
ViewModel, LiveData
상단으로

티스토리툴바