Android 앱을 개발하다 보면 자연스럽게 Activity
나 Fragment
의 수명 주기에 집중하게 됩니다. 하지만 더 중요하면서도 종종 간과되는 것이 앱 프로세스의 수명 주기입니다. 특히 미디어 앱처럼 백그라운드에서도 지속적인 작업이 필요한 앱을 개발할 때는 프로세스 수명 주기를 이해하는 것이 필수적입니다.
이 글에서는 Android 프로세스의 수명 주기, 중요도 계층, 그리고 이것이 미디어 앱 개발에 어떤 영향을 미치는지 살펴보겠습니다.
Android 프로세스의 특징
Android에서 각 앱은 일반적으로 자체 Linux 프로세스 내에서 실행됩니다. 이 프로세스는 앱 코드를 실행해야 할 때 생성되며, 시스템이 더 많은 메모리를 확보해야 하거나 해당 프로세스가 더 이상 필요하지 않다고 판단할 때까지 유지됩니다.
Android의 가장 기본적이고 중요한 특징 중 하나는 앱 프로세스의 수명이 앱 자체에 의해 제어되지 않는다는 점입니다. 대신, Android 시스템이 다음과 같은 요소들을 고려하여 프로세스 수명을 결정합니다.
- 현재 실행 중인 앱 컴포넌트 (Activity, Service, BroadcastReceiver 등)
- 이러한 컴포넌트가 사용자에게 얼마나 중요한지
- 시스템의 전체 메모리 가용량
이러한 특성을 이해하지 못하면 앱이 예상치 못하게 종료되거나, 중요한 작업이 중단되는 상황을 맞을 수 있습니다.
프로세스 중요도 계층
Android는 메모리가 부족할 때 어떤 프로세스를 종료할지 결정하기 위해 각 프로세스를 중요도 계층으로 분류합니다. 다음은 중요도 순으로 나열한 프로세스 유형입니다.
1. 포그라운드 프로세스 (Foreground Process)
사용자가 현재 수행하고 있는 작업에 필수적인 프로세스입니다. 다음 조건 중 하나라도 충족되면 포그라운드로 간주됩니다.
- 사용자가 현재 상호작용하는 Activity가 실행 중 (
onResume()
상태) - 현재 실행 중인
BroadcastReceiver
가 있음 (onReceive()
메서드 실행 중) - 콜백 내에서 코드를 실행 중인
Service
가 있음 (onCreate()
,onStart()
,onDestroy()
등)
시스템에는 보통 소수의 포그라운드 프로세스만 존재하며, 메모리가 극도로 부족한 상황에서만 최후의 수단으로 종료됩니다.
2. 가시적 프로세스 (Visible Process)
사용자가 현재 인식하고 있는 작업을 수행하는 프로세스로, 종료되면 사용자 경험에 즉각적인 부정적 영향을 미칩니다.
- 화면에 표시되지만 포그라운드에 있지 않은 Activity (
onPause()
상태) startForeground()
로 실행되는 포그라운드 Service- 사용자가 인식하는 특정 기능을 수행하는 시스템 서비스 (라이브 배경화면, 입력 메서드 등)
3. 서비스 프로세스 (Service Process)
startService()
메서드로 시작된 Service를 포함하는 프로세스입니다. 이러한 프로세스는 직접 사용자에게 보이지는 않지만, 사용자가 관심을 갖는 작업(음악 재생, 네트워크 데이터 다운로드 등)을 수행합니다.
시스템은 가능한 한 이러한 프로세스를 유지하지만, 포그라운드와 가시적 프로세스를 위한 메모리가 필요할 경우 종료될 수 있습니다. 또한, 30분 이상 실행된 서비스는 중요도가 낮아져 캐시된 프로세스로 간주될 수 있습니다.
4. 캐시된 프로세스 (Cached Process)
현재 필요하지 않은 프로세스로, 시스템이 리소스를 회수하기 위해 언제든지 종료할 수 있습니다. 일반적으로 현재 보이지 않는 Activity(onStop()
호출 후)를 포함하고 있습니다.
정상적인 시스템에서는 메모리 관리에 관련된 유일한 프로세스 유형이며, 시스템은 효율적인 앱 전환을 위해 여러 캐시된 프로세스를 유지합니다.
프로세스 수명 주기 관리의 실제적 예
실제 개발 상황에서 프로세스 수명 주기 관리가 얼마나 중요한지 살펴보겠습니다.
위 시퀀스 다이어그램은 미디어 앱에서 사용자가 앱을 나가도 음악이 계속 재생되는 일반적인 시나리오를 보여줍니다. 포그라운드 서비스를 사용하면 앱의 UI 관련 프로세스가 종료되더라도 음악 재생은 계속 유지될 수 있습니다.
흔한 실수와 해결책
BroadcastReceiver에서 비동기 작업 시작
// 잘못된 구현
class MyReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Thread {
// 장시간 작업 수행
}.start()
// onReceive() 반환 후 프로세스가 종료될 수 있음
}
}
// 올바른 구현
class MyReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val scheduler = context.getSystemService(JobScheduler::class.java)
val jobInfo = JobInfo.Builder(JOB_ID,
ComponentName(context, MyJobService::class.java))
.build()
scheduler.schedule(jobInfo)
}
companion object {
private const val JOB_ID = 1000
}
}
결론
Android 프로세스 수명 주기를 이해하고 적절히 관리하는 것은 모든 앱, 특히 미디어 앱 개발에서 핵심적인 요소입니다. 시스템 자원이 부족할 때도 사용자 경험을 유지하는 것은 개발자의 책임이며, 이를 위해서는 Android 시스템이 프로세스를 관리하는 방식을 철저히 이해해야 할 것 같습니다.