안드로이드 LifeCyCle(생명 주기)를 배우기 앞서 Activity 인스턴스에 대해 간단히 배우고 넘어가겠다.
Activity 인스턴스 : 각각의 화면을 Activity 인스턴스라고 하는데, Activity 인스턴스는 생명 주기 안에서 상태가 계속 변한다.
- Activity를 생성하거나
- Activity를 잠시 중지하거나
- Activity를 다시 시작하거나
- Activity를 아예 종료한다.
이런식으로 액티비티는 여러 상태 변화가 일어난다.
액티비티의 여러 상태의 변화에 대해 자세히 알아보자.
생명주기의 흐름을 간단히 글로 풀어보자면,
- 우선 액티비티가 실행되면, onCreate() -> onStart() -> onResume() 순서로 액티비티를 활성화하게 된다.
- 다른 화면에 의해 화면의 일부가 가려지면 onPause()가 실행되고, 전체가 가려지면 onStop()이 실행된다.
(PAUSED 상태에서 사용자가 다시 화면으로 돌아오면 onResume() 로 액티비티가 활성화된다.)
(STOPPED 상태에서 사용자가 화면으로 돌아오면 onRestart() -> Start() -> onResume() 로 액티비티가 활성화된다.)
[1. 불투명한 새로운 액티비티가 최상단으로 올라와서 기존 액티비티가 완전히 보이지 않게 되면 onPause()에 이어서 onStop()까지 바로 호출된다.
2. 투명한 새로운 액티비티가 최상단으로 올라와서 기존 액티비티는 보이는 상태라면 onPause()까지만 호출된다.] - 사용자가 화면을 회전하거나 종료한다면 onPause() -> onStop() -> onDestroy() 하여 액티비티가 소멸된다.
간단한 정리로 하자면,
- 액티비티 생성
onCreate() → 생성된 화면 구성요소를 메모리에 로드
onStart(), onResume() → 화면의 구성요소를 나타내고 사용자와 상호작용 시작(Resumed: 실행 중) - 액티비티 화면에서 제거
onPause(), onStop() → 뒤로 가기, finish()를 실행할 때 동시에 실행
onDestory() → 최종적으로 액티비티가 메모리에서 제거 - 액티비티를 종료하지 않고 다른 액티비티 실행
onPause(), onStop() → 현재 액티비티를 종료하지 않고 새로운 액티비티가 만들어질 때(Stopped)
onStart(), onResume() → 두 메서드가 연속적으로 실행되고 Resumed 상태로 변경
구체적으로 생명주기 상태 정리를 하자면,
- onCreate()
Activity가 생성되면 가장 먼저 호출됨
화면 Layout 정의, View 생성, Databinding 등은 이곳에 구현함
생명주기 통틀어서 단 한 번만 수행되는 메소드
따라서 Activity 최초 실행에 해야하는 작업을 수행하기에 적합함 - onStart()
Activity가 화면에 표시되기 직전에 호출됨
화면에 진입할 때마다 실행되어야 하는 작업을 이곳에 구현함 - onResume()
Activity가 화면에 보여지는 직후에 호출됨
현재 Activity가 사용자에게 포커스인 되어있는 상태 - onPause()
Activity가 화면에 보여지지 않은 직후에 호출됨
현재 Activity가 사용자에게 포커스아웃 되어있는 상태
다른 Activity가 호출되기 전에 실행되기 때문에 무거운 작업을 수행하지 않도록 주의해야함
영구적인 Data는 이곳에 저장 - onStop()
Activity가 다른 Activity에 의해 100% 가려질 때 호출되는 메소드
홈 키를 누르는 경우, 다른 액티비티로의 이동이 있는 경우가 있음
이 상태에서 Activity가 호출되면, onRestart() 메소드가 호출됨 - onDestroy()
Activity가 완전히 종료되었을 때 호출되는 메소드
사용자: finish(), onBackPressed()(기존 액티비티의 onResume()까지 호출된 후 onDestroy() 호출)
시스템: 메모리부족(프로세스 종료)
onStop(), onDestroy() 메소드는 메모리 부족이 발생하면 스킵될 수 있음 - onRestart()
onStop()이 호출된 이후에 다시 기존 Activity로 돌아오는 경우에 호출되는 메소드
onRestart()가 호출된 이후 이어서 onStart()가 호출됨
이렇게 안드로이드 LifyCycle(생명주기)를 배웠으니 실제로 적용한 사례를 알아보자.
✔️ 사용자가 앱을 사용하는 도중에 전화가 걸려오거나 다른 앱으로 전환할 때 비정상 종료되는 문제
활동이 전화나 다른 앱으로 가려질 때에는 onStop()이, 재개될 때에는 onResume()이 실행된다.
따라서, 이 두 콜백 메서드를 잘 활용해 종료를 방지할 수 있다.
✔️ 사용자가 앱을 활발하게 사용하지 않는 경우 귀중한 시스템 리소스가 소비되는 문제
처음에 이야기했던 '액티비티를 떠났는데 백그라운드 작업이 종료되지 않는 경우' 등이 여기에 해당할 수 있다.
이런 상황을 방지하기 위해서 기본적인 안드로이드의 onDestroy() 또는 onStop() 메서드를 활용할 수 있다.
onDestroy() 또는 onStop()에서 문제가 되는 작업을 종료시키면, 액티비티가 종료되거나 잠시 떠나 있을 때 해당 작업도 자동으로 중지된다.
✔️ 사용자가 앱에서 나갔다가 나중에 돌아왔을 때 사용자의 진행 상태가 저장되지 않는 문제
상태를 임시 저장해야 할 경우에는, onStop() 이후에 onSavedInstanceState()라는 메서드가 자동으로 실행된다. (안드로이드 P 이후 기준)
또한 활동을 복구해야 할 때에는, onRestoreInstanceState() 메서드가 자동으로 실행된다.
따라서 이 문제는 onSavedInstanceState()와 onRestoreInstanceState()라는 수명 주기 콜백을 활용해 방지할 수 있다.
✔️ 화면이 가로 방향과 세로 방향 간에 회전할 경우, 비정상 종료되거나 사용자의 진행 상태가 저장되지 않는 문제
(화면이 회전해 가로/세로모드가 변경될 때에는 onDestroy() 이후 onCreate()가 다시 시작된다고 했었다.
하지만 화면이 돌아갈 때마다 onCreate()부터 시작해서 모든 값들을 다시 가져올 필요는 없지 않을까?
>> 화면을 돌리더라도 지금까지의 상태를 지속하면서 화면 구성만 변경할 수 있다면 좋지 않을까?
Manifest에서 수정할 수 있는 Activity별 configChanges는 그런 편의성을 제공한다.
Activity의 configChanges값에 '변경될 수 있는 설정'들을 지정해주면, 이 설정들이 변경되더라도 액티비티가 재시작되지 않는다.
이렇게 하면, onDestroy() -> onCreate() 대신에 onConfigurationChanged() 메서드가 실행되어 view가 유지된다.
생명주기를 적용하고 싶은데 어떻게 적용하는지 모를때 예제를 보며 익혀보자.
[예제]
- LoginActivity에서 RegisterActivity를 호출했을 때 라이프 사이클 호출 순서를 나열하시오.
[login] onPause()
[register] onCreate()
[register] onStart()
[register] onResume()
[login] onStop()
------------------------------------------------------------------------------------------------------------------
registerActivity를 호출하면 우선 loginActivity를 onPause()합니다.
그런 다음 registerActivity를 onCreate()하고,
생성된 registerActivity를 onStart()하고,
시작한 registerActivity를 onResume()합니다.
registerActivity가 onResume()되며 포커스인 상태이기 때문에, 비로소 loginActivity는 onStop() 상태에 들어가게 됩니다. - RegisterActivity 종료 후 다시 LoginActivity가 보여질 때 라이프사이클 호출 순서를 나열하시오.
[register] onPause()
[login] onRestart()
[login] onStart()
[login] onResume()
[register] onStop()
[register] onDestroy()
------------------------------------------------------------------------------------------------------------------
다시 loginActivity를 호출하면 우선 registerActivity를 onPause()합니다.
loginActivity는 onDestroy()되지 않고 onStop() 된채로 백그라운드에서 돌아가고 있기 때문에, onRestart()를 수행합니다.
그런 다음 onRestart()된 loginActivity를 onStart()하고,
onStart() 상태의 loginActivity를 onResume()합니다.
loginActivity가 onResume() 상태가 되어 사용자가 registerActivity로부터 포커스아웃 되었기 때문에 비로소 registerActivity는 onStop() 상태가 됩니다.
registerActivity를 종료하면 onDestroy() 호출 후 완전히 종료됩니다. - 앱을 실행시킬 때
onCreate - onStart - onResume - 홈 화면으로 나갔을 때
onPause - onStop - 앱으로 다시 돌아왔을 때
onRestart - onStart - onResume - 앱을 종료했을 때
onPause - onStop - 첫 번째 activity -> 두 번째 activity로 이동
1onPause - 2onCreate - 2onStart - 2onResume - 1onStop - 두 번째 activity -> 첫 번째 activity로 돌아가기
2onPause - 1onRestart - 1onStart - 1nResume - 2onStop - 2onDestroy
공부하면서 느낀점 : 안드로이드 생명주기를 배우면서 앱을 만들면서 설계 과정에서 생명주기도 생각을 해주며 설계를 해주어야 하고 완벽한 앱을 만들기위해선 생명주기에 대한 완벽한 이해가 필요하다는 생각과 앱이 가려지거나 종료 될때에 대해서 많은 생각을 하고 생명주기에 대해 생각을 하고 개발을 해야겠다.
[참고자료 : https://brunch.co.kr/@mystoryg/80 , https://bbaktaeho-95.tistory.com/62, https://todaycode.tistory.com/25]