반응형

안드로이드 개발자를 채용하는 회사들의 공고들을 보면 어느 회사나 공통적으로 자격 요건에 이런 문구가 있습니다.

  • Kotlin으로 개발해 본 경험이 있으신 분
  • MVVM 아키텍처에 익숙하신 분

안드로이드 개발하시는 분들 중 대학교에서 전공수업으로 자바를 접하시고서 안드로이드로 넘어오시는 분들이 많은데, 그런 분들에게는 코틀린도, MVVM도 낯설 수밖에 없습니다. 그래서 미약하지만 그런 분들께 조금이나마 도움이 되고자 꾸준하게 안드로이드와 코틀린에 관한 포스팅을 하고자 합니다.

이번 포스팅에서는 ViewModel과는 별개로 LiveData에 관해서만 다루고, AAC ViewModel에 대해서는 다른 포스팅에서 다루도록 하겠습니다.([Android/Jetpack] - [Android] 화면 회전해도 데이터 유지하기 - AAC ViewModel)

미리 말씀 드립니다만, AAC ViewModel을 사용했다고 해서 Android Architecture에서 말하는 MVVM 패턴을 구현했다고 착각하시면 안됩니다! 이와 관련해서도 별도의 포스팅을 다루도록 하겠습니다.
예제는 구글의 Developer 사이트에서 발췌하였습니다.

 

LiveData란?

LiveData는 Observable data holder class 입니다. 이 말은 들고 있는 데이터에 대해 관찰을 하는 클래스라고 보시면 되겠습니다. LiveData는 독특하게도 액티비티, 프래그먼트, 서비스 등 안드로이드 컴포넌트의 Lifecycle에 영향을 받습니다. 한 마디로 LiveData는 컴포넌트들의 생명주기 상태가 active(활성화) 상태일 때만 data에 대한 update를 제공합니다.
(여기서 active 상태란 STARTED, RESUME 상태를 말합니다.)

LiveData 객체는 Observer 객체와 함께 사용됩니다. LiveData가 들고 있는 데이터에 변화가 일어날 경우, LiveData는 등록된 Observer 객체에 변화를 알려주고, Observer의 onChanged() 메소드가 실행되게 됩니다. 자세한 내용은 아래에서 예시와 함께 다루겠습니다.

 

LiveData의 장점

  1. UI가 데이터 상태와 일치하는 것을 보장합니다.
    LiveData는 Observer 패턴을 따릅니다. LiveData는 생명주기에 변경이 일어날 때마다 Observer 객체에 알려줍니다. 그리고 이 Observer 객체를 사용하면 데이터의 변화가 일어나는 곳마다 일일이 UI를 변경하는 코드를 넣을 필요 없이, 통합적이고 확실하게 데이터의 상태와 UI를 일치시킬 수 있습니다.
  2. 메모리 누수가 없습니다.
    옵저버 객체는 Lifecycle 객체와 결합되어 있어서 컴포넌트가 Destroy 될 경우 메모리를 스스로 해제합니다.
  3. Stop 상태의 액티비티와 Crash가 발생하지 않습니다.
    액티비티가 Back Stack에 있는 것처럼 Observer의 생명주기가 inactive(비활성화) 일 경우, Observer는 LiveData의 어떤 이벤트도 수신하지 않습니다.
  4. 생명주기에 대한 추가적인 handling을 하지 않아도 됩니다.
    LiveData가 생명주기에 따른 Observing을 자동으로 관리를 해주기 때문에 UI 컴포넌트는 그저 관련 있는 데이터를 관찰하기만 하면 됩니다.
  5. 항상 최신 데이터를 유지합니다.
  6. 화면 구성이 변경되어도 데이터를 유지합니다.
    예를 들어, 디바이스를 회전하여 세로에서 가로로 화면이 변경될 경우에도 LiveData는 회전하기 전의 최신 상태를 즉시 받아옵니다.
  7. Resource를 공유할 수 있습니다.
    LiveData를 확장하는 클래스를 만들어 싱글톤 패턴으로 관리를 할 경우 앱 전체에서 사용 가능한 LiveData 객체를 만들 수 있고, 이를 통해 자원을 공유할 수 있습니다.

 

LiveData 사용하기

