본문 바로가기

Android/Basic

[Android Basic] Activities & Activity Lifecycle

[들어가며..]

Jetpack Compose의 등장으로 Android의 개발 방식이 근본적으로 바뀌어 가는 중이다.

전통적인 XML 기반 레이아웃과 View 시스템에서 벗어나, 선언형 UI 프레임워크를 제공하여 UI코드를 더 간결하고 직관적으로 만들었다. 이 덕분에 UI 로직과 비즈니스 로직을 분리하기 쉬워졌고, 무엇보다 현재 PlayConsole이 APK에서 AAB 업로드 환경으로 변함에 따라 권장되는 멀티 모듈을 도입하는 것도 수월해 졌다.

그래서 필자는 Compose환경을 기반으로한 Android Basic 내용을 틈틈히 정리해 나가볼까 한다.

오늘 다룰 주제는 역시 Basic하면 빼놓을 수 없는 4대 컴포넌트, 그 중 가장 많이 접하게 되는 Activity와 그의 Lifecycle이다.

Activity는 어떤 안드로이드 서적이나 강좌를 봐도 가장 먼저 등장하는 튜토리얼 같은 개념이다. 그래서 쓸까말까 고민을 많이 했지만, 기왕 정리를 시작한거 하나씩 복습해 나간다는 가벼운 마음으로 작성해 볼까한다.


[Activity]

Jetpack Compose의 도입 후, 액티비티는 주로 Composable 함수를 호스팅하는 컨테이너의 역할로 변하게 되었다.

전통적인 UI 작업 및 상태 관리 로직은 대부분 Composable 함수로 이동하였고, 액티비티는 위 사진처럼setContent()를 사용해 Compose UI함수를 호스팅하며 진입점 역할을 담당하게 되었다.

하지만 진입점 역할을 담당하게 되었다고 해서, 액티비티의 중요성이 떨어진 것은 아니다.

액티비티는 여전히

  • 앱이 포그라운드 / 백그라운드에서 활동 중인지에 대한 정보를 가지며
  • 다른 앱이나 웹으로부터 앱이 실행될 수 있도록 하는 Entry Point 정보를 포함한다.

즉, 액티비티는 오히려 더 간결하고 명확한 역할을 수행하게 되었다고 생각한다.

📎
Entry Point?

"Entry point"는 프로그램, 앱, 시스템, 또는 코드 블록이 시작되는 지점을 의미한다. 이는 프로그램의 실행이 시작되는 초기 지점이거나, 외부에서 특정 기능이나 서비스에 접근할 수 있는 시작 지점을 가리킨다.

그럼, Compose 관점에서 바라본 액티비티의 주요 역할과 정보는 어떤 것이 있을까?

  • Compose UI Hosting:
    • setContent 메서드를 사용하여 Composable 함수를 호스팅한다.
  • Lifecycle Management:
    • 액티비티의 생명주기는 Compose에서도 중요하다.
    • Composable 함수 내에서 Lifecycle 상태에 따라 특정 작업을 수행하려면, LaunchedEffect와 같은 Compose의 생명주기 관련 API를 사용할 수 있다.
  • Intent Data:
    • 액티비티가 시작될 때 전달된 Intent 데이터에 접근할 수 있다.
    • 이는 다른 액티비티나 앱, 또는 알림 등에서 데이터를 받을 때 사용된다.
  • Context Access:
    • LocalContext와 같은 Compose 제공자를 사용하여 Composable 내에서 Context에 접근할 수 있다.
  • Window and Decor Management:
    • 전체 화면 모드, 상태 바 색상 변경 등의 창 관리 기능은 여전히 액티비티에서 수행된다.
  • Permission Handling:
    • 권한 처리는 액티비티에서 이루어지며, Compose에서는 rememberPermissionState와 같은 헬퍼를 사용하여 권한 상태를 추적하고 요청할 수 있다.
  • Back Navigation:
    • 액티비티는 뒤로 가기 동작을 처리한다.
    • Compose 내에서는 BackHandler를 사용하여 커스텀 뒤로 가기 동작을 정의할 수 있다.
  • Configuration Changes:
    • 환경 설정 변경, 예를 들면 화면 회전, 언어 변경 등에 응답하는 로직은 액티비티에서 관리될 수 있다.

