액티비티, 프래그먼트 라이프사이클 정리
액티비티와 프래그먼트의 라이프사이클을 총체적으로 정리해보았다.
액티비티의 모든 인스턴스는 라이프사이클을 가지는데, 이 라이프사이클 동안 액티비티는 실행(running), 일시 중지(paused), 중단(stopped)의 3가지 상태를 가진다. 각 전환이 발생할 때 액티비티에 상태 변경을 알려주는 액티비티 메서드들이 있으며 이 메서드들을 안드로이드 런타임이 자동 호출한다.
‘실무에 바로 적용하는 안드로이드 프로그래밍’, 빌 필립스
액티비티 라이프사이클은 다음과 같은 순서로 진행된다.
onCreate() -> onStart() -> onResume() -> onPause() -> onStop() -> onDestroy()
(onRestart()는 onStop() 이후에 호출되며, 다시 onStart()를 호출한다)
그렇다면 라이프 사이클이 진행되는 동안 액티비티의 상태는 어떻게 될까? 우선 액티비티가 존재하지 않을 때부터 실행(running)될 때 까지의 순서로 살펴보자. onCreate()가 호출되기 이전 앱의 액티비티는 존재하지 않는다.
만약 onCreate()가 호출되면 액티비티는 중단(stopped)상태로 전환된다. 아직까지는 사용자가 화면을 볼 수 없다. 다음으로 onStart()가 호출되면 액티비티가 일시중지(paused) 상태로 전환되어 화면이 보이게 되며, onResume()이 호출되고 나서야 비로소 액티비티가 실행(running) 상태가 된다. 이 때의 액티비티는 포그라운드에 존재한다.
이제는 실행중인 액티비티가 소멸될 때까지의 과정을 살펴보자.
소멸 과정은 생성에서 실행되었던 과정을 반대로 거슬러 올라간다고 보면 된다. 실행(running)중인 액티비티에서 onPause()가 호출되면 액티비티는 일시 중지(pause)상태가 되며 아직 화면은 보이는 상태이다.
여기서 onStop()이 호출되면 액티비티는 중단(stopped) 상태로 전한되며 화면에 나타나지 않게 된다. 하지만 화면에 보이지 않는다고 해서 액티비티가 존재하지 않는 것은 아니다. 마지막으로 onDestroy()까지 호출되어야 비로소 액티비티가 소멸되어 더 이상 존재하지 않게 된다.
위에서 언급한 메서드들은 개발자가 직접 호출하여 실행하는 것이 아니라 안드로이드 시스템이 실행하는 것이며, 개발자는 하고자 하는 동작을 적절한 메서드에서 오버라이딩하여 작성해야 한다. 하지만 나같은 초보는 막상 코드를 작성하려고 하면 머릿속이 하얘지고 이런 생각이 든다.
“라이프사이클이 저런 구조를 가진다는 것은 알겠는데, 그래서 내가 작성하고자 하는 코드를 대체 어디 넣어야 하는거지?”
이럴 때는 백문이 불여일타. 샘플앱을 만들어 각각의 메서드에 로그를 찍어보고 이것저것 동작시켜보았다. (사실 이 글은 앱을 제대로 만들지 못해서 3주째 삽질을 하다가 정리해보는 글이다. 왜 진작에 이렇게 정리해 볼 생각을 못했는지 도무지 이해가 안가는 과거의 나.. 반성하자)
야래와 같이 액티비티를 하나 만들고 위의 6개 메서드에 각각 로그를 찍어보았다(잘 보이게 하기 위해 에러 로그를 사용하였다). 앱을 실행함과 동시에 onCreate(), onStart(), onResume()이 호출된다.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.e("TAG ", "onCreate(), MainActivity")
}
override fun onStart() {
super.onStart()
Log.e("TAG ", "onStart(), MainActivity")
}
override fun onResume() {
super.onResume()
Log.e("TAG ", "onResume(), MainActivity")
}
override fun onPause() {
super.onPause()
Log.e("TAG ", "onPause(), MainActivity")
}
override fun onStop() {
super.onStop()
Log.e("TAG ", "onStop(), MainActivity")
}
override fun onDestroy() {
super.onDestroy()
Log.e("TAG ", "onDestroy(), MainActivity")
}
}
이 때 홈버튼을 클릭하여 홈 화면으로 나가면(앱을 종료하는 것은 아님) onPause()와 onStop()이 호출된다. 그리고 다시 앱을 띄우면 onStart()와 onResume()이 호출된다. 오버뷰(overview) 버튼 (네모 버튼) 을 클릭해도 마찬가지로 onPause()와 onStop()이 호출되고, 다시 앱을 띄울 때 onStart()와 onResume()이 호출된다.
그럼 만약 앱이 화면에 띄워진 상태에서 백버튼을 누르면 어떻게 될까? onPause(), onStop()과 더불어 onDestroy()까지 호출된다. 액티비티가 소멸되었기때문에 다시 앱으로 돌아갔을때에는 onCreate()부터 onStart(), onResume()까지 호출된다.
이제 액티비티 라이프사이클에 대해서 확실히 알아보았으니 프래그먼트의 라이프사이클로 넘어가보자.
프래그먼트 라이프사이클은 다음과 같은 순서로 진행된다.
onAttach() -> onCreate() -> onCreateView() -> onActivityCreated() -> onStart() -> onResume() -> onPause() -> onStop() -> onDestroyView() -> onDestroy() -> onDetach()
위에서 로그를 찍어보았던 메인액티비티에 FrameLayout을 만들고 그 안에 아래와 같이 프래그먼트를 add해보았다. 그리고 프래그먼트에도 위의 액티비티에서 로그를 찍었던 것처럼 모든 오버라이딩 메소드에 로그를 찍어보았다.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.e("TAG ", "onCreate(), MainActivity")
supportFragmentManager.beginTransaction()
.add(R.id.frameContainer, FirstFragment())
.commit()
}
앱을 실행하자마자 위에서 보았던것처럼 액티비티의 onCreate()가 호출되었고, 이어서 프래그먼트의 onAttach(), onCreate(), onCreateView(), onViewCreated(), onActivityCreated()가 차례대로 호출되었다. 그리고 액티비티의 onStart()와 onResume()이 호출되고 난 후에 프래그먼트의 onResume()이 호출되었다. 액티비티를 생성한 다음 프래그먼트를 생성하고, 액티비티를 포그라운드로 보낸 다음(실행 상태) 프래그먼트를 포그라운드로 보낸 것이다.
백버튼을 눌러보았더니 예상 밖의 순서로 진행되었다. 프래그먼트가 먼저 onPause()를 호출하고 액티비티의 onPause()가 호출되었다. 그리고 프래그먼트의 onStop()이 먼저 호출된 후 액티비티의 onStop()이 호출되었으며, 이어서 프래그먼트의 onDestroyView(), onDestroy(), onDetach()가 호출된 후 액티비티의 onDestroy()가 호출되었다. 앱을 다시 화면에 띄우니 처음 실행할때의 순서대로 똑같은 메서드들이 호출되었다.
취소 버튼을 눌렀을때에는 프래그먼트 onPause() -> 액티비티 onPause() -> 프래그먼트 onStop() -> 액티비티 onStop() 순서로 호출되었다. 그리고 다시 앱을 화면에 띄우면 액티비티 onStart() -> 액티비티 onResume() -> 프래그먼트 onResume() 순서로 호출되었다. 오버뷰 버튼을 눌렀을때 역시 마찬가지였다.
이렇게 한 번 보고 나니 액티비티와 프래그먼트가 각기 어떤 흐름으로 실행되는 것인지 감이 온다. 이제 실전에서 적용해 볼 차례이다.
참고 자료