[개념] Lifecycle-Aware Components

쓰리원·2022년 5월 18일
0
post-thumbnail

1. Lifecycle-Aware Components 란?

수명 주기 인식 구성요소는 활동과 프래그먼트 같은 다른 구성요소의 수명 주기 상태 변경에 따라 작업을 실행합니다. 이러한 구성요소를 사용하면 잘 구성된 경량의 코드를 만들고 더욱 쉽게 유지할 수 있습니다.

일반적인 패턴은 활동과 프래그먼트의 수명 주기 메서드에 종속 구성요소의 작업을 구현합니다. 하지만 이 패턴으로 인해 코드 구성이 나빠지고 오류가 증가하게 됩니다. 수명 주기 인식 구성요소를 사용하면, 수명 주기 메서드에서 구성요소 자체로 종속 구성요소의 코드를 옮길 수 있습니다.

androidx.lifecycle 패키지는 수명 주기 인식 구성요소(활동이나 프래그먼트의 현재 수명 주기 상태를 기반으로 동작을 자동 조정할 수 있는 구성요소)를 빌드할 수 있는 클래스 및 인터페이스를 제공합니다.

Android 프레임워크에 정의된 대부분의 앱 구성요소에는 수명 주기가 연결되어 있습니다. 수명 주기는 운영체제 또는 프로세스에서 실행 중인 프레임워크 코드에서 관리합니다. 또한 Android 작동 방식의 핵심으로, 애플리케이션은 수명 주기를 고려해야 하며, 그렇게 하지 않으면 메모리 누수 또는 애플리케이션 비정상 종료가 발생할 수 있습니다.

화면에 기기 위치를 표시하는 활동이 있다고 가정해 보겠습니다. 일반적인 구현은 다음과 같을 수 있습니다.

internal class MyLocationListener(
        private val context: Context,
        private val callback: (Location) -> Unit
) {

    fun start() {
        // connect to system location service
    }

    fun stop() {
        // disconnect from system location service
    }
}

class MyActivity : AppCompatActivity() {
    private lateinit var myLocationListener: MyLocationListener

    override fun onCreate(...) {
        myLocationListener = MyLocationListener(this) { location ->
            // update UI
        }
    }

    public override fun onStart() {
        super.onStart()
        myLocationListener.start()
        // manage other components that need to respond
        // to the activity lifecycle
    }

    public override fun onStop() {
        super.onStop()
        myLocationListener.stop()
        // manage other components that need to respond
        // to the activity lifecycle
    }
}

이 샘플은 괜찮아 보이지만, 실제 앱에서는 수명 주기의 현재 상태에 따라 UI와 다른 구성요소를 관리하는 호출이 너무 많이 발생하게 됩니다. 여러 구성요소를 관리하면 onStart() 및 onStop()과 같은 수명 주기 메서드에 상당한 양의 코드를 배치하게 되어 유지하기 어려워집니다.

게다가 활동이나 프래그먼트가 중지되기 전에 구성요소가 시작된다는 보장도 없습니다. onStart()의 일부 구성 확인과 같은 장기 실행 작업을 진행해야 하는 경우 특히 그렇습니다. 이로 인해 onStop() 메서드가 onStart() 전에 종료되어 구성요소가 필요 이상으로 오래 유지되는 경합 상태가 발생할 수 있습니다.

class MyActivity : AppCompatActivity() {
    private lateinit var myLocationListener: MyLocationListener

    override fun onCreate(...) {
        myLocationListener = MyLocationListener(this) { location ->
            // update UI
        }
    }

    public override fun onStart() {
        super.onStart()
        Util.checkUserStatus { result ->
            // what if this callback is invoked AFTER activity is stopped?
            if (result) {
                myLocationListener.start()
            }
        }
    }

    public override fun onStop() {
        super.onStop()
        myLocationListener.stop()
    }

}

androidx.lifecycle 패키지는 이러한 문제를 탄력적이고 단독적인 방법으로 처리하는 데 도움이 되는 클래스와 인터페이스를 제공합니다.

2. Lifecycle

Lifecycle은 활동이나 프래그먼트와 같은 구성요소의 수명 주기 상태 관련 정보를 포함하며 다른 객체가 이 상태를 관찰할 수 있게 하는 클래스입니다.

Lifecycle은 두 가지 기본 열거를 사용하여 연결된 구성요소의 수명 주기 상태를 추적합니다.

  • 이벤트

프레임워크 및 Lifecycle 클래스에서 전달되는 수명 주기 이벤트입니다. 이러한 이벤트는 활동과 프래그먼트의 콜백 이벤트에 매핑됩니다.

  • 상태

Lifecycle 객체가 추적한 구성요소의 현재 상태입니다.

상태를 그래프의 노드로, 이벤트를 이 노드 사이의 가장자리로 생각하세요.

클래스는 DefaultLifecycleObserver를 구현하고 onCreate, onStart 등의 상응하는 메서드를 재정의하여 구성요소의 수명 주기 상태를 모니터링할 수 있습니다. 그러면 개발자는 다음 예에 나와 있는 것처럼 Lifecycle 클래스의 addObserver() 메서드를 호출하고 관찰자의 인스턴스를 전달하여 관찰자를 추가할 수 있습니다.

