프로세스 및 앱 수명 주기

대부분의 경우 모든 Android 애플리케이션은 자체 Linux 프로세스에서 실행됩니다. 이 프로세스는 일부 코드를 실행해야 할 때 애플리케이션을 위해 생성되며 시스템이 다른 애플리케이션에서 사용할 메모리를 회수해야 하고 더 이상 필요하지 않을 때까지 실행 상태로 유지됩니다.

Android의 이례적이고 기본적인 기능은 애플리케이션 프로세스의 전체 기간이 애플리케이션 자체에 의해 직접 제어되지 않는 것입니다. 대신 시스템에서 실행 중임을 알고 있는 애플리케이션 부분, 이러한 항목이 사용자에게 중요한 정도 및 시스템에서 사용할 수 있는 전체 메모리 양을 조합하여 시스템에 의해 결정됩니다.

애플리케이션 개발자는 다양한 애플리케이션 구성요소(특히 Activity, Service, BroadcastReceiver)가 애플리케이션 프로세스의 전체 기간에 미치는 영향을 이해하는 것이 중요합니다. 이러한 구성요소를 올바르게 사용하지 않으면 시스템에서 중요한 작업을 실행하는 동안 애플리케이션의 프로세스가 종료될 수 있습니다.

프로세스 수명 주기 버그의 일반적인 예는 BroadcastReceiver.onReceive() 메서드에서 Intent를 수신한 후 함수에서 반환될 때 스레드를 시작하는 BroadcastReceiver입니다. 반환된 후에는 시스템은 BroadcastReceiver가 더 이상 활성 상태가 아닌 것으로 간주하며, 다른 애플리케이션 구성요소가 활성 상태가 아닌 한 호스팅 프로세스가 더 이상 필요하지 않은 것으로 간주합니다.

따라서 시스템은 언제든지 프로세스를 종료하여 메모리를 회수할 수 있으며 이 과정에서 프로세스에서 실행 중인 생성된 스레드를 종료합니다. 이 문제를 해결하는 방법은 일반적으로 프로세스에 진행 중인 작업이 있음을 시스템이 알 수 있도록 BroadcastReceiver에서 JobService를 예약하는 것입니다.

