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

KWANWOO·2022년 1월 25일
1
post-thumbnail

Android Activity Life Cycle

안드로이드 관련 마지막 포스팅이 안드로이드의 Activity에 대한 소개와 간단한 콜백 함수들을 정리 했었다. 이 글에서는 이러한 Activity가 안드로이드 환경에서 어떠한 생명주기를 가지는지와 콜백 함수들에 대해 좀 더 자세히 정리하고 한다.

사용자가 Activity를 벗어나 다시 돌아왔을 때 Activity가 작동하는 방식을 생명 주기 콜백 메소드에서 선언할 수 있다. 즉 각 콜백 함수들은 상태 변화에 적합한 특정 작업을 실행할 수 있도록 한다. 이러한 콜백을 잘 구현하면 아래와 같은 문제들을 예방할 수 있다.

  • 사용자가 앱을 사용하는 도중에 전화가 걸려오는 등 다른 앱으로 전환할 때 비정상적으로 종료되는 문제
  • 사용자가 앱을 활발하게 사용하지 않는 경우 시스템 리소스가 낭비되는 문제
  • 사용자가 앱에서 나갔다가 돌아왔을 때 사용자의 진행 상태가 저장되지 않는 문제
  • 화면이 가로 또는 세로 방향으로 회전할 경우 비정상적으로 종료되거나 사용자의 진행 상태가 저장되지 않는 문제

1. Activity Life Cycle의 개념

사용자가 앱을 탐색하고, 앱에서 나가고, 앱으로 다시 돌아가면, 앱의 Activity 인스턴스는 수명 주기 안에서 서로 다른 상태를 통해 전환됩니다. Activity 클래스는 활동이 상태 변화(시스템이 활동을 생성, 중단 또는 다시 시작하거나, 활동이 있는 프로세스를 종료하는 등)를 알아차릴 수 있는 여러 콜백을 제공합니다.

Activity 생명주기 단계 사이의 전환을 위해서 Activity 클래스는 6가지 주요 콜백을 제공한다. Activity가 새로운 상태에 들어가면 시스템은 각 콜백을 호출한다.

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

    사용자가 Activity를 벗어나기 시작하면 시스템은 Activity를 대체할 메소드를 호출한다. 이 때 Activity는 메모리 안에 남아 있으며 포그라운드로 다시 돌아올 수 있다.

    포그라운드란?

    화면에서 앱이 어떤 작업을 하고 있는지 눈으로 직접 확인할 수 있는 것을 포그라운드 프로세스라고 볼 수 있으며, 쉽게 말해 유튜브를 보거나 웹서핑을 하는 등을 포그라운드 프로세스라고 할 수 있다.

사용자가 Activity로 돌아오는 경우 사용자가 종료한 지점에서 Activity가 다시 시작된다. 몇 가지 예외를 제외하면 앱은 백드라운드에서 실행될 때 Activity를 실행할 수 없다!!!!

2. Activity 콜백

여기서는 Activity 생명 주기의 개념에서 소개한 각각의 콜백에 대해 자세히 설명한다.

1) onCreate()

onCreate() 콜백은 시스템이 Activity를 생성할 때 실행되는 것으로, 필수적으로 구현해야 한다!!!! Activity가 생성되면 [생성됨] 상태가 된다. onCreate() 메소드에서 Activity의 전체 생명 주기 동안 한 번만 발생해야 하는 기본 애플리케이션 시작 로직을 실행한다.

  • 데이터를 목록에 바인딩, Activity를 ViewModel과 연결 등

onCreate()는 savedIndstanceState 매개변수를 받는데, 이는 Activity의 이전 저장 상태가 포함된 Bundle 객체이다.(처음 생성된 Activity의 경우 null이다.)

  • Activity에 대한 기본 설정 예시
lateinit var textView: TextView

// some transient state for the activity instance
var gameState: String? = null

override fun onCreate(savedInstanceState: Bundle?) {
    // call the super class onCreate to complete the creation of activity like
    // the view hierarchy
    super.onCreate(savedInstanceState)

    // recovering the instance state
    gameState = savedInstanceState?.getString(GAME_STATE_KEY)

    // set the user interface layout for this activity
    // the layout file is defined in the project res/layout/main_activity.xml file
    setContentView(R.layout.main_activity)

    // initialize member TextView so we can manipulate it later
    textView = findViewById(R.id.text_view)
}

// This callback is called only when there is a saved instance that is previously saved by using
// onSaveInstanceState(). We restore some state in onCreate(), while we can optionally restore
// other state here, possibly usable after onStart() has completed.
// The savedInstanceState Bundle is same as the one used in onCreate().
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
    textView.text = savedInstanceState?.getString(TEXT_VIEW_KEY)
}