class MyObserver : DefaultLifecycleObserver {
    override fun onResume(owner: LifecycleOwner) {
        connect()
    }

    override fun onPause(owner: LifecycleOwner) {
        disconnect()
    }
}

myLifecycleOwner.getLifecycle().addObserver(MyObserver())

위 예에서 myLifecycleOwner 객체는 LifecycleOwner 인터페이스를 구현합니다. 이 내용은 다음 섹션에서 설명합니다.

3. LifecycleOwner

LifecycleOwner는 클래스에 Lifecycle이 있음을 나타내는 단일 메서드 인터페이스입니다. 이 인터페이스에는 클래스에서 구현해야 하는 getLifecycle() 메서드가 하나 있습니다. 대신 전체 애플리케이션 프로세스의 수명 주기를 관리하려는 경우 ProcessLifecycleOwner를 참고하세요.

이 인터페이스는 Fragment 및 AppCompatActivity와 같은 개별 클래스에서 Lifecycle의 소유권을 추출하고, 함께 작동하는 구성요소를 작성할 수 있게 합니다. 모든 맞춤 애플리케이션 클래스는 LifecycleOwner 인터페이스를 구현할 수 있습니다.

관찰자가 관찰을 위해 등록할 수 있는 수명 주기를 소유자가 제공할 수 있으므로, DefaultLifecycleObserver를 구현하는 구성요소는 LifecycleOwner를 구현하는 구성요소와 원활하게 작동합니다.

위치 추적 예에서는 MyLocationListener 클래스에서 DefaultLifecycleObserver를 구현하도록 한 후 onCreate() 메서드에서 활동의 Lifecycle로 클래스를 초기화할 수 있습니다. 이렇게 하면 MyLocationListener 클래스가 자립할 수 있습니다. 즉, 수명 주기 상태의 변경에 반응하는 로직이 활동 대신 MyLocationListener에서 선언됩니다. 개별 구성요소가 자체 로직를 저장하도록 설정하면 활동과 프래그먼트 로직을 더 쉽게 관리할 수 있습니다.

class MyActivity : AppCompatActivity() {
    private lateinit var myLocationListener: MyLocationListener

    override fun onCreate(...) {
        myLocationListener = MyLocationListener(this, lifecycle) { location ->
            // update UI
        }
        Util.checkUserStatus { result ->
            if (result) {
                myLocationListener.enable()
            }
        }
    }
}

일반적인 사용 사례에서는 Lifecycle이 현재 정상 상태가 아닌 경우 특정 콜백 호출을 피합니다. 예를 들어 활동 상태가 저장된 후 콜백이 프래그먼트 트랜잭션을 실행하면 비정상 종료를 트리거할 수 있으므로 콜백을 호출하지 않는 것이 좋습니다.

이러한 사용 사례를 쉽게 만들 수 있도록 Lifecycle 클래스는 다른 객체가 현재 상태를 쿼리할 수 있도록 합니다.

internal class MyLocationListener(
        private val context: Context,
        private val lifecycle: Lifecycle,
        private val callback: (Location) -> Unit
): DefaultLifecycleObserver {

    private var enabled = false

    override fun onStart(owner: LifecycleOwner) {
        if (enabled) {
            // connect
        }
    }

    fun enable() {
        enabled = true
        if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
            // connect if not connected
        }
    }

    override fun onStop(owner: LifecycleOwner) {
        // disconnect if connected
    }
}

이 구현으로 LocationListener 클래스는 수명 주기를 완전히 인식합니다. 다른 활동이나 프래그먼트의 LocationListener를 사용해야 한다면 클래스를 초기화하기만 하면 됩니다. 모든 설정과 해제 작업은 클래스 자체에서 관리합니다.

라이브러리에서 Android 수명 주기와 작동하는 데 필요한 클래스를 제공한다면 수명 주기 인식 구성요소를 사용하는 것이 좋습니다. 클라이언트 측에서 직접 수명 주기를 관리하지 않아도 라이브러리 클라이언트는 이러한 구성요소를 쉽게 통합할 수 있습니다.

4. 맞춤 LifecycleOwner 구현

지원 라이브러리 26.1.0 이상의 프래그먼트 및 활동에서는 이미 LifecycleOwner 인터페이스가 구현되어 있습니다.

LifecycleOwner를 만들려는 맞춤 클래스가 있다면 LifecycleRegistry 클래스를 사용할 수 있지만 다음 코드 예와 같이 이 클래스에 이벤트를 전달해야 합니다.

class MyActivity : Activity(), LifecycleOwner {

    private lateinit var lifecycleRegistry: LifecycleRegistry

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

        lifecycleRegistry = LifecycleRegistry(this)
        lifecycleRegistry.markState(Lifecycle.State.CREATED)
    }

    public override fun onStart() {
        super.onStart()
        lifecycleRegistry.markState(Lifecycle.State.STARTED)
    }

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

x. reference

https://developer.android.com/topic/libraries/architecture/lifecycle

profile
가장 아름다운 정답은 서로의 협업안에 있다.

0개의 댓글