Android

액티비티 (6) : 프로세스와 앱 수명 주기

까망사과 2022. 5. 12. 20:30

안드로이드 앱은 대부분 자체 리눅스 프로세스에서 실행된다. 이 프로세스는 앱의 코드가 실행되어야 할 때 생성되고, 더 이상 필요하지 않으면서 시스템이 다른 앱을 위해 메모리를 회수해야 할 때까지 계속 실행된다.

 

앱 프로세스의 수명은 앱 자체가 아니라 시스템이 제어한다. 시스템이 실행 중인 것으로 파악하는 앱의 요소, 사용자에게 있어 요소의 중요도 및 시스템이 사용 가능한 메모리 양 등에 기반하여 결정된다.

 

앱 개발자는 다른 앱의 컴포넌트가 어떻게 자신의 앱에 영향을 미치는지 이해해야 한다. 이 컴포넌트들을 올바르게 사용하지 않으면 앱이 중요한 작업을 수행하는 도중 시스템이 앱의 프로세스를 종료할 수 있다.


프로세스 수명주기 버그의 일반적인 예로 브로드캐스트 리시버를 들 수 있다. BroadcastReceiver 클래스는 onReceive()로 인텐트를 수신하고 나서 반환될 때 스레드를 시작한다. 메서드에서 반환되면 시스템은 브로드캐스트 리시버가 더 이상 활성 상태가 아니라고 간주하므로 호스팅 프로세스가 더 이상 필요 없다(다른 컴포넌트가 그 안에서 활성 상태가 아닌 이상). 그러므로 시스템은 메모리 회수가 필요하면 언제든지 그 프로세스를 종료할 수 있다. 이때 프로세스에서 실행 중인 스레드를 종료한다. 이 문제에 대한 일반적인 해결책은 브로드캐스트 리시버에서 JobService를 예약하는 것이다. 그렇게 하면 시스템이 프로세스 안에 아직 진행 중인 작업이 있다는 사실을 알 수 있다.

 

안드로이드는 메모리가 부족할 때 실행 중인 컴포넌트와 그의 상태에 기반하여 프로세스를 중요도 계층 구조에 배치하고 그에 따라 종료할 프로세스를 결정한다. 프로세스 유형은 중요도 순서대로 다음과 같다.

  1. 포그라운드 프로세스
    사용자가 진행 중인 작업에 필요한 프로세스. 메모리가 매우 부족하여 불가피한 경우가 아닌 이상 종료되지 않는다.
    이하 조건 중 하나를 만족하면 포그라운드 프로세스로 간주된다.
    • 사용자와 상호작용 중인 액티비티(onResume()이 호출됨)를 실행 중이다.
    • 현재 실행 중인 브로드캐스트 리시버가 있다.
    • 현재 콜백(Service.onCreate(), Service.onStart, Service.onDestroy())을 실행 중인 서비스가 있다.
  2. 가시적(Visible) 프로세스
    사용자가 현재 인지하고 있는 작업을 수행하는 프로세스. 모든 포그라운드 프로세스의 실행을 유지해야 하는 경우가 아닌 이상 종료되지 않는다.
    이하 조건 중 하나를 만족하면 가시적 프로세스로 간주된다.
    • 사용자에게 보이지만 포그라운드에 있지 않은 액티비티(onPause()가 호출됨)를 실행 중이다.
    • startForeground()를 통해 포그라운드 서비스로 실행 중인 서비스가 있다. 이 메서드는 서비스를 사용자가 인지하거나 보이는 것으로 처리하도록 시스템에게 요청한다.
    • 시스템이 사용자가 인지하고 있는 특정 기능(라이브 배경화면, 입력 방법 등)을 위해 사용하고 있는 서비스를 호스팅한다.
  3. 서비스 프로세스
    startService()로 시작된 서비스에 필요한 프로세스. 사용자에게 직접 표시되지 않지만 일반적으로 사용자가 관심을 가진 작업(백그라운드 네트워크 데이터 업로드 및 다운로드 등)을 수행한다. 따라서 포그라운드 및 가시적 프로세스를 유지하기 위해 메모리 회수가 필요하지 않은 이상 계속 실행된다.

    서비스가 장시간 실행되면 프로세스의 중요도가 강등되어 캐시된 LRU(Least Recently Used) 목록에 추가될 수 있다. 이는 장시간 실행되는 서비스가 리소스를 과도하게 사용하는 것을 방지한다.
  4. 캐시드(Cached) 프로세스
    현재 필요하지 않은 프로세스. 시스템이 다른 곳에 사용할 리소스를 회수해야 할 때 언제든 종료될 수 있다. 이는 정상적으로 동작하는 시스템에서 리소스 관리에 관련된 유일한 프로세스이다. 제대로 운영되는 시스템은 항상 캐시드 프로세스를 여러 개 사용할 수 있으며 필요할 때 가장 오래된 것을 정기적으로 종료한다. 캐시드 프로세스는 오직 극단적인 상황에서만 모두 종료되며 그 다음부터 서비스 프로세스를 종료하기 시작한다.

    캐시드 프로세스는 보통 현재 사용자에게 표시되지 않는 액티비티 인스턴스를 하나 이상 가지고 있다. 액티비티 수명주기를 올바르게 구현하면 시스템이 프로세스를 종료해도 앱으로 돌아왔을 때 사용자 경험에 영향을 미치지 않는다(관련된 액티비티가 새 프로세스에서 재생성될 때 이전 상태가 복구된다).

    이러한 프로세스는 LRU 목록에서 유지된다. 일반적으로 유용한 프로세스일수록 앞쪽에 유지된다. 또한 허용되는 프로세스의 수, 프로세스가 지속적으로 캐시된 상태를 유지할 수 있는 시간 등 다른 프로세스 종료 기준도 적용될 것이다.

 

시스템은 프로세스 안에서 현재 실행 중인 가장 중요도가 높은 컴포넌트에 기반하여 프로세스 분류 방법을 결정한다.

프로세스의 우선순위는 프로세스가 가지고 있는 다른 종속성에 기반하여 증가할 수도 있다.

예를 들어 프로세스 A가 Context.BIND_AUTO_CREATE 플래그로 서비스에 바인딩되거나 프로세스 B의 컨텐츠 프로바이더를 사용한다면 프로세스 B의 우선순위는 언제나 프로세스 A 이상일 것이다.