// invoked when the activity may be temporarily destroyed, save the instance state here
override fun onSaveInstanceState(outState: Bundle?) {
    outState?.run {
        putString(GAME_STATE_KEY, gameState)
        putString(TEXT_VIEW_KEY, textView.text.toString())
    }
    // call superclass to save any view hierarchy
    super.onSaveInstanceState(outState)
}

Activity의 상태를 저장하는 SIS나 ViewModel은 다른 글에서 따로 정리해야 겠다.
Activity는 생성됨 상태에 머무르지 않는다. onCreate()가 실행을 완료하면 시작됨 상태가 되고, 시스템은 이어서 onStart()와 onResume() 메소드를 호출한다.

2) onStart()

Activity가 시작됨 상태에 들어가면 시스템은 onStart() 콜백을 호출한다. onStart()가 호출되면 Activity가 사용자에게 표시되고, 앱은 Activity를 포그라운드에 보내 상호작용 할 수 있도록 준비한다.

onStart() 메소드는 매우 빠르게 완료되고, 생성됨 상태와 마찬가지로 Activity는 시작됨 상태에 머무르지 않는다. 콜백이 완료되면 Activity가 재개됨 상태에 들어가고, 시스템은 onResume() 메소드를 호출한다.

3) onResume()

Activity가 재개됨 상태에 들어가면 포그라운드에 표시되고, 시스템이 onResume() 콜백을 호출한다. 이 상태에 들어갔을 때 앱이 사용자와 상호작용을 한다. 어떤 이벤트(전화가 오거나 다른 Activity로 이동하거나, 기기 화면이 꺼지는 등)가 발생하여 앱에서 포커스가 떠날 때까지 앱은 이 상태에 머무른다.

방해되는 이벤트가 발생하면 Activity는 일시중지됨 상태로 전환되고, 시스템이 onPause()를 호출한다. 활동이 일시중지됨 상태에서 다시 재개됨 상태로 돌아오면 시스템은 onResume() 메소드를 다시 호출한다. 즉, onResume()을 구현하여 onPause() 중에 해제되는 구성 요소를 초기화 하고, Activity가 재개됨 상태로 전환될 때마다 필요한 다른 초기화 작업도 수행해야 한다.

  • 구성요소가 ON_RESUME 이벤트를 수신할 때 카메라에 액세스하는 생명 주기 인식 구성요소의 예시(LifecycleObserver가 ON_RESUME 이벤트를 수신하면 카메라를 초기화 한다.)
class CameraComponent : LifecycleObserver {
    ...
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun initializeCamera() {
        if (camera == null) {
            getCamera()
        }
    }
    ...
}

멀티 윈도우와 같이 Acvitity가 일시중지됨 상태이지만 위와 같이 카메라를 활성 상태로 유지하고 싶다면 ON_START 이벤트가 실행된 이후에 카메라를 초기화해야 한다.(실질적으로는 전반적인 사용자 환경의 저하되는 예시임!!)

ON_START 이벤트 이후에 무언가를 초기화하는 경우, ON_STOP 이벤트 이후에 이를 해제하거나 종료하세요. ON_RESUME 이벤트 이후에 초기화하는 경우, ON_PAUSE 이벤트 이후에 해제하세요.

4) onPause()

시스템은 사용자가 Activity를 떠나는 것을 나타내는 첫 번째 신호로 onPause()를 호출한다. 즉, Activity가 포그라운드에 있지 않게 되었다는 것을 나타낸다. 여기에서 구성 요소가 포그라운드에 있지 않을 때 실행할 필요가 없는 기능을 정지할 수 있다.

Activity가 이 상태에 들어가는 이유로는 이벤트가 앱 실행을 방해하거나, 멀티 윈도우 또는 반투명 활동(대화상자) 등이 있다.

  • 멀티 윈도우 모드를 잘 지원하기 위해서는 UI 관련 리소스와 작업을 완전히 해제하거나 조정할 때 onPause() 대신 onStop()을 사용하는 것이 좋다.
  • LifecycleObserver가 ON_PAUSE 이벤트에 응답하는 예시(ON_RESUME에서 초기화 된 카메라를 해제한다.)
class CameraComponent : LifecycleObserver {
    ...
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun releaseCamera() {
        camera?.release()
        camera = null
    }
    ...
}

onPause()는 아주 잠깐 실행된다. 따라서 저장 작업을 실행하기에는 시간이 부족할 수 있다. onPause()를 사용해 애플리케이션 또는 사용자 데이터를 저장하거나, 네트워크 호출, 데이터베이스 트랜잭션을 실행해서는 안된다.(onStop()에서 수행!!!!)

onPause() 실행이 완료되더라도 Activity는 일지중지됨 상태로 남아있을 수 있다. Activity가 다시 실행되면 onResume()을 호출하고, 완전히 보이지 않게 되면 onStop()을 호출한다.

