|

안드로이드 액티비티 생명주기 기초 정리

안드로이드 액티비티 생명주기 기초 정리 대표 이미지
안드로이드 액티비티 생명주기 핵심 흐름 요약

안드로이드 앱을 처음 만들면 가장 자주 듣는 말 중 하나가 생명주기입니다. 그런데 많은 입문자가 onCreate(), onStart(), onResume() 같은 이름은 외우지만, 정작 언제 무엇을 해야 하는지는 헷갈립니다. 이 글은 액티비티가 어떤 상태로 바뀌는지, 그리고 각 단계에서 무엇을 해야 하는지를 초보자 기준으로 차근차근 정리합니다.


액티비티 생명주기를 왜 먼저 이해해야 할까

안드로이드의 액티비티는 사용자가 보고 상호작용하는 화면 단위입니다. 이 화면은 항상 같은 상태로 머무르지 않고, 생성되고, 보이기 시작하고, 포커스를 잃고, 가려지고, 종료될 수 있습니다. 생명주기를 이해한다는 것은 단순히 메서드 순서를 외우는 것이 아니라 상태 변화에 맞춰 코드를 어디에 둘지 아는 것입니다.


액티비티 생명주기 전체 흐름 한 번에 보기

공식 문서 기준으로 핵심 콜백은 onCreate(), onStart(), onResume(), onPause(), onStop(), onDestroy() 여섯 가지입니다. 다시 돌아올 때는 onRestart()가 등장할 수 있습니다.

안드로이드 액티비티 생명주기 흐름도
액티비티 생성, 표시, 상호작용, 일시 중단, 정지, 종료 흐름 요약

크게 나누면 생성 구간, 보이는 구간, 실제로 사용 중인 구간, 정리 구간으로 나눠 이해하면 훨씬 쉽습니다.


onCreate, onStart, onResume의 역할 구분하기

onCreate: 화면을 처음 만들 때

onCreate()는 액티비티가 처음 생성될 때 호출됩니다. 보통 레이아웃 연결, View 초기화, 기본 데이터 준비, 이전 저장 상태 복원 시작 같은 작업을 둡니다. 초보자 입장에서는 화면의 기본 뼈대를 세우는 곳이라고 이해하면 좋습니다.

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        Log.d("Lifecycle", "onCreate")
    }
}

onStart: 사용자에게 보이기 시작할 때

onStart()는 액티비티가 화면에 보이기 시작할 때 호출됩니다. 이 시점은 사용자가 상호작용하기 직전일 수 있지만 화면은 이미 보이는 상태입니다. 공식 레퍼런스에서는 onStart()부터 onStop()까지를 visible lifetime으로 설명합니다.

override fun onStart() {
    super.onStart()
    Log.d("Lifecycle", "onStart")
}

onResume: 실제로 상호작용하는 상태

onResume()은 액티비티가 포그라운드에 올라와 사용자 입력을 직접 받기 시작할 때 호출됩니다. 공식 레퍼런스에서는 onResume()부터 onPause()까지를 foreground lifetime으로 설명합니다. 즉, 단순히 보이는 상태가 아니라 실제로 사용 중인 상태입니다.

override fun onResume() {
    super.onResume()
    Log.d("Lifecycle", "onResume")
}

onPause, onStop, onDestroy는 언제 호출될까

onPause: 포커스를 잃을 때

onPause()는 액티비티가 더 이상 전면의 활성 상태가 아닐 때 호출됩니다. 이 시점의 액티비티는 아직 일부 보일 수 있습니다. 따라서 포커스를 잃는 시점으로 이해하는 것이 좋습니다.

override fun onPause() {
    super.onPause()
    Log.d("Lifecycle", "onPause")
}

onStop: 화면에서 더 이상 보이지 않을 때

onStop()은 액티비티가 더 이상 사용자에게 보이지 않을 때 호출됩니다. 즉, onPause()보다 한 단계 더 뒤로 밀린 상태입니다. onPause는 포커스를 잃는 단계, onStop은 화면에서 사라지는 단계로 구분하면 이해가 쉬워집니다.

override fun onStop() {
    super.onStop()
    Log.d("Lifecycle", "onStop")
}

onDestroy: 액티비티가 정리될 때

onDestroy()는 액티비티가 종료되며 정리되는 마지막 단계에서 호출될 수 있습니다. 다만 시스템이 백그라운드 앱 프로세스를 종료하는 경우에는 onDestroy() 호출이 보장되지 않습니다. 그래서 모든 정리 코드를 여기에만 몰아넣는 습관은 위험할 수 있습니다.

