본문 바로가기

Android/Tech

[Dagger-Hilt] ActivityScope 와 ActivityRetainedScope 의 차이

Unsplash, Will francis.

Hilt

Hilt 는 현재 가장 주목 받는 DI (Dependency Injection) 라이브러리입니다. 안드로이드 제트팩 라이브러리 이기도 하죠.

 

Hilt 에는 수많은 스코프 가 있습니다. 스코프의 존재 이유는 외부에서 생성되어 주입되는 인스턴스가 안드로이드 컨테이너의 생명주기에 따라 자동적으로 생성 및 해제되도록 하기 위해서입니다. 제 때 생성되지 않아 문제가 생기는 경우나, 해제되지 않아 지속적으로 메모리가 낭비되는 상황을 방지하기 위해서지요.

 

Scoping an object A to another object B means that throughout the lifecycle of B,
 it’ll always have the same instance of A. 
(오브젝트 A를 B에 스코핑하는 것은 B의 Lifecycle 동안 B는 항상 같은 A의 인스턴스를 가지도록 한다.

When it comes to dependency injection (DI), 
an object A scoped to a container means that the container will always provide the 
same instance of A until the container is destroyed.
(DI의 경우, 컨테이너에 스코프된 오브젝트 A는 컨테이너가 파괴될 때까지 같은 A의 인스턴스 참조하도록 한다.)
- Dagger-Hilt DOCS

 

Hilt 에서 사용할 수 있는 스코프의 종류와 그 범위는 다음과 같습니다.

 

 

대부분의 Scope 는 직관적인 이름을 가지기 때문에 쉽게 이해가 가능합니다. 

이해가 가지 않는 건 @ActivityScoped@ActivityRetainedScoped 입니다. 분명히 차이가 있을텐데, 쉽게 감이 오지 않습니다. 이번 기회에 알고 씁시다.


@ActivityScope

직관적인 네이밍입니다. @ActivityScoped 어노테이션이 붙은 컴포넌트는 Activity 의 onCreate() 에 생성되고 onDestroy() 에 해제됩니다.

@ActivityRetainedScoped

전자에 비해 직관적이지 못합니다. Retain 은 '보유하다' 라는 의미를 가집니다. Activity 가 컴포넌트를 보유한다? 그럼 전자와 다른게 무엇인지 궁금합니다.

@ActivityRetainedScoped 어노테이션이 붙은 컴포넌트는 전자와 같이 onCreate 에 생성되고, onDestroy 에 해제되지만 구성 변경 (가로 / 세로 화면 전환, 언어 변경 등) 에 의한 onDestroy 에는 해제되지 않습니다.

 

🤔 Q. ActivityRetainedScoped 는 그럼 ViewModelScoped 와 차이점이 뭔가요? ViewModelScoped 로 생성된 컴포넌트도 구성 변경에 의해 해제되지 않는데요?

간단한데, @ActivityRetainedScoped 는 어디까지나 Activity Lifecycle 을 따릅니다. @ViewModelScopedViewModelScoped 를 따르고요. 둘 다 구성 변경에 의해 해제되지 않는 것은 마찬가지이지만, 대상으로 하는 Lifecycle 이 근본적으로 다릅니다.

 


차이를 눈으로 확인하기 위해 직접 구현하여 로그를 출력해봤습니다.

hashCode 는 Hilt에 의해 생성된 객체의 필드입니다.

 

// ActivityScoped
I/System.out: Manager's address com.example.disingleton.Manager@e5fba42
I/System.out: Manager's hashCode 241154626

// 동일 객채 재생성
I/System.out: Manager's address com.example.disingleton.Manager@7a77859
I/System.out: Manager's hashCode 128415833

// 설정 변경 (화면 전환)
I/System.out: Manager's address com.example.disingleton.Manager@e32a062
I/System.out: Manager's hashCode 238198882

// 매번 객체의 주소값과 해시 코드가 다릅니다.
// ActivityRetainedScope
I/System.out: Manager's address com.example.disingleton.Manager@e5fba42
I/System.out: Manager's hashCode 241154626

// 동일 객체 재생성
I/System.out: Manager's address com.example.disingleton.Manager@7d1f7c2
I/System.out: Manager's hashCode 131200962

// 설정 변경 (화면 전환)
I/System.out: Manager's address com.example.disingleton.Manager@7d1f7c2
I/System.out: Manager's hashCode 131200962

// 재생성시에는 설정 변경시에는 같은 객체입니다.
// Singleton
I/System.out: Manager's address com.example.disingleton.Manager@e5fba42
I/System.out: Manager's hashCode 241154626

// 동일 객체 재생성
I/System.out: Manager's address com.example.disingleton.Manager@e5fba42
I/System.out: Manager's hashCode 241154626

// 설정 변경 (화면 전환)
I/System.out: Manager's address com.example.disingleton.Manager@e5fba42
I/System.out: Manager's hashCode 241154626

// 어떠한 상황에도 같은 객체를 유지합니다.

 

Activity 의 기본 전환은 onCreate -> onStart -> onResume -> onPause -> onStop -> onFinish 의 순서지만

설정 변경 (Configuration Change) 의 생명 주기 순서는 

onCreate -> onStart -> onRestoreInstanceState -> onResume -> onPause -> onSaveInstanceState -> onStop -> onDestory 입니다. 의존성이 주입된 인스턴스가 Activity Instance에 저장되었다가 불러와지는 형태입니다.


 실제로 사용하면서도 긴가민가했던 부분인데, 이번 기회에 제대로 알아보고 기록해둘 수 있어서 좋습니다.