본문 바로가기

개발/Android

Lifecycle aware component 수명주기 인식 컴포넌트

정의

 

Component*가 LifecycleOwner의 상태변화를 observe하여 필요한 작업을 스스로 수행할 수 있도록 하는 기능

 

*여기서의 Component는 안드로이드 Component가 아닌 자신 만의 역할이 있는 코드 컴포넌트를 의미

 


필요성

 

안드로이드 플랫폼은 Application, Activity, Fragment 등의 Lifecycle 상태에 맞게 callback(onCreate 등)을 불러준다. 개발자는 플랫폼에서 알아서 불러주는 callback을 override하여 하고 싶은 일을 작성한다.

 

1. Callback을 override하기 위해서는 Activity와 Fragment와 같은 UI controller에 코드 작업을 해야한다. Lifecycle에 맞춰 이런 저런 Component 처리 코드들 넣다보면 UI controller의 코드량이 비대해질 수 밖에 없다.

 

2. Background thread를 이용해서 작업을 수행하게 되면 controller가 중지되어도 작업을 계속하게 될 수 있다. 따라서 필요 이상으로 작업이 오랫동안 유지되거나 메모리 누수가 발생할 수 있다.

 

이에 대해서는 여기에 잘 설명되어있다.

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

 

수명 주기 인식 구성요소로 수명 주기 처리  |  Android 개발자  |  Android Developers

새 Lifecycle 클래스를 사용하여 활동 및 프래그먼트 수명 주기를 관리합니다.

developer.android.com

 

2번은 UI controller의 Lifecycle callback을 이용하더라도 잘 개발한다면 막을 수 있는 문제라고 생각한다.

 

다만 1번은 해결방안이 딱히 없다.

MVP 패턴이라는 것을 이용하면 UI controller가 비대해지는 걸 막을 순 있다고 하는데, 근본적인 해결방법은 아닌 것 같다.

 

따라서 안드로이드에서는 Component가 Lifecycle에 따른 작업을 직접 소유할 수 있도록, Observer 패턴의 API를 만들어 냈다.

 

이를 구현한 Component를 Lifecycle aware하다고 한다.

 

 

해당 API를 사용하기 위해서는 gradle 종속성이 필요하다.

dependencies {
    // ViewModel이나 LiveData없이 Lifecycle을 사용하고 싶은 경우
    // kotlin
    implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
    // java
    implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"
}

 

 

테스트를 하다 발견한 것인데 다음 종속성 없이도 androidx에서는 이 종속성의 jar들이 external library에 추가되었다.

 

그 이유는 AppCompatActivity가 ComponentActivity를 extends하고 있는데, 내부적으로 이미 다음 종속성의 API들을 사용하고 있다.

androidx.activity.ComponentActivity를 살펴보면 알 수 있다.

 

따라서 다음 종속성을 명시하지 않더라도 따라오는 듯하다.

 

다만 혼동을 막기 위해 원하는 버전을 사용하는 편이 나을 것 같다. (종속성에 설정한 버전을 따라간다)

여튼 종속성은 추가하는게 맞는 것 같다.

 


LifecycleOwner

 

먼저 Component가 controller의 Lifecycle를 알기 위해서는, LifecycleObserver를 등록할 대상이 필요하다.

이 대상은 LifecycleObserver를 등록할 수 있어야 하며 당연히 Lifecycle을 가지는 자여야 한다.

 

LifecycleOwner는 해당 클래스에 Lifecycle이 존재함을 나타내는 Interface이다.

소유하고 있는 Lifecycle을 얻을 수 있는 getLifecycle 함수를 구현해야한다.

 

package androidx.lifecycle;

public interface LifecycleOwner {

    @NonNull
    Lifecycle getLifecycle();
}

 

반환값이 되는 Lifecycle은 abstract class로 LifecycleObserver를 추가하고 제거하며,

현재 state를 확인할 수 있는 인터페이스를 제공한다.

 

package androidx.lifecycle;

public abstract class Lifecycle {

    @MainThread
    public abstract void addObserver(@NonNull LifecycleObserver observer);

    @MainThread
    public abstract void removeObserver(@NonNull LifecycleObserver observer);

    @MainThread
    @NonNull
    public abstract State getCurrentState();

    public enum Event {
        // ...
    }

    public enum State {
        // ...
    }
}

 

좀 전에 ComponentActivity에서 이미 Lifecycle 종속성을 사용하고 있다고 언급했다.

 

AppCompatActivity가 extends하는 ComponentActivity와 Fragment는 LifecycleOwner를 이미 implements하고 있다.

(Support library 26.1.0부터 이렇게 변경되었다고 한다)

 

package androidx.core.app;

public class ComponentActivity extends Activity implements LifecycleOwner, KeyEventDispatcher.Component {
    // ...
}

 

package androidx.fragment.app;

public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener, LifecycleOwner, ViewModelStoreOwner {
    // ...
}

 

당연히 기본 android.app.Activity나 android.app.Fragment를 사용하면 플랫폼에서 제공하는 Lifecycle을 사용할 수 없게 된다.

이런 경우는 직접 구현하거나 Activity를 AppCompatActivity로 변경해야겠다.

 

 

