코딩하는 개굴이

[안드로이드] LifecycleObserver ( Lifecycle Aware Component 만들기 ) 본문

안드로이드

[안드로이드] LifecycleObserver ( Lifecycle Aware Component 만들기 )

개굴이모자 2023. 1. 1. 02:08
반응형

 

시작하기 전에 해당 포스팅을 찾아왔다면 Lifecycle 이 무엇인지 알고는 있겠지만 확실히 하기 위해서 잠시 요약해보도록 하자.

 

Lifecycle

Lifecycle 이란, activity 와 fragment 와 같은 컴포넌트의 lifecycle state 를 들고 있는 정보로, 다른 object 들이 해당 state 를 추적(observe) 할 수 있도록 허용하는 클래스를 말한다.

 

Lifecycle 은 Event, State 를 활용해 연결된 component 의 수명 주기 상태를 추적한다.

 

Event 는 프레임워크 및 Lifecycle 에서 전달되는 수명 주기 이벤트로, activity 와 fragment 의 콜백 이벤트에 맵핑되고,

State 는 Lifecycle 객체가 추적한 컴포넌트의 현재 상태를 말한다.

 

(하위 그래프에서 state 는 node, event 는 edge 라고 생각할 수 있다.)

 

Lifecycle Aware Component 구현하기

경우에 따라 특정 컴포넌트에 맞춰 함께 서비스가 실행되거나 관찰되어야하는 케이스가 존재할 수 있다. 이럴때 필요한 것이 lifecycle aware component 인데, lifecycle aware component는 activity와 fragment 같은 다른 컴포넌트들의 lifecycle 변경에 따라 작업을 실행한다.

 

이를 어떻게 구현할 수 있을까?

컴포넌트의 Lifecycle method 안에서 직접 구현?

일반적으로 의존적인 컴포넌트의 lifecycle method 안에서 액션을 구현하는 방식으로 개발을 하지만, 이러한 패턴은 구조상의 복잡도와 오류 증가를 야기할 수 있다. 따라서, Lifecycle aware component 를 사용하여 component 자체로 액션 코드를 옮길 수 있다.

예시로, 아래와 같이 MyActivity 의 lifecycle에 따라 Location Service 를 조절하려한다고 해보자.

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
    }
}

코드를 위와 같이 구성할 경우 실제 onStart/onStop 메서드 안에 로직이 상당하게 들어가야하므로 유지하기가 어려워질 수 있다. 또한, stop 이 start 보다 먼저 수행되어 의도하지 않은 동작이 나올 수도 있다.

LifecycleObserver 를 사용하자.

클래스는 컴포넌트의 라이프사이클을 DefaultLifecycleObserver 를 implement 하여 모니터하여 onCreate, onStart 등을 대응하는 메서드를 오버라이딩 할 수 있다. 그리고 라이프사이클에 addObserver() 메서드로 observer 를 추가한다.

 

이렇게 하면 원하는 라이프 사이클에 맞게 override 된 부분에서 원하는 로직을 수행할 수 있고, 실제 컴포넌트에 로직이 들어가지 않기 때문에 복잡도도 줄어드는 장점이 있다.

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

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

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

 

 

LifecycleOwner

LifecycleOwner는 클래스에 Lifecycle 이 있음을 나타내는 single method interface 로, 해당 인터페이스에서는 getLifecycle() 메서드가 존재한다. 이 인터페이스는 fragment 및 AppCompatActivity 와 같은 개별 클래스에서 Lifecycle을 추출하고, 함께 작동하는 컴포넌트를 생성할 수 있도록 한다.

라이브러리 26.1.0 이상의 fragment 및 activity 에서는 이미 LifecycleOwner 인터페이스가 구현되어있으나, 구현하고자 한다면, 아래와 같은 방법으로 클래스에 State 에 대한 이벤트를 수동으로 전달할 수 있다.

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
    }
}

 

 

예제

LifecycleObserver 를 생성하여, 해당 lifecycle 에 맞게 string 을 반환하는 lifecycle aware 한 구성요소에 대한 예시를 하나 만들어보도록 하자.

 

MyLifecycleObserver

조건에 충족할 경우 enable 시킬 수 있도록 하고 해당 값이 true 일 경우 string 을 반환하도록 하였다.

package com.gaegul2moja.lifecycletest

import android.util.Log
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner

class MyLifecycleObserver(
    private val lifecycle: Lifecycle,
    private val callback: (String) -> Unit,
    ): DefaultLifecycleObserver {
    private var enabled: Boolean = false

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

    override fun onStart(owner: LifecycleOwner) {
        super.onStart(owner)
        if (enabled) {
            Log.d("TAG", "onStart")
            callback.invoke("onStart")
        }
    }

    override fun onCreate(owner: LifecycleOwner) {
        super.onCreate(owner)
        if (enabled) {
            Log.d("TAG", "onCreate")
            callback.invoke("onCreate")
        }
    }

    override fun onDestroy(owner: LifecycleOwner) {
        super.onDestroy(owner)
        if (enabled) {
            Log.d("TAG", "onDestroy")
            callback.invoke("onDestroy")
        }
    }

    override fun onResume(owner: LifecycleOwner) {
        super.onResume(owner)
        if (enabled) {
            Log.d("TAG", "onResume")
            callback.invoke("onResume")
        }
    }

    override fun onPause(owner: LifecycleOwner) {
        super.onPause(owner)
        if (enabled) {
            Log.d("TAG", "onPause")
            callback.invoke("onPause")
        }
    }

    override fun onStop(owner: LifecycleOwner) {
        super.onStop(owner)
        if (enabled) {
            Log.d("TAG", "onStop")
            callback.invoke("onStop")
        }
    }
}

MainActivity

main 의 코드는 간단하다. MyLifecycleObserver 를 생성하고, callback 이 오면 그에 맞게 TextView 를 수정할 수 있도록 하였다. 그리고, MyLifecycleObserver 를 enable 시키고 이를 lifecycle 에 Observer 로 등록해두었다.

package com.gaegul2moja.lifecycletest

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView

class MainActivity : AppCompatActivity() {
    private lateinit var myLifecycleObserver: MyLifecycleObserver
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        myLifecycleObserver = MyLifecycleObserver(lifecycle) { string ->
            val textView = findViewById<TextView>(R.id.main_tv)
            textView.text = string
        }

        myLifecycleObserver.enable()
        lifecycle.addObserver(myLifecycleObserver)
    }
}

결과

TextView 도 로그도 잘 찍히는 것을 볼 수 있다! 간단한 예시이다.

 

참고 링크

 

반응형
Comments