5) onStop()

Activity가 사용자에게 더 이상 표시되지 않으면 중단됨 상태에 들어가고, 시스템은 onStop() 콜백을 호출한다.

  • 새로 시작된 Activity가 화면 전체를 차지하는 경우 등

onStop() 메소드에서는 앱이 사용자에게 보이지 않는 동안 앱은 필요하지 않은 리소스를 해제하거나 조정해야 한다.

  • 애니메이션 일시중지, 세밀한 위치 업데이트를 대략적인 위치 업데이트로 전환 등

또한 CPU를 비교적 많이 소모하는 종료 작업을 실행해야한다.

  • 정보를 데이터베이스에 저장 등
  • 영구 저장소에 데이터를 저장하는 예시(SQLite를 직접 사용 - 실제로는 Room 라이브러리를 사용해야 한다.)
override fun onStop() {
    // call the superclass method first
    super.onStop()

    // save the note's current draft, because the activity is stopping
    // and we want to be sure the current note progress isn't lost.
    val values = ContentValues().apply {
        put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText())
        put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle())
    }

    // do this update in background on an AsyncQueryHandler or equivalent
    asyncQueryHandler.startUpdate(
            token,     // int token to correlate calls
            null,      // cookie, not used here
            uri,       // The URI for the note to update.
            values,    // The map of column names and new values to apply to them.
            null,      // No SELECT criteria are used.
            null       // No WHERE columns are used.
    )
}

Activity가 중단됨 상태에서 다시 시작되어 사용자와 상호작용을 하거나 실행을 종료하고 사라진다. Activity가 다시 시작되면 시스템은 OnRestart()를 호출하고, Activity가 실행을 종료하면 onDestroy()를 호출한다.

6) onDestroy()

onDestroy()는 Activity가 소멸되기 전에 호출된다. 이 콜백이 호출되는 경우는 아래와 같다.

  • (사용자가 Activity를 완전히 닫거나 Activity에서 finish()가 호출되어)Activity가 종료되는 경우
  • 구성 변경(기기 회전 또는 멀티 윈도우 모드 등)으로 인해 시스템이 일시적으로 Activity를 소멸시키는 경우

ViewModel을 사용하면 Activity가 구성 변경으로 인해 다시 생성될 경우 데이터를 보존시켜 다음 Activity로 인스턴스에 전달할 수 있어 추가 작업이 필요하지 않다. Activity가 다시 생성되지 않을 경우 ViewModel은 onCleared() 메소드를 호출하여 모든 데이터를 정리해야 한다. (ViewModel 관련 내용은 나중에 다시 자세히 정리하기로 하자...)

Activity가 종료되는 경우 onDestroy()는 Activity가 수신하는 마지막 생명 주기 콜백이 된다. 구성 변경으로 인해 onDestroy()가 호출되는 경우 시스템이 즉시 새 활동 인스턴스를 생성한 다음, 새로운 구성에서 onCreate()를 호출한다.

onDestory() 콜백은 이전의 콜백에서 해제되지 않은 모든 리소스를 해제해야 한다!!

3. Activity 상태 및 메모리에서 제거

시스템은 RAM에 여유 공간이 필요할 때 프로세스를 종료한다. 시스템이 특정 프로세스를 종료할 가능성은 그 시점의 프로세스의 상태에 따라 달라지고, 프로세스의 상태는 프로세스에서 실행되는 Activity의 상태에 따라 달라진다.

종료될 가능성프로세스 상태액티비티 상태
최소foreground
(포커스가 있거나 포커스를 가져올 예정)
생성됨(created)
시작됨(started)
재개됨(resumed)
background
(포커스 상실)
일시중지됨(paused)
background
(보이지 않음)
중지됨(stopped)
최대Empty소멸됨(destroyed)

시스템은 메모리 공간을 확보하기 위해 Activity를 직접 종료하지 않고 Activity를 실행하는 프로세스를 종료하여 Activity 뿐만 아니라 프로세스에서 실행되는 다른 모든 작업을 함께 소멸시킨다.

안드로이드 라이프사이클 내용이 꽤 기네...ㅠ

안드로이드 라이프사이클은 안드로이드 개발을 시작하는데 있어 가장 중요한 부분이라고 생각한다. 이러한 생명주기를 알고 프로그램을 만들어야 성능이 좋으면서 계획한 기능을 제대로 수행하는 앱을 만들 수 있을 것이다. 해당 글에서는 주요 콜백과 상태 등에 대해서 정리해 보았다. 다음에는 뭘 쓰지...글에서 ViewModel 이랑 SIS 나왔으니까 이 내용 정리해볼까😃😃

📄 Reference

profile
관우로그

0개의 댓글