Android Process & App Lifecycle

Victor·2026년 1월 8일

Process & App Lifecycle 개요

대부분의 경우, 모든 Android 애플리케이션은 개별적인 리눅스(Linux) 프로세스 내에서 실행된다.

이 프로세스는 애플리케이션의 코드가 실행되어야 할 때 생성된다.

프로세스 실행 상태 유지 경우는 다음과 같다.

  1. 시스템이 다른 애플리케이션을 위해 메모리를 회수해야 하거나
  2. 해당 프로세스가 더 이상 필요하지 않을 때까지 한다.

Android 의 근본적인 특징은 어플리케이션 프로세스의 생명 주기는 어플리케이션 그 자체가 직접적으로 제어하지 않는다.

대신 시스템이 아래 세 가지를 종합적으로 고려하여 프로세스 생명주기를 결정한다.

  1. 현재 실행 중이라고 파악한 앱의 구성 요소
  2. 해당 요소들이 사용자에게 얼마나 중요한지
  3. 그리고 시스템의 전체 가용 메모리 상태

앱의 구성 요소

애플리케이션 개발자는 다양한 구성 요소(특히 Activity, Service, BroadcastReceiver)가 앱 프로세스의 수명에 어떤 영향을 미치는지 이해하는 것이 중요하다. 이러한 구성 요소를 올바르게 사용하지 않으면, 시스템이 중요한 작업을 수행 중인 애플리케이션 프로세스를 종료하는 결과를 초래할 수 있다.

프로세스 수명 주기와 관련된 흔한 오류 사례는 BroadcastReceiveronReceive() 메서드에서 Intent를 받았을 때 스레드를 시작한 후 함수를 반환해 버리는 경우이다. 함수가 반환되는 순간 시스템은 해당 BroadcastReceiver더 이상 활성 상태가 아니라고 판단하며, 프로세스 내에 다른 활성 구성 요소가 없다면 해당 프로세스가 더 이상 필요하지 않은 것으로 간주한다.

따라서 시스템은 메모리 회수를 위해 언제든 프로세스를 종료할 수 있으며, 이 과정에서 프로세스 내에서 실행 중이던 스레드도 함께 강제 종료된다. 이 문제의 일반적인 해결책은 BroadcastReceiver에서 JobService를 예약하여 시스템이 해당 프로세스 내에서 활발한 작업이 진행 중임을 인지하게 만드는 것이다.

메모리가 부족할 때 어떤 프로세스를 종료할지 결정하기 위해, 안드로이드는 실행 중인 구성 요소와 그 상태에 따라 각 프로세스를 중요도 계층으로 분류한다. 중요도가 높은 순서대로 나열한 프로세스 유형은 다음과 같다.

1. 포그라운드 프로세스 (Foreground Process)

포그라운드 프로세스는 사용자가 현재 수행 중인 작업에 직접적으로 필요한 프로세스이다. 다양한 애플리케이션 구성 요소로 인해 해당 프로세스는 다음과 같은 조건에서 포그라운드로 간주된다.

  • 사용자와 상호작용 중인 화면 최상단의 Activity를 실행 중일 때
    (=onResume() 메서드가 호출된 상태)
  • 현재 실행 중인 BroadcastReceiver를 보유하고 있을 때
    (=onReceive() 메서드가 실행 중인 상태)
  • 서비스 콜백(Service.onCreate(), Service.onStart(), Service.onDestroy()) 중 하나에서 코드를 실행 중인 Service를 보유하고 있을 때.

시스템 내에 이러한 프로세스는 극히 소수이며, 메모리가 너무 부족하여 이들조차 실행할 수 없는 최후의 수단으로만 종료된다. 일반적으로 이 상황이 발생하면 장치는 메모리 페이징 상태에 도달한 것이므로, 사용자 인터페이스의 응답성을 유지하기 위해 프로세스 종료가 필수적이다.

2. 가시적 프로세스 (Visible Process)

가시적 프로세스는 사용자가 현재 인지하고 있는 작업을 수행 중이므로, 이를 종료하면 사용자 경험에 눈에 띄는 부정적 영향을 미친다. 프로세스는 다음과 같은 조건에서 가시적인 것으로 간주된다.

  • 사용자 화면에 보이지만 포그라운드는 아닌 Activity를 실행 중일 때
    (=onPause() 메서드가 호출된 상태)
    예를 들어, 포그라운드 액티비티가 대화상자(Dialog) 형태로 표시되어 그 뒤로 이전 액티비티가 보이는 경우이다.
  • Service.startForeground()를 통해 포그라운드 서비스로 실행 중인 서비스를 보유하고 있을 때.
    이는 시스템에 사용자가 이 서비스를 인지하고 있음을 알리는 것이며, 사실상 가시적인 상태와 다름없다.
  • 라이브 배경화면이나 입력기(IME) 서비스와 같이 사용자가 인지하고 있는 특정 기능을 위해 시스템이 사용하는 서비스를 호스팅하고 있을 때.