메모리가 부족할 때 종료할 프로세스를 결정하기 위해 Android는 프로세스에서 실행되는 구성요소와 구성요소의 상태에 따라 각 프로세스를 중요도 계층 구조에 배치합니다. 중요도 순으로 이러한 프로세스 유형은 다음과 같습니다.

  1. 포그라운드 프로세스는 사용자가 현재 하고 있는 작업에 필요한 프로세스입니다. 다양한 애플리케이션 구성요소로 인해 포함된 프로세스가 다른 방식으로 포그라운드로 간주될 수 있습니다. 다음 조건 중 하나라도 해당하면 프로세스가 포그라운드에 있는 것으로 간주됩니다.
  2. 시스템에는 이러한 프로세스가 몇 개뿐이며, 이러한 프로세스도 계속 실행될 수 없을 정도로 메모리가 부족할 때만 최후의 수단으로서만 종료됩니다. 일반적으로 이런 일이 발생하면 기기가 메모리 페이징 상태에 도달한 것이므로 이 작업은 사용자 인터페이스의 반응성을 유지해야 합니다.

  3. 가시적 프로세스는 사용자가 현재 알고 있는 작업을 실행하는 중이므로 이 프로세스를 종료하면 사용자 환경에 눈에 띄게 부정적인 영향을 미칩니다. 프로세스는 다음 조건에서 가시적인 것으로 간주됩니다.
    • 앱이 화면에서 사용자에게 표시되지만 포그라운드에는 표시되지 않는 Activity를 실행 중입니다 (onPause() 메서드가 호출됨). 예를 들어 포그라운드 Activity가 그 뒤에 이전 Activity를 볼 수 있는 대화상자로 표시되는 경우 이러한 상황이 발생할 수 있습니다.
    • 이 인터페이스에는 Service.startForeground()를 통해 포그라운드 서비스로 실행되는 Service가 있습니다. 이 서비스는 서비스를 사용자가 알고 있는 것으로 또는 기본적으로 표시된 것처럼 처리하도록 시스템에 요청합니다.
    • 시스템에서 사용자가 알고 있는 특정 기능(예: 라이브 배경화면 또는 입력 방법 서비스)에 사용하는 서비스를 호스팅하고 있습니다.

    시스템에서 실행 중인 이러한 프로세스의 수는 포그라운드 프로세스보다 덜 제한적이지만 그럼에도 비교적 제어됩니다. 이러한 프로세스는 매우 중요한 것으로 간주되며 모든 포그라운드 프로세스를 계속 실행하는 데 필요한 경우가 아니라면 종료되지 않습니다.

  4. 서비스 프로세스startService() 메서드로 시작된 Service를 보유하는 프로세스입니다. 이러한 프로세스는 사용자에게 직접 표시되지는 않지만 일반적으로 사용자가 관심을 갖는 작업 (예: 백그라운드 네트워크 데이터 업로드 또는 다운로드)을 실행하므로 시스템은 모든 포그라운드 프로세스와 표시되는 프로세스를 유지하기에 충분한 메모리가 없는 한 이러한 프로세스를 항상 실행 상태로 유지합니다.

    오랫동안 (예: 30분 이상) 실행된 서비스는 중요도가 낮아져 프로세스가 캐시된 목록으로 삭제될 수 있습니다.

    장기간 실행해야 하는 프로세스는 setForeground로 만들 수 있습니다. 엄격한 실행 시간이 필요한 주기적 프로세스인 경우 AlarmManager를 통해 예약할 수 있습니다. 자세한 내용은 장기 실행 작업자 지원을 참조하세요. 이렇게 하면 메모리 누수와 같이 과도한 리소스를 사용하는 장기 실행 서비스로 인해 시스템에서 우수한 사용자 환경을 제공하지 못하는 상황을 방지할 수 있습니다.

  5. 캐시된 프로세스는 현재 필요하지 않은 프로세스이므로, 시스템은 메모리와 같은 리소스가 다른 곳에서 필요할 때 필요에 따라 자유롭게 이 프로세스를 종료할 수 있습니다. 정상적으로 작동하는 시스템에서는 리소스 관리와 관련된 유일한 프로세스입니다.

    잘 실행되는 시스템에서는 효율적으로 애플리케이션 간에 전환할 수 있도록 여러 개의 캐시된 프로세스를 항상 사용할 수 있으며 필요에 따라 캐시된 앱을 정기적으로 종료합니다. 매우 중요한 상황에서만 시스템이 캐시된 프로세스가 모두 종료되고 서비스 프로세스 종료를 시작해야 하는 지점에 이르게 됩니다.

    캐시된 프로세스는 시스템에 의해 언제든지 종료될 수 있으므로 앱은 캐시된 상태에 있는 동안 모든 작업을 중지해야 합니다. 앱에서 사용자에게 중요한 작업을 실행해야 하는 경우 위의 API를 사용하여 활성 프로세스 상태에서 작업을 실행해야 합니다.

    캐시된 프로세스는 현재 사용자에게 표시되지 않는 하나 이상의 Activity 인스턴스를 보유하는 경우가 많습니다 (onStop() 메서드가 호출되어 반환됨). 시스템이 이러한 프로세스를 종료할 때 Activity 수명 주기를 올바르게 구현한다면 앱으로 돌아갈 때 사용자 환경에 영향을 미치지 않습니다. 연결된 활동이 새 프로세스에서 다시 생성될 때 이전에 저장된 상태를 복원할 수 있습니다. 시스템에서 프로세스가 종료되는 경우 onDestroy()의 호출이 보장되지 않는다는 점에 유의하세요. 자세한 내용은 Activity를 참고하세요.

    Android 13부터 앱 프로세스는 위의 활성 수명 주기 상태 중 하나가 될 때까지 실행 시간이 제한되거나 배제될 수 있습니다.

    캐시된 프로세스는 목록에 보관됩니다. 이 목록의 정확한 순서 지정 정책은 플랫폼의 구현 세부정보입니다. 일반적으로 다른 유형의 프로세스보다 먼저 사용자의 홈 애플리케이션을 호스팅하는 프로세스나 사용자가 본 마지막 활동을 호스팅하는 프로세스와 같이 더 유용한 프로세스를 유지하려고 합니다. 허용되는 프로세스 수에 엄격한 제한을 설정하거나 프로세스가 계속 캐시된 상태로 유지될 수 있는 시간을 제한하는 등 프로세스를 종료하는 다른 정책도 적용할 수 있습니다.

프로세스를 분류하는 방법을 결정할 때 시스템은 프로세스에서 현재 활성 상태인 모든 구성요소 중에서 가장 중요한 수준을 기준으로 결정을 내립니다. 이러한 각 구성요소가 프로세스 및 애플리케이션의 전체 수명 주기에 기여하는 방식을 자세히 알아보려면 Activity, Service, BroadcastReceiver 문서를 참고하세요.

프로세스의 우선순위는 프로세스가 가지고 있는 다른 종속 항목에 따라 증가할 수도 있습니다. 예를 들어 프로세스 A가 Context.BIND_AUTO_CREATE 플래그를 사용하여 Service에 바인딩되었거나 프로세스 B에서 ContentProvider를 사용하고 있다면 프로세스 B의 분류는 항상 프로세스 A의 분류만큼 중요합니다.