액티비티 수명주기

kosdjs·2025년 6월 2일
0

Android

목록 보기
4/24
  • 사용자가 앱을 탐색하고, 앱을 나가고, 다시 돌아올 때, 앱의 Activity 인스턴스는 수명주기(lifecycle)내에서 여러 상태를 오가게 됨

  • Activity 클래스는 상태가 변경되거나 시스템이 액티비티를 생성, 중지, 재개, 또는 액티비티가 속한 프로세스를 파괴할 때를 알려주는 여러 콜백(callback)을 제공

  • 각 콜백을 통해 적절한 시점에 올바른 작업을 하고, 전환을 올바르게 처리하면 앱이 더 견고하고 성능이 좋아짐

  • 수명주기 콜백을 잘 구현하지 못한다면 일어날 수 있는 문제들

    • 사용자가 전화가 오거나 다른 앱으로 전환할 때 앱이 강제 종료되는 문제
    • 사용자가 앱을 적극적으로 사용하지 않을 때 불필요하게 시스템 리소스를 소모하는 문제
    • 사용자가 앱을 떠났다가 돌아왔을 때 진행 상황이 사라지는 문제
    • 화면이 가로/세로로 회전할 때 앱이 강제 종료되거나 진행 상황이 사라지는 문제

액티비티 수명주기 개념

  • Activity 클래스는 수명주기 단계 전환을 위해 6가지 핵심 콜백을 제공함

  • 사용자가 액티비티를 떠나기 시작하면 시스템은 액티비티를 해체하는 메소드들을 호출함, 경우에 따라 액티비티가 부분적으로 해체되어 메모리에 남아 있을 수 있음.

    • 예시: 사용자가 다른 앱으로 전환했다가 다시 돌아오면 액티비티의 이전 상태에서 다시 시작할 수 있음
  • 모든 수명주기 메소드를 구현할 필요는 없지만 각 메소드의 의미를 이해하고 사용자의 기대에 맞게 메소드를 구현하는 것이 중요함.

onCreate()

  • 반드시 구현해야 하는 콜백이며, 시스템이 처음 액티비티를 생성할 때 호출됨

  • 액티비티 전체 수명 동안 한 번만 실행되는 기본적인 앱 초기화 로직을 수행

    • 예시: 데이터 바인딩, ViewModel 연결, 멤버 변수 초기화 등
  • savedInstanceState 라는 이전에 저장된 상태를 담은 Bundle 객체인 파라미터가 있음

    • 액티비티를 처음 생성할 때는 null로 들어옴
  • 수명주기 인식(lifecycle-aware) 컴포넌트가 액티비티의 수명 주기에 연결되어 있다면 ON_CREATE 이벤트를 받음

    • @OnLifecycleEvent로 어노테이션된 메서드가 호출되어 컴포넌트가 Created 상태에서 필요한 초기화 코드를 수행할 수 있음
  • onCreate() 가 끝나면 액티비티는 Created 상태에 머무르지 않고 곧바로 Started 상태로 진입하며, onStart()onResume() 이 연달아 호출됨

onCreate() 예시

lateinit var textView: TextView

// Activity 인스턴스의 임시 상태
var gameState: String? = null

override fun onCreate(savedInstanceState: Bundle?) {
    // 슈퍼클래스의 onCreate를 호출하여 뷰 계층 구조 등
    // 액티비티의 생성을 완료합니다.
    super.onCreate(savedInstanceState)

    // 인스턴스 상태를 복원합니다.
    gameState = savedInstanceState?.getString(GAME_STATE_KEY)

    // 이 액티비티의 사용자 인터페이스 레이아웃을 설정합니다.
    // 레이아웃은 프로젝트의 res/layout/main_activity.xml 파일에 정의되어 있습니다.
    setContentView(R.layout.main_activity)

    // 멤버 TextView를 초기화하여 나중에 사용할 수 있도록 합니다.
    textView = findViewById(R.id.text_view)
}

// 이 콜백은 onSaveInstanceState()를 사용해 이전에 저장된 인스턴스가 있을 때만 호출됩니다.
// 일부 상태는 onCreate()에서 복원됩니다. 다른 상태는 필요에 따라 여기서 복원할 수 있으며,
// onStart()가 완료된 후에 사용할 수도 있습니다.
// savedInstanceState Bundle은 onCreate()에서 사용된 것과 동일합니다.
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
    textView.text = savedInstanceState?.getString(TEXT_VIEW_KEY)
}

// 액티비티가 일시적으로 파괴될 수 있을 때 호출되며, 여기에서 인스턴스 상태를 저장합니다.
override fun onSaveInstanceState(outState: Bundle?) {
    outState?.run {
        putString(GAME_STATE_KEY, gameState)
        putString(TEXT_VIEW_KEY, textView.text.toString())
    }
    // 슈퍼클래스를 호출하여 뷰 계층 구조의 상태도 저장합니다.
    super.onSaveInstanceState(outState)
}

