안드로이드 액티비티 생명주기

  1. 액티비티 생명주기 (Activity Life Cycle)
    1. 액티비티 상태와 생명주기 콜백
    2. 액티비티의 4가지 상태
    3. 액티비티 콜백 함수
    4. 액티비티 실행 순서
    5. UI 변경과 다중 창 모드
    6. 정리
    7. 참고자료

액티비티 생명주기 (Activity Life Cycle)

앱을 사용하다보면 세로로 보다 가로로 봐야할 때가 종종 있다. 사용자는 당연히 보던 화면이 그대로 옮겨져 화면에 보여지는 것을 원한다. 안드로이드 앱에서는 자동으로 알아서 상태를 관리해 보여줄까??

결론부터 얘기하자면 그렇지 않다. 물론, EditText와 같은 기본적인 상태 관리는 제공을 해주는 편이다.

액티비티 상태와 생명주기 콜백

우선, 위와 같은 자동 상태 관리가 안 되는 이유는 안드로이드를 구성하는 Activity, Fragment에 대해 알아야 한다. 이번 Chapter에서는 Activity에 대해 알아보고자 한다.

Activity의 상태

  • 실행 재개 (Resumed)
  • 일시 중지 (Paused)
  • 중단 (Stopped)
  • 존재하지 않음 (Nonexistent) == 소멸(Destroyed) 상태

Activity는 위 4가지 상태로 상호 전환된다. 이때, 전환이 발생할 때 Activity에 상태 변경을 알려주는 Activity 함수들이 존재하며, 이 함수들은 안드로이드가 자동 호출한다.


이미지 출처 : 실무에 바로 적용하는 안드로이드 프로그래밍 65p

위에서 말한 액티비티 콜백 함수는 총 6가지로 다음과 같다.

  • onCreate()
  • onStart()
  • onResume()
  • onPause()
  • onStop()
  • onDestroy()

액티비티의 4가지 상태

저자는 아래와 같이 4가지 액티비티 상태를 정리 했다.

상태메모리에 있음?사용자에게 보임?포그라운드에서 실행?
존재하지 않음(Nonexistent)아니오아니오아니오
중단(Stopped)아니오아니오
일시 중지(Paused)예 (부분적*)아니오
실행 재개(Resumed)

네이버에 의하면 포그라운드란 다음과 같은 뜻을 같는다.


이미지 출처 : 네이버 영어사전

이 뜻과 함께 유추해보자면, 포그라운드는 앱에 액티비티가 보여지고, 상호작용 할 수 있는 공간이라고 이해하면 될 것 같다.

  • 존재하지 않음(Nonexistent)

    액티비티가 아직 생성되어 보여지지 않았거나 소멸된 상태 보여지지도 않고 소멸되있는 상태기 때문에 메모리에 존재하지 않는다.

  • 중단 (Stopped)

    액티비티가 완전히 가려져 보여지고 있진 않지만 소멸되진 않아 메모리에 액티비티 인스턴스가 남아있는 상태 이 상태는 액티비티가 처음 시작될 때와 보여지지 않을 때 거치게 된다. 참고로 상태와 콜백함수는 엄연히 다르다. (상태에 따른 콜백함수가 호출 됨)

  • 일시 중지 (Paused)

    액티비티가 포그라운드에서 작동(상호작용)하진 않지만, 액티비티 인스턴스의 뷰 전체, 혹은 일부를 화면에서 볼 수 있는 상태 이러한 경우는 불투명한 또다른 액티비티가 보여지거나, 다중 창 모드(분할 화면 모드)일 때 주로 발생한다.

  • 실행 재개 (Resumed)

    액티비티가 메모리에 있으면서 화면에서 전체를 볼 수 있고(가려지지 않음), 포그라운드에 있음(상호작용 가능) 상태 이러한 실행 재개 상태는 장치의 전체 시스템에 걸쳐 하나의 액티비티만 될 수 있다. 즉, 내가 실행중인 앱을 제외하곤 다른 앱은 각기 다른 상태로 전환된다.

액티비티 콜백 함수

액티비티는 위 상태가 전환 될 때 함수를 사용해 생명주기 전환에 필요한 작업을 처리할 수 있다. 이러한 함수들을 “생명주기 콜백함수”라고 부른다.

UI를 준비하기 위해 액티비티에서는 onCreate(Bundle?) 함수를 오버라이드 한다.

  • 위젯을 inflate 해 뷰 객체 생성 후 화면에 띄움(setContentView(Int))
  • inflate 된 위젯의 객체 참조를 얻음
  • 사용자와의 상호 작용을 처리하기 위해 위젯에 리스너를 설정
  • 외부의 모델 데이터를 연결

기본적으로 이러한 생명주기 콜백 함수들은 사용자(개발자)가 직접 호출하지 않는다.

상태가 변경 됨을 액티비티에 알려주기 위해 안드로이드(운영체제)가 생명주기 콜백 함수들을 적절한 시점에 호출하기 때문이다. 사용자(개발자)는 사용할 콜백 함수를 오버라이드 해서 사용하기만 하면 됨

