Activity Lifecycle은 Android가 Activity의 상태 전환(생성, 가시성, 입력 포커스, 부분적 가시성 손실, 완전한 숨김, 소멸)에 따라 호출하는 콜백 메서드 집합입니다. 시스템은 onCreate()가 호출되는 순간부터 onDestroy()까지 각 애플리케이션 화면의 수명 주기를 관리합니다. 이러한 상태를 이해하는 것은 Android 애플리케이션의 안정적인 작동을 위한 필수 요구 사항이며, 메서드 간 전환을 잘못 처리하면 메모리 누수, 사용자 데이터 손실 및 예기치 않은 충돌이 발생합니다. Android 아키텍처에 대한 자세한 내용은 Android에 대한 일반 문서를 참조하세요.
핵심 요점
Activity Lifecycle(Activity 수명 주기)은 Android 애플리케이션의 각 화면이 생성부터 완전한 소멸까지 거치는 상태 기계입니다. Android 시스템은 사용자 작업(애플리케이션 열기, 최소화, 화면 회전, 수신 전화 응답, 애플리케이션 간 전환 및 종료)에 따라 이 프로세스를 관리합니다.
수명 주기를 이해하는 것은 모든 Android 개발자에게 필수적입니다. 시스템은 메모리가 부족할 때 언제든지 Activity를 소멸시킬 수 있으며, 애플리케이션은 상태를 올바르게 복원해야 하기 때문입니다. Google Android Vitals(2025)에 따르면 onSaveInstanceState()에서 상태 저장을 처리하지 않는 애플리케이션은 Activity 재생성 시 42% 더 많은 충돌을 보입니다.
수명 주기에는 6가지 주요 콜백 메서드가 포함됩니다: onCreate(), onStart(), onResume(), onPause(), onStop(), onDestroy(). 또한 Activity가 중지 상태에서 돌아올 때 onStart() 전에 호출되는 onRestart() 메서드가 있습니다. 각 메서드에는 엄격하게 정의된 목적과 실행 시간이 있으며, 시스템은 이를 순차적으로 호출하고 개발자는 자신의 로직을 구현하기 위해任意의 메서드를 재정의할 수 있습니다.
주기는 세 가지 주요 단계로 나눌 수 있습니다: 전체 수명(onCreate → onDestroy), 가시 수명(onStart → onStop), 포그라운드 수명(onResume → onPause). 이 세 가지 수준을 이해하면 초기화 및 리소스 해제 코드를 올바르게 분배하는 데 도움이 됩니다.
각 수명 주기 메서드는 엄격하게 정의된 작업을 수행합니다. 시스템은 이를 고정된 순서로 호출하며, 개발자는 특정 로직에 필요한 메서드만 재정의해야 합니다. 수명 주기 메서드를 직접 호출하는 것은 권장되지 않습니다. 이는 Android Runtime이 처리합니다.
애플리케이션 시작 시 일반적인 순서: onCreate → onStart → onResume. 뒤로 가기 버튼을 누를 때: onPause → onStop → onDestroy. 최소화할 때: onPause → onStop, 그런 다음 복귀 시: onRestart → onStart → onResume.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun onStart() {
super.onStart()
}
override fun onResume() {
super.onResume()
}
override fun onPause() {
super.onPause()
}
override fun onStop() {
super.onStop()
}
override fun onDestroy() {
super.onDestroy()
}
override fun onRestart() {
super.onRestart()
}
}
재정의된 각 메서드는 super 버전을 호출해야 합니다. 그렇지 않으면 시스템이 상태 전환을 올바르게 완료할 수 없습니다. 이 규칙은 Android Developers 문서에 명시되어 있으며 Android Studio의 lint 규칙에 의해 확인됩니다.
첫 번째 수준 — 전체 수명: onCreate와 onDestroy 사이의 간격입니다. 여기서 일회성 초기화와 전역 리소스의 최종 해제가 수행됩니다. 두 번째 수준 — 가시 수명: onStart와 onStop 사이입니다. Activity가 화면에 표시되지만 다른 창에 의해 부분적으로 가려질 수 있습니다. 세 번째 수준 — 포그라운드 수명: onResume과 onPause 사이입니다. Activity가 작업 스택의 최상위에 있으며 사용자와 상호 작용합니다.
onCreate() — Activity 수명 주기의 첫 번째이자 유일한 필수 메서드. 시스템이 Activity 인스턴스를 생성할 때 한 번 호출합니다. 이 메서드는 savedInstanceState: Bundle? 매개변수를 받으며, Activity가 소멸 후 재생성되는 경우(예: 화면 회전 시) 이전에 저장된 상태가 포함됩니다.
onCreate 내에서는 다음 작업이 수행됩니다: setContentView()를 통한 레이아웃 리소스를 사용한 사용자 인터페이스 초기화, findViewById()를 통한 View 요소 바인딩, RecyclerView 및 ViewPager 어댑터 설정, savedInstanceState에서 상태 복원, ViewModel 및 LiveData 초기화, 클릭 및 제스처 리스너 설정. 메서드는 가능한 한 빨리 완료되어야 합니다. 여기서 장기 작업은 첫 번째 프레임의 렌더링을 차단하여 애플리케이션 시작 시간을 증가시킵니다.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_profile)
val userNameText: TextView = findViewById(R.id.user_name)
val loadButton: Button = findViewById(R.id.load_button)
if (savedInstanceState != null) {
userNameText.text = savedInstanceState.getString("user_name")
}
loadButton.setOnClickListener {
loadUserProfile()
}
}
Activity가 처음 생성되는 경우 savedInstanceState는 null입니다. 화면 회전 후 재생성 시 Bundle에는 onSaveInstanceState()에 저장된 데이터가 포함됩니다. null 검사는 사용자가 입력한 데이터를 잃지 않고 UI를 올바르게 복원하기 위한 표준 관행입니다.
onStart()는 Activity가 사용자에게 표시될 때 onCreate() 직후 또는 onRestart() 후에 호출됩니다. 이 상태에서 Activity는 아직 포그라운드에 있지 않으며 사용자와 상호 작용할 수 없지만, 사용자 인터페이스는 이미 화면에 표시됩니다. 예를 들어, 애플리케이션을 시작할 때 시스템은 onStart와 onResume 호출 사이에 인터페이스의 첫 번째 프레임을 렌더링합니다.
onStart 메서드에서는 일반적으로 다음 작업이 수행됩니다: Activity가 표시되는 동안 실행되어야 하는 애니메이션 시작, BroadcastReceiver 바인딩, 위치 서비스 및 센서에 연결, ViewModel 또는 Room에서 데이터 업데이트. 애플리케이션이 프로세스 내에서 클라이언트-서버 아키텍처를 사용하는 경우 bindService()를 통한 Bound 서비스 바인딩도 여기서 수행됩니다.
override fun onStart() {
super.onStart()
val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
5000L,
10f,
locationListener
)
}
override fun onStop() {
super.onStop()
val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
locationManager.removeUpdates(locationListener)
}
중요한 규칙: onStart에서 연결된 리소스는 onStop에서 해제되어야 합니다. 이렇게 하면 Activity가 화면에 표시되지 않을 때 배터리 및 시스템 리소스를 소비하지 않습니다. Google Play Store는 업데이트 심사 시 LocationListener 및 기타 시스템 서비스 누출 여부를 확인합니다.
onResume() — Activity가 포그라운드에 있으며 사용자와 상호 작용할 준비가 된 상태. 이것이 화면의 작동 상태입니다. 시스템이 입력 포커스를 Activity로 전송하고 모든 터치 이벤트, 키보드 입력 및 제스처가 이 화면으로 전달됩니다. onResume 메서드는 Activity가 포그라운드로 돌아올 때마다(다른 Activity가 종료된 후, 대화 상자가 닫힌 후, 기기 잠금이 해제된 후) 호출됩니다.
onResume에서는 다음이 수행됩니다: onPause에서 일시 중지된 애니메이션 재개, 카메라 및 기타 독점 리소스 열기, 센서 리스너(가속도계, 자이로스코프) 등록, UI용 타이머 및 스톱워치 시작, 최신 데이터로 화면 콘텐츠 업데이트. onResume/onPause 쌍은 포커스가 있을 때만 활성화되어야 하는 리소스(예: 연속 음성 인식 또는 비디오 캡처)에 사용됩니다.
override fun onResume() {
super.onResume()
cameraHolder.openCamera()
animator.resume()
sensorManager.registerListener(
stepCounter,
sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER),
SensorManager.SENSOR_DELAY_NORMAL
)
}
override fun onPause() {
super.onPause()
cameraHolder.closeCamera()
animator.pause()
sensorManager.unregisterListener(stepCounter)
}
onStart와 onResume의 차이는 중요합니다. Activity가 표시(onStart)될 수는 있지만 활성(onResume) 상태가 아닐 수 있습니다. 예를 들어, 팝업 대화 상자나 투명 잠금 화면이 위에 표시되는 경우입니다. 독점 액세스가 필요한 독점 리소스는 onStart가 아닌 onResume에서 열어야 합니다.
onPause()는 Activity가 입력 포커스를 잃었지만 부분적으로 표시된 상태로 남아 있을 때 호출됩니다. 일반적인 시나리오: 대화 상자 열기, 최근 앱 버튼 누르기, 수신 전화, 홈 버튼 누르기(이 경우 onPause 다음에 onStop이 옴). onPause 메서드는 사용자가 잃어서는 안 되는 데이터를 저장하는 마지막 신뢰할 수 있는 위치입니다.
onPause에서는 다음이 수행됩니다: 이메일 초안 및 입력 양식을 Room 또는 SharedPreferences에 저장, 애니메이션 및 비디오 재생 중지, 카메라 닫기 및 독점 리소스 해제, 백그라운드에서 중요하지 않은 고비용 작업 취소. onPause 메서드는 100밀리초 미만으로 완료되어야 합니다. 시스템은 onPause가 제어권을 반환할 때까지 다음 Activity로의 전환을 차단하며, 제한을 초과하면 ANR(Application Not Responding)이 발생합니다.
override fun onPause() {
super.onPause()
val editor = SharedPreferences.Manager ...
editor.putString("draft_text", draftEditText.text.toString())
editor.apply()
videoView.pause()
cameraHolder.release()
}
중요: onPause는 UI 스레드에서 실행되므로 Room을 통한 동기 쿼리로 데이터베이스에 쓰는 등의 차단 작업은 비동기(코루틴)로 대체하거나 백그라운드 스레드에서 실행해야 합니다. SharedPreferences에는 commit() 대신 apply()를 사용하세요. apply는 비동기적으로 데이터를 쓰며 UI 스레드를 차단하지 않습니다.
onStop()는 Activity가 사용자에게 더 이상 표시되지 않을 때 호출됩니다. 이는 다음 경우에 발생합니다: Activity가 다른 Activity에 완전히 가려짐, 사용자가 홈 버튼을 누르거나 다른 애플리케이션으로 전환, Activity가 종료 중임(이후 onDestroy 호출). onStop 상태에서 Activity는 메모리에 남아 있으며 모든 필드를 유지합니다. 소멸되지 않았지만 활성 상태도 아닙니다.
onStop에서는 다음이 수행됩니다: onStart에서 등록된 BroadcastReceiver 등록 해제, Bound 서비스 연결 해제, LocationListener, SensorListener 및 기타 시스템 리스너 해제, 애플리케이션이 숨겨져 있을 때 필요하지 않은 장기 백그라운드 작업 중지, onPause에서 수행되지 않은 경우 onSaveInstanceState()를 통해 Bundle에 현재 UI 상태 쓰기.
override fun onStop() {
super.onStop()
unregisterReceiver(connectivityReceiver)
unbindService(serviceConnection)
if (isChangingConfigurations()) {
Log.d("Lifecycle", "구성으로 인해 Activity가 재생성됩니다")
}
}
시스템은 메모리 부족 시 onDestroy를 호출하지 않고 onStop 상태의 Activity를 소멸시킬 수 있습니다. 따라서 모든 중요한 데이터는 onStop으로 전환되기 전에 저장되어야 합니다. isChangingConfigurations() 플래그는 onStop 호출이 화면 회전과 관련이 있는지 여부를 확인하는 데 사용됩니다. 이 경우 Activity는 종료되지 않고 재생성됩니다.
onDestroy() — Activity가 완전히 소멸되기 전에 호출되는 마지막 수명 주기 메서드. 시스템은 두 가지 경우에 onDestroy를 호출합니다: Activity가 finish()로 종료되거나 사용자가 뒤로 가기 버튼을 누름, Activity가 구성 변경(예: 화면 회전)으로 인해 시스템에 의해 소멸되고 다시 생성됨. onDestroy 메서드는 최종 리소스 정리를 수행할 수 있습니다: 스레드 및 코루틴 바인딩 해제, 영구적으로 열린 커서 및 소켓 닫기, NDK를 통한 네이티브 메모리 해제.
override fun onDestroy() {
super.onDestroy()
backgroundJob.cancel()
dbHelper.close()
if (isFinishing) {
Log.d("Lifecycle", "Activity가 영구적으로 종료됩니다")
} else {
Log.d("Lifecycle", "Activity가 재생성됩니다")
}
}
중요 참고: 애플리케이션 프로세스가 시스템에 의해 종료되는 경우(out-of-memory kill) onDestroy 호출이 보장되지 않습니다. 따라서 데이터 저장을 onDestroy에 의존할 수 없습니다. 이 작업은 onPause 또는 onStop에서 처리됩니다. isFinishing 속성은 finish()에 의한 Activity 종료와 구성 변경에 의한 재생성을 구별하는 데 사용됩니다.
onRestart()는 Activity가 중지 상태(onStop)에서 포그라운드로 돌아올 때 onStart() 전에 호출됩니다. 이는 사용자가 최근 앱 메뉴에서 애플리케이션을 다시 열거나 하위 화면에서 뒤로 가기 버튼을 눌러 Activity로 돌아올 때 발생합니다. onRestart 메서드는 onCreate와 다른 로직을 실행할 수 있습니다. 예를 들어, Activity가 숨겨져 있는 동안 변경되었을 수 있는 데이터를 업데이트할 수 있습니다.
override fun onRestart() {
super.onRestart()
refreshDataFromNetwork()
Log.d("Lifecycle", "Activity가 스택에서 다시 시작됩니다")
}
일반적인 시나리오: 사용자가 애플리케이션을 열고 다른 작업으로 전환한 후 한 시간 후에 돌아왔습니다. onRestart에서 애플리케이션은 데이터의 최신성을 확인하고, 시간이 많이 경과한 경우 콘텐츠를 다시 로드하도록 제안할 수 있습니다. 이는 사용자 경험을 개선하고 오래된 정보가 표시될 가능성을 줄입니다.
화면 회전은 Activity 재생성의 가장 일반적인 시나리오입니다. 기본적으로 Android는 방향이 변경될 때마다 현재 Activity를 소멸시키고 새 것을 생성합니다. 상태가 저장되지 않으면 사용자는 입력한 모든 데이터를 잃게 됩니다. Android는 이를 위해 두 가지 메커니즘을 제공합니다: 직렬화 가능한 데이터를 위한 onSaveInstanceState()와 구성 변경을 생존하는 데이터를 위한 ViewModel입니다.
임시 상태를 저장하기 위해 Activity가 소멸되기 전에 onSaveInstanceState()가 호출됩니다. 저장된 데이터는 savedInstanceState 매개변수를 통해 onCreate와 onStart 후에 호출되는 onRestoreInstanceState() 메서드로 전달됩니다. Bundle에는 크기 제한(약 500KB)이 있으므로 대용량 데이터(예: 비트맵)는 ViewModel을 통해 저장됩니다.
<!-- AndroidManifest.xml — 방향 고정 -->
<activity android:name=".MainActivity"
android:configChanges="orientation|screenSize" />
android:configChanges를 통한 방향 고정은 Activity 재생성을 방지하지만, 애플리케이션이 두 방향을 모두 지원해야 하는 경우 안티패턴으로 간주됩니다. Google의 최신 권장 사항은 사용자가 UI에 입력하는 데이터에 대해 onSaveInstanceState와 함께 ViewModel을 사용하는 것입니다.
Fragment에는 Activity와 유사하지만 추가 메서드가 있는 자체 수명 주기가 있습니다: onAttach, onCreate, onCreateView, onViewCreated, onStart, onResume, onPause, onStop, onDestroyView, onDestroy, onDetach. Fragment는 항상 Activity 내에 존재하며, 그 수명 주기는 컨테이너 Activity의 수명 주기에 연결됩니다. Activity가 소멸되면 Fragment도 따라갑니다.
주요 차이점: Fragment는 구성 요소의 상태뿐만 아니라 View 계층 구조도 관리합니다. onCreateView 메서드는 Fragment의 루트 View를 반환하고 onDestroyView는 이 계층 구조를 소멸시킵니다. 이를 통해 Fragment는 화면 회전 시 Activity 재생성에서 생존할 수 있습니다. Fragment는 유지되고 View는 onCreateView에서 다시 생성됩니다.
class ProfileFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return inflater.inflate(R.layout.fragment_profile, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val avatarImage: ImageView = view.findViewById(R.id.avatar_image)
loadAvatar(avatarImage)
}
}
onCreate와 onCreateView의 차이를 이해하는 것은 매우 중요합니다. onCreate는 Fragment의 수명 동안 한 번(View가 다시 생성되는 경우에도) 호출되는 반면, onCreateView는 Fragment가 View 계층 구조를 생성하거나 재생성할 때마다 호출됩니다. 데이터 초기화는 onCreate에서 수행되고 UI 바인딩은 onViewCreated에서 수행됩니다.
LifecycleObserver — Activity나 Fragment에서 메서드를 재정의하지 않고 수명 주기 변경에 반응할 수 있는 Android Jetpack 라이브러리의 구성 요소. 각 수명 주기 메서드에서 코드를 중복하는 대신, 개발자는 @OnLifecycleEvent 어노테이션이 있는 별도의 클래스를 만들어 lifecycle.addObserver()에 전달합니다.
Jetpack은 또한 AppCompatActivity와 Fragment가 구현하는 LifecycleOwner 인터페이스를 제공합니다. LifecycleOwner를 구현하는 모든 객체는 LiveData 구독, lifecycleScope를 통한 코루틴, 수명 주기와 관련된 WorkManager를 관리할 수 있습니다. 이는 MVVM 및 Jetpack 기반의 현대적인 Android 아키텍처의 초석입니다.
class MyLocationObserver(private val context: Context) : DefaultLifecycleObserver {
override fun onStart(owner: LifecycleOwner) {
startLocationUpdates()
}
override fun onStop(owner: LifecycleOwner) {
stopLocationUpdates()
}
}
// Activity에서:
lifecycle.addObserver(MyLocationObserver(this))
DefaultLifecycleObserver를 사용하면 테스트가 간소화되고, 코드 중복이 줄어들며, 수명 주기 로직을 여러 화면에서 재사용할 수 있습니다. 이는 각 Activity에서 수동으로 onStart/onStop을 재정의하는 현대적인 대안입니다. IT Sectr이 개발한 Android 애플리케이션에서는 위치 정보, Bluetooth 스캐닝 및 분석에 LifecycleObserver를 적용합니다. 이렇게 하면 보일러플레이트 코드의 양이 30~40% 줄어듭니다.
자주 묻는 질문
super.onCreate() 또는 다른 super 수명 주기 메서드를 호출하지 않으면 시스템이 SuperNotCalledException을 던지고 애플리케이션이 충돌합니다. 이는 Android Runtime의 엄격한 요구 사항입니다. 각 메서드는 기본 클래스에 실행을 위임해야 하며, 그렇지 않으면 내부 상태 기계가 다음 상태로 전환할 수 없습니다.
화면 회전 시 Activity가 재생성되는 이유는 방향 변경이 기기 구성 변경이기 때문입니다. 기본적으로 Android는 대체 리소스(layout-land, values-land)를 로드하기 위해 Activity를 소멸시키고 새 것을 만듭니다. 재생성을 비활성화하려면 매니페스트에 android:configChanges 속성을 추가할 수 있지만, Google은 데이터 보존을 위해 ViewModel 사용을 권장합니다.
중요한 데이터는 onPause()에서 저장됩니다. 이는 시스템에 의해 애플리케이션이 종료되기 전에 호출이 보장되는 마지막 메서드이기 때문입니다. onStop 및 onDestroy 이후 시스템은 추가 메서드를 호출하지 않고 프로세스를 종료할 수 있습니다. 초안 및 중간 데이터에는 apply()와 함께 SharedPreferences 또는 코루틴과 함께 Room을 사용하세요.
onPause는 Activity가 포커스를 잃었지만 부분적으로 표시된 상태로 남아 있을 때(예: 대화 상자가 열림) 호출됩니다. onStop은 Activity가 다른 Activity나 홈 버튼 누름으로 인해 화면에서 완전히 숨겨졌을 때 호출됩니다. 주요 실용적 차이점: onPause는 데이터 저장의 마지막 지점이고, onStop은 백그라운드에서 필요하지 않은 리스너 및 시스템 서비스를 해제하는 장소입니다.
ViewModel은 UI 데이터를 저장하고 구성 변경(화면 회전)을 자동으로 생존하는 Android Jetpack 구성 요소입니다. ViewModel은 Activity 재생성 시 소멸되지 않습니다. LifecycleOwner(Activity 또는 Fragment)가 완전히 종료될 때까지 유지됩니다. 이는 Bundle 및 onSaveInstanceState를 사용하지 않고 화면 회전 중 데이터 보존 문제를 해결합니다. ViewModel은 Google이 권장하는 MVVM 아키텍처의 필수 요소입니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.