onStart()

  • 액티비티가 Started 상태에 진입하면 시스템이 호출하는 콜백

  • 이 때 액티비티는 사용자에게 보이게 되고, 앱은 액티비티가 포그라운드에서 상호작용할 준비를 함

  • 이 메소드에서 UI를 유지하는 코드를 초기화할 수 있음

  • Started 상태에 진입하면, 수명주기 인식 컴포넌트는 ON_START 이벤트를 받음

  • onStart() 메소드는 빠르게 끝남, 메소드가 끝나면 액티비티는 Resumed 상태로 진입하며, 곧바로 onResume() 이 호출

onResume()

  • 액티비티가 Resumed 상태에 진입하면 시스템이 액티비티를 포그라운드로 올리고 호출하는 콜백

  • 이 상태는 앱이 사용자와 상호작용하는 상태, 포커스를 잃을 때까지 이 상태가 유지됨

    • 전화가 오거나, 사용자가 다른 액티비티로 이동하거나, 화면이 꺼지는 등의 일이 일어나면 포커스를 잃음
  • 이 메소드에서 카메라 프리뷰를 시작하는 등 포그라운드에서 동작해야 하는 기능을 활성화할 수 있음

  • 초기화 시점과 해제 시점을 맞춰야 함, ON_START 이벤트에서 초기화 하면 ON_STOP 이벤트에서 해제해야 하고, ON_RESUME 이벤트에서 초기화 하면 ON_PAUSE 이벤트에서 해제해야 함

  • Resumed 상태에 진입하면, 수명주기 인식 컴포넌트는 ON_RESUME 이벤트를 받음

  • 중단(Interruptive) 이벤트가 발생하면 액티비티는 Paused 상태로 진입하며,
    시스템이 onPause() 를 호출함

  • 액티비티가 Paused 상태에서 Resumed 상태로 다시 돌아갈 때 시스템은 onResume() 메소드를 다시 호출함, 따라서 onPause() 에서 해제(Release)하는 컴포넌트를 onResume() 에서 초기화해야 함

ON_RESUME 이벤트를 받는 수명주기 인식 카메라 컴포넌트 예시

class CameraComponent : LifecycleObserver {
    ...
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun initializeCamera() {
        if (camera == null) {
            getCamera()
        }
    }
    ...
}
  • 멀티 윈도우 모드의 경우 액티비티가 보이는 상태에서도 Paused 상태로 진입할 수 있음

    이 때 카메라를 사용하는 액티비티이고, 보이는 동안 카메라를 계속 사용하고 싶으면 ON_START 이벤트에 카메라를 초기화하면 되지만 이럴 경우에 다른 Resumed 상태인 앱이 카메라를 사용하지 못하게 막을 수 있음. 따라서 카메라와 같은 공유 시스템 리소스를 멀티 윈도우 모드에서 어떤 생명주기에 접근할 것인지 조심히 생각해야 함.

onPause()

  • 이 메소드는 사용자가 액티비티를 떠난다는 첫 표시로 시스템이 호출함, 호출된다고 항상 액티비티가 파괴되는 것은 아님, 멀티 윈도우 모드에서는 앱이 포그라운드에 있지 않아도 표시될 수 있음

  • 이 상태로 들어가는 몇가지 이유

    • onResume() 에서 설명했듯, 앱 실행을 중단(Interrupt)하는 이벤트가 있을 때 액티비티를 Paused 상태로 만듬. 가장 일반적인 이유
    • 멀티 윈도우 모드에서 한 앱만 포커스를 얻을 수 있음. 나머지 앱은 전부 시스템이 Paused 상태로 만듬
    • 다이얼로그와 같은 반투명한 새 액티비티를 현재 액티비티 위에 띄울 때, 액티비티는 부분적으로 보이지만 포커스를 잃었기 때문에 Paused 상태가 됨
  • 이 메소드에서 포그라운드 상태가 아닐 때 실행해야 할 필요가 없는 컴포넌트를 멈출 수 있음, 이 상태에서 필요하지 않은 시스템 자원이나 센서(예: GPS)에 대한 핸들과 같이 배터리 수명에 영향을 주는 자원을 해제하는 데 사용할 수 있음

  • Paused 상태에 진입하면, 수명주기 인식 컴포넌트는 ON_PAUSE 이벤트를 받음

  • 멀티 윈도우 모드에서는 앱이 보일 때 이 상태에 진입할 수 있기 때문에 UI 관련한 자원을 onStop() 에서 해제하는 것을 고려해 멀티 윈도우 모드를 더 잘 지원할 수 있음

  • onPause() 메소드는 매우 짧게 실행되어야 함, 따라서 오래 걸릴 수 있는 사용자 데이터 저장, 네트워크 호출, 데이터베이스 트랜잭션을 이 메소드에서 하면 안 됨, 이런 작업은 onStop() 에서 하길 권장함

  • 액티비티가 Paused 상태에서 Resumed 상태로 돌아갈 때 시스템이 기존 인스턴스를 메모리에 저장하기 때문에 Resumed 상태까지 생성된 컴포넌트를 다시 초기화할 필요가 없음

  • 액티비티가 완전히 보이지 않게 되면 onStop() 을 호출함