override fun onDestroy() {
    super.onDestroy()
    Log.d("Lifecycle", "onDestroy")
}

onRestart는 어디에서 등장할까

onRestart()는 액티비티가 완전히 새로 생성되는 것이 아니라, 기존 인스턴스가 다시 전면으로 돌아올 때 등장할 수 있습니다. 보통은 onStop() → onRestart() → onStart() → onResume() 흐름으로 이해하면 됩니다.


상황별로 호출 흐름을 보면 더 잘 이해된다

  • 앱을 처음 실행할 때: onCreate() → onStart() → onResume()
  • 다른 액티비티나 다이얼로그가 일부 가릴 때: 보통 onPause()
  • 다른 화면이 완전히 덮을 때: 보통 onPause() → onStop()
  • 같은 인스턴스로 다시 돌아올 때: onRestart() → onStart() → onResume()
  • 뒤로 가기로 현재 화면을 닫을 때: 보통 onPause() → onStop() → onDestroy()

공식 문서 기준으로 사용자가 Back을 눌러 현재 액티비티를 종료하는 경우에는 기본적으로 onSaveInstanceState()가 호출되지 않는다고 설명할 수 있습니다.


savedInstanceState로 상태를 저장하는 기본 원리

입문자가 생명주기에서 가장 자주 부딪히는 문제는 화면 회전 시 데이터가 사라지는 현상입니다. 이때 기본적으로 알아야 하는 것이 onSaveInstanceState()입니다. 이 메서드는 액티비티가 일시적으로 재생성될 가능성이 있을 때 간단한 UI 상태를 저장하는 데 쓰입니다.

class MainActivity : AppCompatActivity() {

    private lateinit var textView: TextView
    private var message: String = "초기값"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        textView = findViewById(R.id.textView)

        if (savedInstanceState != null) {
            message = savedInstanceState.getString("message", "초기값")
        }

        textView.text = message
    }

    override fun onSaveInstanceState(outState: Bundle) {
        outState.putString("message", message)
        super.onSaveInstanceState(outState)
    }
}

핵심은 저장은 onSaveInstanceState()에서 하고, 복원은 onCreate() 또는 onRestoreInstanceState()에서 한다는 점입니다. 공식 문서에서는 UI 상태 보존을 위해 ViewModel, onSaveInstanceState(), 영속 저장소를 조합해서 보라고 설명합니다.


생명주기 로그를 직접 찍어보면 이해가 빨라진다

이 개념은 눈으로만 읽는 것보다 직접 로그를 찍어보면 훨씬 빨리 익숙해집니다. 아래는 가장 단순한 예제입니다.

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.d("Lifecycle", "onCreate")
    }

    override fun onStart() {
        super.onStart()
        Log.d("Lifecycle", "onStart")
    }

    override fun onResume() {
        super.onResume()
        Log.d("Lifecycle", "onResume")
    }

    override fun onPause() {
        super.onPause()
        Log.d("Lifecycle", "onPause")
    }

    override fun onStop() {
        super.onStop()
        Log.d("Lifecycle", "onStop")
    }

    override fun onRestart() {
        super.onRestart()
        Log.d("Lifecycle", "onRestart")
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d("Lifecycle", "onDestroy")
    }
}
  • 앱 처음 실행
  • 홈 버튼 누르기
  • 최근 앱 화면으로 이동했다가 복귀하기
  • 화면 회전하기
  • 뒤로 가기로 종료하기

위 상황을 직접 시험해보면 콜백이 왜 분리되어 있는지 감이 훨씬 빨리 잡힙니다.


초보자가 자주 하는 실수

  1. onPause와 onStop을 같은 의미로 생각하기
  2. 모든 정리를 onDestroy에만 몰아넣기
  3. 화면 회전 시 왜 값이 사라지는지 모른 채 넘어가기
  4. 콜백 이름만 외우고 상태 개념은 이해하지 않기

중요한 것은 메서드 이름보다 현재 액티비티가 생성 중인지, 보이는지, 실제로 사용 중인지, 가려졌는지, 종료되는지를 구분하는 것입니다. 상태를 이해하면 메서드는 자연스럽게 따라옵니다.


마무리

안드로이드 액티비티 생명주기는 외워야 하는 규칙표가 아니라 화면 상태가 바뀔 때 코드를 어디에 둘지 알려주는 설계 지도에 가깝습니다. 이번 글에서 꼭 기억하면 좋은 핵심은 세 가지입니다. onPause와 onStop은 다르고, onDestroy는 항상 보장되지 않으며, 화면 회전 같은 재생성에는 onSaveInstanceState()를 이해해야 한다는 점입니다.

함께보면 좋은 글