LifecycleOwner — 는 Android Jetpack 라이브러리의 핵심 인터페이스로, 객체가 생명주기를 가지고 있음을 선언하고 getLifecycle() 메서드를 통해 접근을 제공합니다. 이는 현대 Android 애플리케이션의 컴포넌트 아키텍처의 기초이며, 생명주기 관리 로직을 Activity나 Fragment의 구체적인 구현으로부터 분리할 수 있게 합니다. Google I/O 2024 데이터에 따르면 Android 신규 프로젝트의 85% 이상이 LifecycleOwner를 사용하여 구독을 관리하고 메모리 누수를 방지합니다. 이 인터페이스는 LiveData, ViewModel 및 기타 Jetpack 컴포넌트의 기반이 되어, 컴포넌트가 활성 상태일 때만 안전하게 코드가 실행되도록 보장합니다.
핵심 요점
LifecycleOwner — 는 androidx.lifecycle 패키지의 인터페이스로, Lifecycle 객체를 반환하는 단일 메서드 getLifecycle()을 포함합니다. 이 객체는 컴포넌트의 현재 상태(CREATED, STARTED, RESUMED, DESTROYED)를 추적하고 상태가 변경될 때 모든 구독 중인 관찰자에게 알립니다. LifecycleOwner는 Architecture Components의 일부이며 lifecycle-runtime 라이브러리에 포함되어 있습니다.
인터페이스의 주요 역할은 생명주기에 대한 접근을 표준화하는 것입니다. Jetpack 등장 이전에는 개발자가 onStart에서 수동으로 구독하고 onStop에서 해제하여 코드 중복과 오류가 발생했습니다. LifecycleOwner는 이 문제를 해결하여 모든 Android 컴포넌트에 통일된 메커니즘을 제공합니다. 생명주기 메서드를 명시적으로 호출하는 대신, 개발자는 한 번 Lifecycle에 구독하면 알림이 자동으로 도착합니다.
인터페이스는 Kotlin에서 단일 추상 메서드를 가진 함수형 인터페이스로 선언됩니다:
interface LifecycleOwner {
val lifecycle: Lifecycle
}
인터페이스의 함수형 특성 덕분에 델리게이트나 람다를 사용하여 쉽게 구현할 수 있습니다. 이는 호스트의 생명주기 변경에 반응해야 하는 Custom Views 및 ViewModel 클래스를 만드는 데 특히 편리합니다. getLifecycle()에서 얻은 Lifecycle 객체는 구독 관리를 위한 addObserver 및 removeObserver 메서드를 제공합니다.
LifecycleOwner는 Lifecycle과 LifecycleObserver라는 두 가지 핵심 클래스와 함께 작동합니다. Lifecycle은 컴포넌트의 현재 상태를 enum State(INITIALIZED, CREATED, STARTED, RESUMED, DESTROYED)로 저장하고 상태 간 전환을 추적합니다. 상태가 변경되면 Lifecycle은 등록된 모든 관찰자에게 알리고 해당 애노테이션이 달린 메서드를 호출합니다. 이 메커니즘을 "lifecycle-aware"라고 하며, 코드는 컴포넌트가 적절한 상태에 있을 때만 실행됩니다.
이벤트 전달 메커니즘은 Observer 패턴을 기반으로 합니다. LifecycleOwner는 Observable 역할을, LifecycleObserver 구현은 Observer 역할을 합니다. Activity나 Fragment는 상태가 변경될 때(onCreate → onStart → onResume → onPause → onStop → onDestroy) AndroidX 시스템에 자동으로 추가되는 내부 메커니즘 ReportFragment를 통해 Lifecycle에 알립니다. 개발자가 수동으로 Lifecycle 메서드를 호출할 필요가 없습니다 — 모든 것이 자동으로 이루어집니다.
| Lifecycle 상태 | 이벤트 | Android 생명주기 메서드 |
|---|---|---|
| INITIALIZED | — | onCreate 전 |
| CREATED | ON_CREATE | onCreate |
| STARTED | ON_START | onStart |
| RESUMED | ON_RESUME | onResume |
| STARTED | ON_PAUSE | onPause |
| CREATED | ON_STOP | onStop |
| DESTROYED | ON_DESTROY | onDestroy |
중요한 세부 사항: Lifecycle은 프로세스 충돌 시에도 ON_STOP 및 ON_DESTROY 이벤트가 전달됨을 보장합니다. 이는 LifecycleOwner를 중요한 리소스를 해제하기 위한 신뢰할 수 있는 도구로 만듭니다. 일반 상태 저장에는 ViewModel에서 SavedStateHandle을 사용하는 것이 권장되지만, LifecycleOwner는 기본적인 안전 수준을 제공합니다.
LifecycleOwner의 이벤트를 구독하는 방법은 두 가지입니다: 애노테이션이 있는 기존 LifecycleObserver와 명시적 메서드가 있는 현대적인 DefaultLifecycleObserver입니다. 두 번째 접근 방식은 더 나은 타입 안전성을 제공하고 애노테이션 방식에서 사용되었던 리플렉션을 피할 수 있기 때문에 Google에서 2022년부터 권장하고 있습니다. DefaultLifecycleObserver는 Java 8+ 또는 Kotlin이 필요하며 새 프로젝트에 선호됩니다.
DefaultLifecycleObserver를 통한 구독 예시:
class MyObserver : DefaultLifecycleObserver {
override fun onStart(owner: LifecycleOwner) {
// 컴포넌트가 활성 상태일 때만 GPS 추적 시작
startLocationUpdates()
}
override fun onStop(owner: LifecycleOwner) {
// 백그라운드 모드 전환 시 안전한 중지
stopLocationUpdates()
}
}
// 연결:
lifecycleOwner.lifecycle.addObserver(MyObserver())
DefaultLifecycleObserver의 각 메서드는 매개변수로 LifecycleOwner를 받습니다. 이를 통해 관찰자는 별도로 전달하지 않고도 실행 중인 컴포넌트의 컨텍스트에 접근할 수 있습니다. 이 접근 방식은 코드를 더 모듈화되고 테스트 가능하게 만듭니다 — Observer는 특정 Activity나 Fragment 구현에 의존하지 않고 LifecycleOwner 추상화와 함께 작동합니다.
@OnLifecycleEvent 애노테이션을 사용하는 이전 방식은 레거시 프로젝트에서 여전히 발견되지만 새 코드에서는 권장되지 않습니다. 애노테이션 처리에 필요한 리플렉션은 오버헤드를 추가하고 컴파일 시에 감지되지 않는 오류를 발생시킬 수 있습니다. Google은 공식적으로 DefaultLifecycleObserver로의 마이그레이션을 권장합니다.
// 오래된 접근 방식 — 새 프로젝트에 권장되지 않음
class MyLegacyObserver : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onStart() {
startLocationUpdates()
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onStop() {
stopLocationUpdates()
}
}
애노테이션 접근 방식에는 심각한 단점이 있습니다: Observer의 수명 제어 부족입니다. 개발자가 LifecycleOwner 파기 시 Observer 구독 해제를 잊으면, 가비지 컬렉터가 호출될 때까지 Observer 객체가 메모리에 남아 있습니다. DefaultLifecycleObserver는 이 문제를 해결합니다 — Observer는 Lifecycle에 바인딩되고 DESTROYED 상태로 전환 시 자동으로 구독 해제됩니다.
AppCompat 1.1.0 및 AndroidX Fragment 1.2.0부터 AppCompatActivity 또는 Fragment를 상속하는 모든 Activity와 Fragment는 자동으로 LifecycleOwner가 됩니다. 즉, getLifecycle() 메서드가 기본적으로 사용 가능하며 추가 설정 없이 생명주기 이벤트 구독이 작동합니다. 개발자는 Activity나 Fragment의 어디서든 lifecycle.addObserver()를 호출하기만 하면 됩니다.
Activity에 LifecycleOwner를 통합하는 예제를 살펴보겠습니다:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycle.addObserver(LocationObserver(this))
}
}
이 예제에서 lifecycle은 AndroidX Activity 덕분에 사용 가능한 확장 속성입니다. LocationObserver 관찰자는 Activity의 시작(ON_START) 및 중지(ON_STOP)에 대한 알림을 자동으로 받습니다. 화면 회전 시 Observer는 ON_DESTROY와 ON_CREATE에 대한 알림을 받아 추가 코드 없이 구성 변경을 올바르게 처리할 수 있습니다.
Fragment는 인터페이스를 통해 LifecycleOwner를 구현하며, 그 Lifecycle은 부모 Activity가 아닌 Fragment의 생명주기에 바인딩됩니다. 이것이 중요합니다: Fragment의 Lifecycle은 Fragment가 트랜잭션에서 제거될 때 DESTROYED로 전환되는 반면, Activity는 RESUMED 상태로 남을 수 있습니다. 이러한 차이로 인해 Observer는 각 컴포넌트의 생명주기에 개별적으로 구독할 수 있습니다.
class MyFragment : Fragment() {
private val uiStateObserver = UiStateObserver()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycle.addObserver(uiStateObserver)
}
}
Fragment에서 LifecycleOwner를 사용하는 중요한 이점은 Fragment가 DESTROYED로 전환될 때의 자동 구독 해제입니다. 이는 Fragment가 동적으로 생성되고 파기될 수 있는 ViewPager에서 특히 중요합니다. 이 시나리오에서 수동 구독 관리는 매우 복잡하고 오류가 발생하기 쉬웠을 것입니다.
LifecycleOwner 인터페이스는 생명주기를 가진 모든 클래스에서 구현할 수 있습니다. 이는 Custom Views, Services 및 일부 아키텍처 솔루션에서는 ViewModel에도 유용합니다. Google은 Lifecycle의 상태를 관리하고 이벤트를 생성하는 도우미 클래스 LifecycleRegistry를 제공합니다. 컴포넌트 상태가 변경될 때 개발자는 LifecycleRegistry의 해당 메서드를 수동으로 호출해야 합니다.
Custom View에서 LifecycleOwner 구현 예제:
class MyCustomView(
context: Context,
attrs: AttributeSet?
) : FrameLayout(context, attrs), LifecycleOwner {
private val lifecycleRegistry = LifecycleRegistry(this)
override val lifecycle: Lifecycle
get() = lifecycleRegistry
fun onStart() {
lifecycleRegistry.setCurrentState(Lifecycle.State.STARTED)
}
fun onStop() {
lifecycleRegistry.setCurrentState(Lifecycle.State.CREATED)
}
}
이 예제에서 LifecycleRegistry는 상태 저장소 역할을 합니다. onStart/onStop 메서드는 Custom View가 표시되거나 숨겨질 때 부모 컴포넌트(예: Activity)에 의해 호출되어야 합니다. LifecycleRegistry는 상태 간 전환에 필요한 이벤트를 자동으로 계산하고 구독 중인 모든 Observer에게 알립니다.
자체 LifecycleOwner를 구현할 때는 규칙을 따르는 것이 중요합니다: LifecycleRegistry의 상태는 다른 모든 작업 후에 해당 생명주기 메서드의 마지막에 업데이트되어야 합니다. 이는 컴포넌트가 새 상태에 완전히 준비되었을 때 Observer가 알림을 받도록 보장합니다. 대안으로 LifecycleRegistry.createUnsafe를 사용하는 것도 가능하지만 스레드에 주의가 필요합니다.
LifecycleOwner는 여러 주요 Android Jetpack 컴포넌트의 기초입니다. LiveData는 활성 상태를 결정하고 컴포넌트 파기 시 자동 구독 해제를 위해 LifecycleOwner를 사용합니다. ViewModel은 직접 LifecycleOwner를 구현하지 않지만 SavedStateHandle을 통해 Lifecycle을 얻을 수 있습니다. Navigation Component는 NavBackStackEntry에서 구독 관리를 위해 LifecycleOwner를 사용합니다. 이 상호 관계를 이해하면 견고한 기반 위에 애플리케이션 아키텍처를 구축하는 데 도움이 됩니다.
LiveData와 LifecycleOwner의 상호 작용:
class ExampleActivity : AppCompatActivity() {
private val viewModel: ExampleViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.userData.observe(this) { data ->
// this — LifecycleOwner (Activity)
// 코드는 Activity가 RESUMED 상태일 때만 실행됩니다
updateUI(data)
}
}
}
LiveData는 observe() 메서드에서 LifecycleOwner를 필요로 합니다. 이는 UI 업데이트가 활성 상태에서만 발생함을 보장하기 때문입니다. Activity가 백그라운드에 있으면 LiveData는 마지막 값을 유지하지만 Observer에게 알리지 않습니다. RESUMED로 돌아오면 Observer는 추가 네트워크나 데이터베이스 요청 없이 최신 값을 받습니다.
DataBinding도 observable 필드를 Activity나 Fragment의 생명주기에 바인딩하기 위해 LifecycleOwner를 사용합니다. 이를 통해 ViewModel + DataBinding 조합에서 메모리 누수를 방지할 수 있습니다 — LifecycleOwner가 파기되면 모든 구독이 자동으로 정리됩니다. 이 접근 방식은 코드를 선언적이고 안전하게 만듭니다.
LifecycleOwner를 올바르게 사용하려면 몇 가지 중요한 규칙을 따라야 합니다. 첫 번째이자 가장 중요한 것: 항상 onCreate/onViewCreated에서 Observer를 구독하고 그 이후가 아닙니다. 이는 Observer가 Lifecycle의 초기 상태(onCreate 후 CREATED)를 받고 이벤트를 놓치지 않도록 보장합니다. 두 번째 규칙: 모든 새 프로젝트에서는 애노테이션 방식 대신 DefaultLifecycleObserver를 사용하세요.
코루틴과 LifecycleOwner를 다루는 현대적인 접근 방식 — repeatOnLifecycle 확장:
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.flow.collect { value ->
updateUI(value)
}
}
}
이 패턴은 Flow의 collect가 STARTED 또는 RESUMED 상태에서만 활성화됨을 보장합니다. STOPPED로 전환 시 수집이 자동으로 취소되고 STARTED로 돌아오면 다시 시작됩니다. repeatOnLifecycle은 Fragment에서 Flow의 수동 구독 해제를 대체하며 UI 컴포넌트에서 비동기 데이터 스트림을 작업하기 위한 Google 권장 접근 방식입니다.
또 다른 중요한 권장 사항: 생명주기와 관련 없는 로직에 LifecycleObserver를 남용하지 마세요. 컴포넌트가 특정 상태에서 작업을 수행해야 하지만 파기 시 구독 해제가 필요하지 않은 경우 onStart/onStop에서 명시적 메서드 호출을 사용하는 것이 좋습니다. LifecycleObserver는 수동 구독 관리가 복잡하고 오류가 발생하기 쉬운 장기 실행 컴포넌트(LocationListener, SensorManager)에 적합합니다.
자주 묻는 질문
LifecycleOwner — 는 객체가 생명주기를 가지고 있음을 선언하는 인터페이스입니다. Lifecycle — 는 현재 상태를 저장하고 Observer를 관리하는 클래스입니다. LifecycleOwner는 getLifecycle()을 통해 Lifecycle을 제공합니다.
아니요, Lifecycle은 DESTROYED로 전환 시 모든 Observer를 자동으로 구독 해제합니다. 이것이 LifecycleOwner의 주요 장점 중 하나입니다 — 개발자가 onDestroy에서 수동으로 removeObserver를 호출할 필요가 없습니다.
Fragment는 AndroidX 프래그먼트 인터페이스를 통해 LifecycleOwner를 구현합니다. 그 Lifecycle은 Activity와 별도로 Fragment의 생명주기에 바인딩됩니다. 이를 통해 Observer는 부모 Activity가 아닌 Fragment의 이벤트에 반응할 수 있습니다.
네, 이를 위해 LifecycleRegistry를 사용합니다. Custom View는 LifecycleOwner 인터페이스를 구현하고 가시성 또는 창에 대한 첨부가 변경될 때 LifecycleRegistry의 상태를 수동으로 업데이트해야 합니다.
LifecycleOwner는 다른 문제를 해결합니다: 생명주기 이벤트 구독 관리이지 코루틴 취소가 아닙니다. 코루틴에는 lifecycleScope가 사용되며 LifecycleOwner가 파기될 때 실행 중인 코루틴을 자동으로 취소합니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.