💡 Intro
Activity, Fragment, View는 각각의 LifeCycle(생명주기)을 가지고 있다. 모두 다뤄볼 예정이며, 먼저 Activity LifeCycle에 대해 알아보자.
❓ 액티비티 상태
액티비티는 총 4가지의 상태를 가지고 있다. 각 상태의 명칭과 특징은 다음과 같다.
존재하지 않음(nonexistent)
- 액티비티가 아직 론칭되지 않았거나 소멸된 상태
- 액티비티 인스턴스는 메모리에 존재하지 않음
- 사용자가 보거나 상호작용하기 위한 뷰가 존재하지 않음
중단(stopped)
- 액티비티 인스턴스가 메모리에 존재함
- 액티비티가 화면에서 완전히 가려졌을 때의 상태(화면에 액티비티가 전혀 보이지 않음)
일시중지(paused)
- 액티비티가 포그라운드에서 작동하지 않음
- 액티비티 전체 혹은 일부가 화면에 보이는 상태(투명한 액티비티, 다중 창 모드, 다이얼로그 등)
실행 재개(resumed)
- 액티비티 인스턴스가 메모리에 있는 상태
- 화면에서 뷰 전체를 볼 수 있는 상태
- 액티비티가 포그라운드에 있는 상태
- 사용자와 상호작용중인 상태
액티비티가 새로운 상태에 들어가면 시스템은 콜백 메서드를 호출하고, 이를 통해 생명주기 전환 시점에서 필요한 일들을 할 수 있게 된다.
❓ 액티비티 생명주기
onCreate()
- 액티비티가 생성될 때 호출된다.
- 전체 생명주기 동안 한 번만 실행되어야 하는 시작 로직들을 넣는다. (클래스의 인스턴스화 작업을 넣는다.)
- savedInstanceState 매개변수를 인자로 받는데, 이는 액티비티의 이전 상태가 저장된 Bundle객체다. 처음 생성된 경우 Bundle은 null이다.
- 너무 많은 작업을 수행하면 앱의 실행 속도가 느려질 수 있다.(feat docs)
onStart()
- 액티비티가 사용자에게 보여지기 바로 직전에 호출된다.
- 화면에 진입할 때마다 실행되어야 하는 작업을 이곳에 구현한다.
- UI를 유지 및 관리하는 코드를 넣는다.
- 주로 데이터를 로드하고 화면에 표시하는 등의 작업을 수행한다. (이 단계는 액티비티가 아직 사용자에게 보여지지는 않지만, 준비과정을 거치고 있는 것으로 생각할 수 있다.)
- UI 초기화에 적합하나 작업이 빠르게 완료되기 때문에 화면이 부자연스럽게 보일 수 있으므로 UI 수정은 조심해야 한다.(feat docs)
onResume()
- 액티비티가 사용자와 상호작용을 시작할 수 있을 때 호출된다.
- 현재 액티비티가 사용자에게 포커스인 되어있는 상태다.
- 어떤 이벤트가 발생하여 앱에서 포커스가 떠날 때까지 앱은 이 상태에 머무른다.(어떤 이벤트란 전화가 오거나, 사용자가 다른 액티비티로 이동하거나, 화면이 꺼지는 등의 이벤트들을 말한다.)
- 사용자가 화면을 볼 때 호출되므로, 작업이 오래 걸리면 화면이 멈추거나 응답하지 않는 것처럼 보일 수 있기 때문에 가벼운 작업을 수행하는 것이 좋다. (feat docs)
onPause()
- 사용자가 액티비티를 떠날 때 첫 번째로 실행된다.
- 액티비티가 포그라운드에 없다는 것을 의미한다.
- 이 메서드는 아주 잠깐 실행되므로 저장 작업을 실행하기에는 시간이 부족할 수 있다. 때문에 이 메서드를 통해 애플리케이션 또는 사용자 데이터를 저장하거나, 네트워크 호출을 하거나, 데이터베이스 트랜잭션을 실행해서는 안된다. (onStop에서 수행해야 한다.)
- 리소스, 센서에 대한 핸들(ex GPS) 또는 액티비티가 일시 중지되고 사용자가 필요로 하지 않는 동안 배터리 수명에 영향을 미치는 모든 리소스를 해제할 수 있다. (feat docs)
- Dialog같은 반투명 액티비티가 열릴 때 해당 상태로 들어간다.
onStop()
- 액티비티가 다른 액티비티에 의해 완전히 가려질 때 호출된다. (액티비티가 더이상 사용자에게 보여지지 않을 때 호출된다.)
- 이 상태에서 다시 액티비티가 호출되면, onRestart()가 호출된다.
- 앱에 필요하지 않은 리소스를 해제하거나 조정하고, 데이터 저장과 관련된 데이터 베이스 트랜잭션 등 CPU를 많이 소모하는 작업을 수행하기 적당하다. (데이터베이스에 정보 저장)
- 메모리가 부족할 경우 onStop 메소드가 호출되지 않을 수도 있으며, 시스템이 액티비티를 강제 종료할 수 있다. 사용자가 화면을 떠난 후 호출되므로, 작업이 오래 걸리면 시스템 자원을 점유하여 다른 앱의 동작에 영향을 줄 수 있기 때문에 오랜 시간 소요되는 작업의 경우에는 별도의 스레드에서 수행하는 것이 좋다. (feat docs)
onDestroy()
- 액티비티가 완전히 소멸되기 직전에 호출된다.
- finish() 메서드가 호출될 때 호출된다.
- 이 메서드는 메모리가 부족한 경우 호출되지 않을 수 있다.
onRestart()
- onStop이 호출된 이후 다시 기존 액티비티로 돌아오는 경우 호출된다.
- 액티비티가 다시 시작되기 전에 필요한 초기화 작업을 수행한다.
- 이후에는 onStart가 호출된다.
❗️ 예시로 익숙해지는 액티비티 생명주기
액티비티의 생명주기를 단순히 외우는 것이 아니라 액티비티가 어떠한 상황에서 상태가 바뀌는지, 상태가 바뀔 때 어떤 메서드가 호출되는지를 기억해서 논리적으로 설명할 수 있도록 노력해보자.
액티비티가 시작될 때
onStart → onStart → onResume
액티비티가 종료될 때
onPause → onStop → onDestroy
기기 구성 변경 발생 시
onPause → onStop → onDestroy → onCreate → onStart → onResume
기기 구성 변경이란, 화면 회전, 테마, 언어 및 글꼴 크기, 두께 변경 등이 있다. 이러한 기기 구성 변경이 발생하면 액티비티를 재생성한다.
불투명한 다른 액티비티가 띄워질 때
onPause → onStop
투명하거나 일부만 가리는 액티비티가 띄워질 때
onPause
홈 버튼을 눌렀을 때
onPause → onStop
다른 작업을 하려고 하지만, 언제든 다시 돌아올 수 있다. 즉, 중단 상태다.
액티비티 인스턴스가 아직 메모리에 있기 때문에 onDestroy는 실행되지 않는다.
하지만 오랜 시간 이 상태에 머물고 있거나 메모리가 부족한경우 OS가 백그라운드에서 실행중인 앱을 종료할 수 있다. 이러한 이유로 액티비티의 onDestroy가 100% 호출된다고 할 수 없다.
⭐️ AlertDialog가 띄워졌을 때
AlertDialog는 액티비티 생명주기에 영향을 주지 않음
AlertDialog는 액티비티 생명주기에 영향을 주지 않기 때문에 아무런 콜백 메서드도 호출되지 않는다.
⭐️ 시스템 권한 대화상자가 띄워졌을 때
onPause → (대화상자 종료) → onResume
시스템 권한 대화상자가 띄워졌을 때는 포그라운드에 있지 않을 때 실행되는 onPause까지만 실행된다. 대화상자가 닫히고 화면으로 돌아오면 onResume이 실행된다.
위에서 확인해봤을 때 다이얼로그는 액티비티 생명주기에 영향을 주지 않는다. 그런데 왜 시스템 권한 대화상자는 액티비티 생명주기에 영향을 주는 것일까?
권한 요청시 requestPermission 메서드를 호출해야 한다. 공식 문서에 의하면 앱이 요구하는 권한을 허용하고 있지 않은 경우 권한 요청을 하는 UI를 띄운다고 되어있다. 다이얼로그라고 명시되어 있지 않지만 UI가 다이얼로그와 비슷하게 생겼기 때문에 다이얼로그라고 불리는 것이 아닐까 한다.
공식문서를 보면 requestPermssion 메서드는 사용자에게 어떤 권한을 허용하고 거절할지에 대한 Activity를 띄울 수 있다고 나와있고, 개발자는 현재 액티비티가 pause되고, resume될 수 있다는 것을 알아야 한다고 나와있다. 아래는 공식문서 내용이다.
다이얼로그 창이 뜨는 경우 onPause까지 실행되는 것을 볼 수 있다.
다이얼로그 창이 사라진 경우 onResume이 실행되는 것을 볼 수 있다.
⭐️ A액티비티가 열려있는 상태에서 B액티비티로 이동할 때
onPause(A) → onCreate(B) → onStart(B) → onResume(B) → onStop(A)
onStop이 호출된다는 것은 현재 액티비티가 사용자에게 보이지 않는다는 것을, onResume이 호출된다는 것은 현재 액티비티가 사용자와 상호작용 할 수 있다는 것을 의미한다.
앱이 사용자와 지속적으로 상호작용하려면 어떠한 액티비티라도 화면이 사용자에게 보여야 한다. 때문에 먼저 띄워져있는 액티비티가 종료되기 전에 띄워질 액티비티가 준비되어야 한다.
정리하면 화면을 벗어나기 때문에 A액티비티가 onPause되고, B액티비티가 화면에 보일 수 있도록 onResume까지 진행된 뒤에 A액티비티가 onStop되게 된다.
⭐️ A액티비티가 열려있는 상태에서 B액티비티로 이동하고, B액티비티를 종료했을 때
onPause(B) → onRestart(A) → onStart(A) → onResume(A) → onStop(B) → onDestroy(B)
위와 같은 이유로 B액티비티가 onPause까지 실행되고, A액티비티가 화면에 보일 수 있도록 onResume까지 실행된다. A액티비티에서 B액티비티로 이동했음을 가정했기 때문에 A액티비티의 onCreate은 실행되지 않는 것이다. A액티비티가 화면에 보일 수 있도록 준비가 다 되면 B액티비티는 onDestroy까지 실행된다.
참고