액티비티는 여전히 Android 애플리케이션의 중요한 컴포넌트이다.

그러나 그 역할은 이제 Composable 함수와의 상호 작용에 중점을 둔 것으로 바뀌었다.


[Lifecycle]

Jetpack Compose와 전통적인 액티비티 생명주기는 서로 연관되어 있지만, 다소 다른 점에서 접근한다.

  • Activity Lifecycle 전통적인 Android 개발에서 액티비티는 앱의 주요 컴포넌트이다.

    사용자 인터페이스를 호스팅하며, 시스템과 사용자 간의 상호작용을 중개한다. 따라서 액티비티의 생명주기는 앱의 동작과 성능에 큰 영향을 미친다.

  • Compose Lifecycle Jetpack Compose는 UI 구성을 위한 도구로 액티비티 내부에서 실행된다. 이렇게만 보면 당연히 액티비티의 생명주기에 종속적일 것 같지만, Composable 함수는 그 자체로 생명주기가 있으며 이는 화면의 일부분이나 전체 UI를 구성할 때 사용된다. Compose의 생명주기는 주로 Composable이 화면에 표시되거나 사라질 때 관련이 된다.

즉, 액티비티의 생명주기는 앱의 전반적인 상태와 동작을 관리하고, Compose의 생명주기는 UI의 세부적인 부분을 관리한다.

두 생명주기는 서로 다르지만, Compose를 사용하는 앱에서는 둘 다 중요하다.

그래서 Compose의 생명주기를 언급했지만, 이번 글에서는 Activity의 생명주기에 대해 서만 작성한다. LaunchedEffect,SideEffect와 같은 Compose Lifecycle은 이후 Compose Screen과 관련된 글에서 다루도록 하겠다.

생명주기하면 빠질 수 없는 이미지다.

Android의 액티비티는 시스템과 사용자의 상호작용에 따라 다양한 상태를 거치게된다. 이런 상태 전환을 개발자가 처리할 수 있도록 Android는 생명주기 콜백을 제공한다.

액티비티 생명주기에 대해 간단하게 설명하겠다.

1. onCreate():

  • 액티비티가 처음 생성될 때 호출되며, 여기서 기본적인 초기화 작업들을 수행하게 된다.
  • 예를 들면 UI 설정, 데이터 바인딩, 리스너 설정 등의 작업이 이루어진다.

2. onStart():

  • 액티비티가 사용자에게 보여질 준비가 되었을 때 호출된다.
  • 이 시점에서 액티비티는 화면에 표시되기 시작하지만 아직 사용자와의 상호작용을 시작하지 않았기 때문에, foreground에 있지만 활성(Active) 상태는 아니다.

3. onResume():

  • 액티비티가 사용자와 상호작용을 시작하기 직전에 호출된다.
  • 이 상태에서 액티비티는 활성 상태(Active)로 간주된다.

4. onPause():

  • 다른 액티비티가 화면의 전경(foreground)으로 나타나면 호출된다.
  • 데이터 저장, 스레드 중지와 같은 것들을 처리하게 된다.

5. onStop():

  • 액티비티가 더 이상 사용자에게 보이지 않을 때 호출된다.
  • 예를 들면, 사용자가 앱을 전환하거나 홈 화면으로 돌아갔을 때 호출된다.

6. onDestroy():

  • 액티비티가 완전히 종료되기 전에 호출된다.
  • 시스템 자원을 회수하거나, 메모리 해제와 같은 마무리 작업을 수행하는 부분이다.

7. onRestart():

  • onStop() 이후에 액티비티가 다시 시작될 때 호출된다.

실행순서는 상기 이미지를 참조할 것

개인적으로 처음 안드로이드를 공부하는 사람이라면, 저 이미지를 외우는 것을 추천한다.


[‣ onStart()와 onResume()의 차이]

필자는 처음 안드로이드를 공부할 때, onStart()와 onResume()을 구분하는 이유가 궁금했던 적이 있다. 언뜻보면 구분 될 이유가 없는 것 같기 때문이다.