ON_PAUSE 이벤트를 받는 수명주기 인식 카메라 컴포넌트 예시

class CameraComponent : LifecycleObserver {
    ...
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun releaseCamera() {
        camera?.release()
        camera = null
    }
    ...
}

onStop()

  • 액티비티가 사용자에게 더 이상 보이지 않게 되면 Stopped 상태에 진입함, 이 때 시스템이 onStop() 메소드를 호출함, 새로 시작된 액티비티가 화면 전체를 덮을 때에 해당함

  • Stopped 상태에 진입하면, 수명주기 인식 컴포넌트는 ON_STOP 이벤트를 받음

  • 이 메소드에서 사용자에게 앱이 보이지 않을 때 더 이상 필요하지 않은 자원을 해제하거나 조정할 수 있음, 상대적으로 무거운 종료 작업을 처리하기 좋은 메소드

  • 액티비티가 이 상태에 진입할 때 액티비티 객체는 메모리에 남아있지만 윈도우 매니저에 연결되지 않음, 액티비티가 다시 재개될 때 이 객체를 이용해 기존 상태와 멤버 정보를 불러옴, 따라서 Resumed 상태까지 생성된 컴포넌트를 다시 초기화할 필요가 없음

노트를 데이터베이스에 저장하는 onStop() 예시

override fun onStop() {
    // 먼저 슈퍼클래스 메서드를 호출합니다.
    super.onStop()

    // 액티비티가 중지되기 때문에 현재 노트의 임시 저장본을 저장합니다
	// 현재 작성 중인 노트의 진행 상황이 사라지지 않도록 하기 위함입니다.
    val values = ContentValues().apply {
        put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText())
        put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle())
    }

    // 이 업데이트 작업은 AsyncQueryHandler나 이와 유사한 백그라운드 작업에서 수행하세요.
    asyncQueryHandler.startUpdate(
            token,     // 호출을 연관시키기 위한 int 토큰
            null,      // 쿠키(여기서는 사용하지 않음)
            uri,       // 업데이트할 노트의 URI
            values,    // 적용할 컬럼 이름과 새로운 값의 맵
            null,      // SELECT 조건 없음
            null       // WHERE 절에 사용할 컬럼 없음
    )
}

참고: 액티비티가 Stopped 상태일 때, 시스템에 메모리가 부족하면 액티비티를 포함한 프로세스를 종료할 수 있음. 프로세스를 종료하더라도 시스템은 View 객체 상태를 Bundle에 저장해 다시 액티비티를 불러올 때 복원할 수 있음

onDestroy()

  • 이 메소드는 액티비티가 파괴되기 직전에 호출됨. 시스템이 이 메소드를 호출하는 이유는 두 가지임

    • 사용자가 액티비티를 완전히 닫거나 finish() 가 호출된 경우
    • 화면 회전이나, 멀티 윈도우 모드 진입과 같은 설정 변경으로 인해 시스템이 임시로 액티비티를 파괴하는 경우
  • Destroyed 상태에 진입하면, 수명주기 인식 컴포넌트는 ON_STOP 이벤트를 받음

  • 이 메소드에서 이전 콜백에서 정리하지 못한 자원을 모두 해제해야 함

  • 액티비티 파괴 이유에 따라 동작이 달라지므로 이 메소드에 직접 코드를 적기보다 ViewModel 을 사용하길 권장함

  • 이 상태는 액티비티가 설정 변경에 따라 재생성되거나, 아닌 경우로 나뉨.

  • 액티비티의 isFinishing() 메소드를 사용하면 어떤 경우인지 구분 가능

  • 액티비티가 설정 변경에 따라 재생성된다면 ViewModel 은 새롭게 생성되는 액티비티에 전달되기 때문에 따로 해야할 것이 없음

  • 액티비티가 재생성되지 않는 경우라면 ViewModelonCleared() 메소드를 사용하면 된다.

원문: https://developer.android.com/guide/components/activities/activity-lifecycle

0개의 댓글