각 콜백 함수들은 아래와 같은 오버라이드 되는 슈퍼 클래스 함수를 호출하는 코드가 존재하며, 이러한 코드는 맨 앞(상단)에 있어야 한다.

override fun onStart() {
    super.onStart()     // 맨 앞에 위치해야 함
    Log.d("Acitivity Life Cycle", "onStart() Called")
}

각 콜백 함수에 override 키워드는 슈퍼 클래스에 있는지 컴파일러에게 확인을 요청하기 때문이다. 아래와 같이 작성하면 컴파일 에러가 발생한다.

override fun onStar() { // 오버라이드 하는 함수명이 슈퍼 클래스에 존재하지 않음 - 컴파일 에러
    super.onStart()     // 맨 앞에 위치해야 함
    Log.d("Acitivity Life Cycle", "onStart() Called")
}

그렇기 때문에 에러를 런타임이 아닌 컴파일 시점에 찾아 해결할 수 있다.

액티비티 실행 순서

  1. 액티비티가 화면에 보여지는 과정
    • onCreate() - onStart() - onResume()
  2. 액티비티가 종료되는 과정
    • onPause() - onStop() - onDestroy()
  3. 기기 구성 변경 발생 시
    • onPause() - onStop() - onDestroy() - onCreate() - onStart() - onResume()

      기기 구성 변경이란, 화면 회전, 도크(dock)모드, 테마, 언어 및 글꼴 크기 두께 변경 등이 있습니다. 이러한 기기 구성 변경이 발생하면 액티비티를 재생성 합니다.

  4. 불투명하거나 일부만 가리는 다른 액티비티가 호출되었을 때
    • onPause() - onStop()
  5. 투명한 액티비티가 호출되었을 때
    • onPause()

UI 변경과 다중 창 모드

안드로이드 7.0(Nougat) 이전에는 대부분의 액티비티가 매우 짧은 시간 동안만 ‘일시 중지(Paused)’ 상태에 머물렀다가 ‘실행 재개(Resumed)나 ‘Paused’ 상태로 바뀌었다. 그에따라 많은 개발자들은 액티비티 상태가 ‘일시 중지’가 아닌 ‘실행 재개’일 때 UI를 변경해야 한다고 생각했고, UI와 관련해서 진행 중인 변경(애니메이션, 데이터 갱신 등)의 시작과 중단을 onResume()과 onPause()에서 하는 것이 일반적이었다고 한다.

그.러.나

안드로이드 7.0(Nougat)부터 도입된 다중 창 모드에서 위와 같이 작성된 로직들이 제대로 동작하지 못하는 일이 생겼다. 다중 창 모드에서는 ‘일시 중지’ 상태여도 긴 시간동안 화면에서 완전하게 보일 수 있기 때문에 사용자들은 실행 중이라고 생각할 수 있다는 것이다.

저자는 예시로 비디오 재생 앱을 들었다. onResume()에서 비디오를 재생하고, onPause()에서 비디오를 멈추게 한다 했을 때, 다중 창 모드에서 다른 앱을 제어 중일 때, 비디오가 멈춘다면? (재생할 수 없다면?) 이는 사용자의 불만을 초래할 것이다. 물론 이러한 문제는 onResume이 아닌 onStart에서, onPause가 아닌 onStop에서 수행하면 된다.

다중 실행 재개(mutli-resume) 구글에서 내놓은 지원 사양으로 다중 창 모드의 장치에서는 사용자가 어떤 창의 앱을 사용하건 각 창에 완전하게 보이는 액티비티들이 실행 재개 상태가 되며, 굳이 이전 안드로이드 버전의 코드를 수정하지 않아도 된다.

안드로이드 9.0(Pie) 이상에서 앱이 실행될 때는 안드로이드 매니페스트 파일에 아래와 같이 정의해줘야 사용 가능하다.

// AndroidManifest.xml
<meta-data 
    android:name="android.allow_multiple_resumed_activities"
    android:value="true"/>

정리

안드로이드에서는 액티비티 생명주기가 존재하며 이러한 생명주기는 안드로이드(운영체제)에 의해서 각기 다른 상태로 전환된다. 이 때, 전환하며 어떠한 작업을 수행하는 ‘함수’들이 존재하는데, 이러한 함수들을 생명주기 콜백 함수라고 한다. 이러한 콜백 함수는 안드로이드 시스템에 의해 호출 되고, 상태가 변할 때 호출되므로 적절한 로직을 담아야 한다.

내 경험담 A 액티비티에서 결과를 표현하고, B 액티비티가 화면에 표시되었다 소멸 됐을 때, 해당 액티비티가 표시 되었다는 것을 A 액티비티에 데이터 갱신을 통해 알려주는 작업을 onResume()에서 수행했었다. 이러한 onResume은 액티비티 생성, 재생성, 다중 창 모드 / 앱에서 나갔을 때도 항상 호출되기 때문에 올바른 사용 방법이 아니다. 이러한 작업은 ActivityResultLauncher를 이용하여 해결하자.

참고자료

실무에 바로 적용하는 안드로이드 프로그래밍