ComponentActivity와 Fragment에서는 Lifecycle을 extends한 LifecycleRegistry 구현체를 멤버 변수로 가지고 있다.

LifecycleRegistry는 여러 Observer들을 추가할 수 있도록 구현이 된 매우 편리한 class이다.

 

package androidx.lifecycle;

public class LifecycleRegistry extends Lifecycle {

    public LifecycleRegistry(@NonNull LifecycleOwner provider) {
        mLifecycleOwner = new WeakReference<>(provider);
        mState = INITIALIZED;
    }
    
    // ...
}

 

직접 LifecycleOwner를 구현하여 쓸 예정이라면 안드로이드 플랫폼 코드를 참고하여 만들면 되겠다.

LifecycleRegistry를 멤버변수로 가지고 getLifecycle에서 이 멤버 변수를 반환하도록 구현하면 된다.

 

class CustomLifecycleOwner : LifecycleOwner {

    private val lifecycleRegistry = LifecycleRegistry(this)

    override fun getLifecycle(): Lifecycle {
        return lifecycleRegistry
    }
}

 

위처럼 만들면 lifecycle.getCurrentState()을 해도 항상 INITIALIZED의 상태일 것이다.

그 이유는 CustomLifecycleOwner에서는 내부적인 Lifecycle state도, 상태 변경도 없기 때문이다.

 

제대로 구현하는 예시는 다음 포스팅에 만들어볼까 한다.

 

어찌되었든 LifecycleRegistry의 상태 변경을 위해서는 다음 메서드 둘 중 하나를 사용해야한다.

차이점은 매개변수가 Lifecycle의 Event인지 State인 것 같고, 둘 다 등록된 LifecycleObserver에 변경사항을 알려준다.

 

public void handleLifecycleEvent(@NonNull Lifecycle.Event event) {
    State next = getStateAfter(event);
    moveToState(next);
}
@MainThread
public void setCurrentState(@NonNull State state) {
    moveToState(state);
}

 

ComponentActivity와 Fragment의 경우 이미 내부적으로 Lifecycle 상태를 알고 있으며,

LifecycleRegistry에 이를 알려주는 코드가 이미 구현이 되어 있다.

 

따라서 바로 LifecycleObserver를 붙여서 사용하기만 하면 되는 것이다.

 


LifecycleObserver

 

LifecycleObserver는 LifecycleOwner의 Lifecycle 상태 변화에 대한 알림을 받아보는 인터페이스이다.

메소드가 없는 마커 인터페이스이다.

 

package androidx.lifecycle;

public interface LifecycleObserver {

}

 

 

Lifecycle을 받아보고 싶은 class가 LifecycleObserver를 implements를 하도록 만든다.

그리고 @OnLifecycleEvent라는 어노테이션을 이용하면, 원하는 Event가 발생했을 때 메서드를 실행시키도록 한다.

 

다음 예시는 모든 Lifecycle 변화에 다음 메서드를 실행시키게 된다.

 

class CustomLifecycleObserver : LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
    fun observeLifecycleChanged() {
        // Lifecycle이 변할 때마다 작업을 수행한다.
    }
}

 

어노테이션 안에 들어갈 수 있는 Event는 Lifecycle 인터페이스 안에 정의된 enum Event이다.

Lifecycle callback에 일대일 매칭이 되는 Event들이 모두 정의되어 있다.

관찰해야 하는 callback에 해당하는 Event를 골라 사용하면 된다.

 

public enum Event {
    ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY, ON_ANY
}

 

경우에 따라서는 Observer의 일반 메서드에서도 Lifecycle의 현재 상태를 알고 싶을 수 있다.

이때는 Lifecycle을 Observer 생성자에서 받아와 composition을 하면 된다.

그리고 원하는 메서드에서 Lifecycle.getCurrentState()을 사용하면 된다.

 

class CustomLifecycleObserver(private val lifecycle: Lifecycle) : LifecycleObserver {

    fun startTask() {
        if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
            // STARTED 또는 RESUMED 상태인 경우에만 작업을 수행한다.
        }
    }
}

 

isAtLeast는 Lifecycle 인터페이스의 State enum에 정의된 메서드이다.

어떻게 작동하는지는 androidx.lifecycle.Lifecycle의 플랫폼 코드를 확인해보면 알 수 있다.

 

 

이제 드디어 lifecycle에 CustomLifecycleObserver를 붙일 수 있다.

그러면 Lifecycle의 변화에 따라 Observer가 원하는 동작을 할 수 있게 된다.

 

lifecycle.addObserver(CustomLifecycleObserver(lifecycle))

 

더 이상 UI controller에 의존적일 필요가 없고 Component가 작업을 처리할 수 있게 된다.

가독성도 좋아지고 코드 응집도도 높아진다.

 

 

첫 포스팅이라서 제대로 글솜씨도 전달력도 많이 부족한 것 같다.

차차 쓰면서 발견하길 바라며 이번 포스팅을 마무리해야겠다.

 


 

 

'개발 > Android' 카테고리의 다른 글

Custom View 만들 때 주의사항 - View 생성자  (0) 2020.12.16