이러한 프로세스의 수는 포그라운드 프로세스보다 덜 제한적이지만 여전히 상대적으로 제어된다. 이들은 매우 중요한 것으로 간주되며, 모든 포그라운드 프로세스를 유지하기 위해 필요한 경우가 아니면 종료되지 않는다.

3. 서비스 프로세스 (Service Process)

서비스 프로세스는 startService() 메서드로 시작된 Service를 보유한 프로세스이다. 비록 사용자에게 직접 보이지는 않지만, 백그라운드 데이터 업로드나 다운로드와 같이 사용자가 중요하게 여기는 작업을 수행한다. 시스템은 포그라운드 및 가시적 프로세스를 유지할 메모리가 부족하지 않는 한 항상 이러한 프로세스를 실행 상태로 유지한다.

  • 30분 이상 오래 실행된 서비스는 중요도가 강등되어 캐시된 목록(Cached list)으로 떨어질 수 있다.
  • 장기간 실행이 필요한 프로세스는 setForeground를 통해 생성할 수 있다. 엄격한 실행 시간이 필요한 주기적 작업은 AlarmManager를 통해 예약할 수 있다. 이는 메모리 누수 등으로 과도한 리소스를 사용하는 장기 실행 서비스가 사용자 경험을 해치는 상황을 방지하는 데 도움이 된다.

4. 캐시된 프로세스 (Cached Process)

캐시된 프로세스는 현재 필요하지 않은 프로세스이므로, 시스템은 다른 곳에 메모리 등의 리소스가 필요할 때 이를 자유롭게 종료할 수 있다. 정상적인 시스템에서 리소스 관리의 대상이 되는 것은 오직 이 프로세스들뿐이다. 잘 작동하는 시스템은 효율적인 앱 전환을 위해 항상 여러 개의 캐시된 프로세스를 유지하며, 필요에 따라 정기적으로 이들을 종료한다. 시스템이 모든 캐시된 프로세스를 종료하고 서비스 프로세스까지 종료해야 하는 상황은 매우 비정상적인 상황뿐이다.

  • 캐시된 프로세스는 언제든지 종료될 수 있으므로, 앱은 캐시 상태에 있을 때 모든 작업을 중단해야 한다.
    사용자에게 중요한 작업을 수행해야 한다면 위에 언급된 API를 사용하여 활성 프로세스 상태에서 작업을 실행해야 한다.
  • 캐시된 프로세스는 대게 사용자에게 보이지 않는 하나 이상의 Activity 인스턴스를 보유한다
    (=onStop() 메서드가 호출되고 반환된 상태)
    액티비티 생명주기를 올바르게 구현했다면, 시스템이 프로세스를 종료하더라도 사용자가 앱으로 돌아올 때 경험에 영향을 주지 않는다. 새로운 프로세스에서 액티비티가 재생성될 때 이전에 저장된 상태를 복구할 수 있기 때문이다. 단, 시스템에 의해 프로세스가 종료되는 경우 onDestroy() 호출은 보장되지 않음에 유의해야 한다.

프로세스의 분류 방식을 결정할 때, 시스템은 프로세스 내에서 현재 활성화된 모든 구성 요소 중 가장 중요한 레벨을 기준으로 결정을 내린다. 각 구성 요소가 프로세스 및 애플리케이션의 전체 수명 주기에 어떻게 기여하는지에 대한 자세한 내용은 Activity, Service, BroadcastReceiver 문서를 참조하면 된다.

또한, 프로세스의 우선순위는 해당 프로세스가 맺고 있는 다른 의존 관계에 따라 높아질 수 있다. 예를 들어, 프로세스 A가 Context.BIND_AUTO_CREATE 플래그를 사용하여 프로세스 B의 서비스에 바인딩되었거나 프로세스 B의 ContentProvider를 사용 중이라면, 프로세스 B의 분류는 항상 최소한 프로세스 A만큼 중요하게 취급된다.


참고

Processes and app lifecycle  |  App architecture  |  Android Developers

Processes and threads overview  |  App quality  |  Android Developers

0개의 댓글