LiveData 객체를 사용하기 위해서는 다음 몇 가지 스텝만 알고 계시면 됩니다.

  1. generic을 사용해 관찰하고자 하는 데이터의 타입을 갖는 LiveData 인스턴스를 생성합니다.
    (보통 LiveData 객체는 ViewModel 클래스 내에서 사용됩니다.)
  2. Observer 객체를 만듭니다. 이때, LiveData가 들고있는 데이터가 변화가 일어났을 때 수행해야 할 로직이 들어있는 onChanged() 메소드를 정의해야 합니다. 보통은 액티비티나 프래그먼트 같은 UI Controller 내에서 생성합니다.
  3. LiveData 클래스의 observe() 메소드를 사용해 Observer 객체를 LiveData 객체에 붙입니다. 이때 observe() 메소드는 LifecycleOwner 객체를 필요로 합니다.(보통은 Activity를 넘기면 됩니다.) LiveData에 저장된 데이터에 변화가 일어난 경우, 부착된 LifecycleOwner가 상태가 active(활성화)인 한 모든 데이터에 대해 trigger가 일어납니다.

 

 

Sample Code

앞서 말씀드린 것처럼 LiveData는 주로 ViewModel 클래스와 함께 사용되기 때문에, 예제 또한 ViewModel 내에서 정의한 LiveData로 작성하겠습니다. ViewModel에 관련한 자세한 내용은 다른 포스팅에서 다루도록 하겠습니다.

Kotlin

class NameViewModel : ViewModel() {

    // Create a LiveData with a String
    val currentName: MutableLiveData<String> by lazy {
        MutableLiveData<String>()
    }
}

 

Java

public class NameViewModel extends ViewModel {

    // Create a LiveData with a String
    private MutableLiveData<String> currentName;
    
    public MutableLiveData<String> getCurrentName() {
        if (currentName == null) {
            currentName = new MutableLiveData<String>();
        }
        return currentName;
    }

}

위 예에서는 MutableLiveData를 사용했지만 불변객체로 LiveData<>를 사용하실 수도 있습니다.

이제 위에서 만든 ViewModel 객체 내의 LiveData를 Activity 안에서 어떻게 사용할지 살펴볼 텐데, 대부분의 경우 LiveData에 Observer를 부착하는 코드는 컴포넌트의 onCreate() 메소드 내에 위치하는 것이 바람직합니다. 이유는 두 가지가 있는데,

  1. onResume()에 하게될 경우 pause()나 stop()에 의해서 잠시 비활성화된 앱이 다시 활성화가 되면서 LiveData에 대한 코드가 중복 호출될 수 있기 때문입니다. 이는 앞서 살펴봤던 LiveData의 장점 중 4번에 어긋나는 방식입니다. 
  2. 액티비티나 프래그먼트가 활성화 되자마자 표시할 수 있는 데이터를 가질 수 있기 때문에 컴포넌트는 STARTED 상태가 되자마자 LiveData 객체로부터 가장 최신의 값을 수신해야 합니다.

Kotlin

class NameActivity : AppCompatActivity() {

    private lateinit var model: NameViewModel

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

        // Get the ViewModel.
        model = ViewModelProvider(this).get(NameViewModel::class.java)

        // Create the observer which updates the UI.
        val nameObserver = Observer<String> { newName ->
            // Update the UI, in this case, a TextView.
            nameTextView.text = newName
        }

        // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
        model.currentName.observe(this, nameObserver)
    }
}

Java

public class NameActivity extends AppCompatActivity {

    private NameViewModel model;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Get the ViewModel.
        model = new ViewModelProvider(this).get(NameViewModel.class);


        // Create the observer which updates the UI.
        final Observer<String> nameObserver = new Observer<String>() {
            @Override
            public void onChanged(@Nullable final String newName) {
                // Update the UI, in this case, a TextView.
                nameTextView.setText(newName);
            }
        };

        // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
        model.getCurrentName().observe(this, nameObserver);
    }
}

참고로, LiveData를 통해서 UI를 업데이트하고 싶은 경우에는 대체로 Activity나 Fragment 내에서 선언하는 것보다 위 예처럼 ViewModel 내에서 정의하고 사용하는 것이 더 좋은데 그 이유는 액티비티와 데이터 간의 결합도를 낮추고, UI Controller는 오직 data를 display 하는 것에만 책임을 지게끔 하는 소프트웨어 디자인을 유지하기 위해서입니다.


developer 사이트에 있는 예제에서 사용 중인 ViewModelProviders.of()deprecated 되었으므로 ViewModelProvider.get() 으로 대체하였습니다. ViewModel 생성 방법에 대해서는 AAC ViewModel 을 생성하는 6가지 방법 - ViewModelProvider 에서 좀 더 자세히 살펴볼 수 있습니다.

반응형
반응형