이 둘은 액티비티의 가시성상호작용 가능성에 따라 구분된다.

상단의 생명주기 설명에 조금 더 살을 붙여 각각을 살펴보자면 다음과 같다.

  1. onStart()
    • 가시성(visibility)
      • 액티비티가 사용자에게 가시적으로 될 때 호출된다.
      • 이 상태에서 아직 사용자와 상호작용 할 수 없다.
    • 목적
      • 액티비티가 사용자에게 보이기 시작할 때 필요한 초기화 작업이나 리소스 할당 수행.
      • UI 업데이트, 애니메이션 시작, 데이터 로딩 등의 작업 수행
  1. onResume():
    • 상호작용(Interactivity)
      • 액티비티가 사용자와 상호작용을 시작할 준비가 되었을 때 호출
      • 이 상태를 비소로 Foreground에 완전히 활성(Active)된 상태라고 한다.
    • 목적
      • 사용자와의 상호작용을 위한 준비 & 실제 상호작용 수행.
      • 실시간 데이터 페치, 사용자 입력을 위한 리스너 활성화 등의 작업 수행

즉, onStart()에서는UI와 관련된 작업을, onResume()에서는 입출력같은 상호작용 작업을 수행한다고 이해하면 된다.

물론 이는 일반적인 가이드를 따를 경우다. 실제 앱을 개발할 때는 상황과 요구사항에 따라 다르게 적용될 수 있다.


[왜 생명주기 함수들은 콜백으로 되어있을까?]

콜백 함수는 결과로 특정 상황이나 상태 변화를 반환한다.

생명주기 함수들은 “내가 이 상태까지 왔다”라는 것을 알려, 개발자가 특정 작업을 적절한 타이밍에 배치할 수 있도록 유도한다.

그 이유는 다음과 같다.

  • 자원 관리 앱은 제한된 자원(메모리, CPU, 배터리 등)을 사용하여 실행된다. 생명주기 함수를 통해 개발자는 필요할 때만 자원을 사용하고, 필요하지 않을 때는 자원을 해제하여 최적화를 이룰 수 있다.

  • 사용자 경험 액티비티의 상태에 따라 UI를 업데이트하거나 사용자의 입력을 처리하는 등의 작업을 수행하여 앱의 반응성을 유지한다.

  • 데이터 무결성 데이터의 안정성과 무결성을 보장하기 위해, 예를 들면 액티비티가 백그라운드로 이동하기 전에 DB에 데이터를 저장하는 등의 작업을 명령할 수 있다.


[‣ Compose 환경에서의 Activity 생명주기]

마지막으로 Compose 환경에서 Activity의 생명주기와 Composable 함수의 관계에 대해 살펴보겠다.

1. onCreate():

  • setContent()를 통해 Composable 함수들이 실행되며, UI가 구성된다.

2. onStart() / onResume():

  • 이 생명주기 콜백에서는 특별한 Composable 관련 작업이 필요하지 않다.
  • Composable UI는 이미 setContent()에서 설정되었으며, UI의 상태가 변경되면 자동으로 업데이트(recomposition)되기 때문이다.

3. onPause() / onStop() / onDestroy():

  • Composable UI는 자동으로 업데이트되므로 이 생명주기에서도 특별한 작업이 필요하지 않지만, 필요한 경우 여기에서 리소스 해제와 같은 작업을 수행할 수 있다.
  • Compose는 선언적 UI를 제공하므로, UI와 관련된 상태 변경이 발생하면 해당 부분만 자동으로 재구성한다.
  • 이로 인해 Activity 생명주기 메서드에서 수동으로 UI 업데이트 작업을 수행할 필요가 사라졌다.
  • 하지만 그렇다고 아예 쓸모가 없는 것은 아니다.
  • UI를 제외한 시스템 리소스 해지, 딥링크 정보 수신 등의 작업은 여전히 생명주기에서 처리되어야 하기 때문이다.


[마무리]

생각보다 글이 길어졌다.

잘못된 내용을 지적하는 댓글은 언제나 환영

그럼 오늘도 즐거운 안-드 공부 하기 바란다.


Uploaded by N2T