Activity LifeCycle (액티비티 생명주기)

지훈·2021년 11월 2일
0

Activity LifeCycle(액티비티 생명주기)

생명주기란?

사용자가 앱을 사용하다가, 앱에서 나가고, 앱으로 다시 돌아오면 액티비티 인스턴스는 수명주기 안에서 "상태"가 바뀐다. Activity 클래스에서는 액티비티가 상태 변화를 알아차릴 수 있는 콜백을 제공한다.
즉, 안드로이드 앱이 실행된 후 다른 액티비티 화면으로 전환되거나, 화면이 꺼짐, 폰 상태바를 내려서 액티비티가 가려짐, 혹은 앱이 종료 될 때 등을 상태변화가 일어난다고 한다. 이러한 상태 변화가 있을 때마다 화면에 보이는 액티비티의 생명주기 메서드를 호출해서 상태 변화를 알려준다.

사용자가 액티비티를 벗어났다가 다시 돌아왔을 때 액티비티가 작동하는 방식을 생명주기 메서드에서 선언할 수 있다.
예) 넷플릭스 동영상 플레이어 사용중, 사용자가 다른 앱으로 전환할 때 동영상 일시중지, 네트워크 연결 종료. 다시 돌아오면 네트워크를 다시 연결, 일시중지했던 지점에서 다시 동영상 시작.
생명 주기 콜백을 잘 구현하면 앱에서 아래 문제가 발생하지 않도록 예방할 수 있다.

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

아래는 안드로이드 Activity LifeCycle의 조직도이다.

생명주기 메서드 (Preview)


메서드 이름액티비티 상태:설명:
onCreate()만들어짐액티비티가 생성될 때 최초로 실행되
onStart()화면에 나타남화면에 보여지기 시작
onResume()현재 실행 중, 화면에 나타남.화면에 나타나 있고 실행 중임.
onPause()화면이 가려짐.액티비티 "화면의 일부"가 다른 액티비티에 가려짐
onStop()화면이 없어짐.다른 액티비티의 실행으로 완전히 가려짐
onDestroy()종료됨액티비티 종료됨.

사용자가 액티비티를 벗어나기 시작하면 시스템은 맥티비티 해체할 메서드를 호출한다. 어떤 경우에는 부분적으로 해체한다. 이 때 액티비티는 여전히 메모리 안에 있으면서 백그라운드로 이동하며(사용자가 다른 앱으로 전환할 경우) 다시 포그라운드(Foreground)로 돌아올 수 있다. 사용자가 해당 액티비티로 다시 돌아오는 경우 종료한 지점에서 다시 액티비티가 시작된다. 몇가지 예외를 제외하고 앱은 백그라운드에서 실행될 때 액티비티를 실행 할 수 없다.
시스템은 그 시점의 액티비티 상태에 따라 특정 프로세스와 그 액티비티를 함께 종료할 지를 결정한다.

예) 게임을 하다가 카톡을 봐야 해서 화면 상단 상태바를 내림. 내리면서 화면의 일부가 가려지고 있을 때 : onPause()
상태 바에 와있던 카톡 알림을 눌러 카카오톡 앱으로 이동. 기존 게임 앱은 화면에서 더이상 보이지 않음 : onStop()
곧 바로 앱으로 돌아온다면 재시작되어 onStart() {onRestart}
장시간 사용하지 않다가 앱을 실행시키면 이미 프로세스가 죽은 상태이므로 onCreate()

아래에서 더 자세히 알아보자.

생명주기 호출

액티비티 인스턴스를 생성하면 그 동시에 생성 관련 생명주기 메서드가 순차적으로 호출된다. 종료 시 소멸과 관련된 생명주기 메서드가 순차적으로 호출된다.

액티비티 생성시

onCreate() : 생성된 화면 요소를 메모리에 로드
onStart(), onResume() : 화면의 요소를 나타내고 사용자와 상호작용 시작 (Resumed 상태)

액티비티를 화면에서 제거

onPause(), onStop() -> 뒤로 가기, finish()를 실행할 때 동시 실행
onDestroy() -> 최정적으로 액티비티가 메모리에서 제거됨.

액티비티를 종료하지 않고 다른 액티비티 실행

onPause(), onStop() -> 현재 액티비티를 종료하지 않고 새로운 액티비티가 만들어 질때 (Stopped 상태)
onStart(), onResume() -> 두 메서드가 연속적으로 실행되고 Resumed 상태로 변경됨.

**액티비티를 종료하지 않거나, 액티비티가 모두 가려지지 않을 때 다른 액티비티 실행

onPause() -> 완전히 사라진 것은 아니므로 (Paused 상태)
onResume() -> 정지한 것이 아니므로 onStrat()를 거치지 않고 바로 onResume으로. (Resumed 상태)

생명주기 메서드(콜백) Detail

onCreate()

시스템이 먼저 액티비티를 생성할 때 실행됨. 필수적으로 구현해야 한다. 액티비티가 생성되면 상태는 (Crated) 상태. 맥티비티의 전체 생명주기 동안 한 번만 발생해야 하는 기본 어플리케이션 시작 로직 실행.
예를 들어 onCreate() 구현하면 목록에 데이터를 Binding, 액티비티를 ViewModel 과 연결. 일부 클래스의 클래수 변수를 인스턴스화 할 수 있다. 이 메서드는 savedInstaceState 라는 매개변수를 수신하는데 이는 액티빙티의 이전 저장 상태가 포함된 Bundle 객체이다. 이번에 처음 생성된 액티비티라면 객체의 값은 null이다.
액티비티의 생명주기에 연결된 생명주기-aware component가 있으면
ON_CREATE 이벤트를 수신 -> OnLifeCycleEvent 메서드 호출

XML파일을 정의하고 setContnetView()에 전달하여 화면에 표시할 수 있다.
혹은 액티비티 코드에서 새 View 객체를 생성하고, 그 View를 ViewGroup에 넣어서 뷰 계층 구조를 만들 수 있다. 그 후 ViewGroup을 setContentView()에 전달해서 그 레이아웃을 사용한다.
예) 액티비티 코드에서 View 객체를 시간에 따라 바뀌는 것으로 생성하고 그 View를 LinearLayout에 넣는다. 해당 setContnetView(해당 LinearLayout) 하여 화면에 표시할 수 있다.

onCreate() 상태에 머무리지 않고 onCreate() 메소드 후 바로 onStart()와 onResume() 메소드를 실행시킨다.

onStart()

액티비티가 Start 상태 -> onStart() 호출. 액티비티가 사용자에게 표시되고 앱의 액티비티를 Foreground에 보내 상호작용하게 함.
이 액비티비의 생명주기에 연결된 모든 생명주기-aware component는 ON_START 이벤트를 수신
마찬가지로 액티비티는 Start 상태에 머무리지 않고 이 콜백이 완료되면 액티비티가 onResume() 메서드 호출, RESUMED 상태에 들어간다.

onResume()

액티비티가 RESUME 상태에 들어가면 포그라운드에 표시되고 onResume() 콜백 호출. 이 상태에서 앱은 사용자와 상호작용한다. 특정 이벤트 발생(전화가 오거나, 사용자가 다른 액티비티로 이
동, 기기 화면이 꺼짐 등) 전까지 앱은 이 상태에 머문다.
이 액비티비의 생명주기에 연결된 모든 생명주기-aware component는 ON_RESUME 이벤트를 수신. 개발자는 이 상태에서 생명주기 component가 포그라운드에서 사용자에게 보이는 동안 실행해야 하는 모든 기능을 활성화할 수 있다. ( 카메라 미리보기 시작 등)
방해되는 이벤트가 발생 -> Pasued 상태 ->onPause() 콜백 호출 -> 다시 Resumed 상태로 돌아오면 -> 다시 onResume() 콜백 호출.
따라서 onResume()을 구현하여 onPause() 일 때 해제하는 구성요소를 초기화하고 액티비티가 Resumed 상태로 전환될 때 마다 필요한 다른 초기화 작업도 수행해야 한다.

class CameraComponent : LifecycleObserver {
    ...

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun initializeCamera() {
        if (camera == null) {
            getCamera()
        }
    }
    ...
}

위 코드는 LifecycleObserver가 ON_RESUME 이벤트를 수신하면 카메라 초기화
그런데 멀티 윈도우모드(화면분할)에서는 Paused 상태일 때도 완전히 보일 수 있다. 멀티윈도우 모드일 때 다른 앱을 선택하면 onPause된다. 그러므로 액티비티가 Paused 상태지만 표시되어 있는동안 활성화시키고 싶다면 ON_START 이벤트가 실행된 이후에 카메라를 초기화해야 한다. 액티비티가 Paused 일 때 기능을 활성화하면 멀티 윈도우 모드에서 Resumed 상태인 다른 앱이 이 기능에 접근하지 못할 수도 있다. 그리고 이 경우 전반적인 사용자 환경이 실질적으로 저하될 수 있다.

멀티 윈도우 지원과 생명주기-aware component로 생명주기 처리하는 방법은 나중에 더 구체적으로 알아보자. 링크 참조

onPause()

  • 시스템은 사용자가 액티비티를 떠날 떄의 첫번째 신호로 이 메서드를 호출한다. (항상 Destroyed는 아님)
  • 액티비티가 포그라운드에 없다는 것을 의미한다.(멀티 윈도우 모드 제외)
  • onPause() 메서드를 사용하여 액티비티가 Paused 상태일 때 실행을 멈추고 잠시 후 다시 시작할 작업을 일시중지하거나 조정한다.

액티비티가 Paused 상태에 들어가는 이유

  • 일부 이벤트가 앱 실행을 방해 (가장 일반적)
  • 멀티 윈도우 모드에서 보이는 여러 앱 중 포커스를 가진 앱 제외하고 모두 Paused 됨
  • Dialog 같은 새로운 반투명 액티비티가 열림. (기존 액티비티가 부분적으로 보이지만 포커스 상태 아니면 Paused)

액티비티가 Paused 상태가 되면 생명주기의 연결된 모든 생명주기-aware component가 ON_PAUSE 이벤트를 수신. 구성요소가 포그라운드에 있지 않을 때 실행할 필요가 없는 기능을 모두 정지할 수 있다.

또한 배터리나 데이터를 많이 잡아먹는 기능(GPS 등)의기능 혹은 액티비티가 Paused 상태이고 사용자가 필요로 하지 않을 때의 모든 리소스를 onPause() 메서드를 사용해 해제할 수 있다. 그런데 멀티윈도우 모드일 때는 Paused 상태여도 화면에 보일 수 있으므로 onStop() 상태일 때 해제하는 것으로 하는 것이 바람직하다.

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

위 코드는 LifecycleObserver가 ON_PAUSE 이벤트에 응답하는 예. ON_RESUME 이벤트가 수신된 후 초기화된 카메라 해제.

onPuase() 는 아주 잠깐 실행되므로 앱 | 사용자 데이터 저장, 네트워크 호출, 데이터베이스 처리를 실행하면 안된다. 대신 부하가 큰 종료 작업은 onStop() 상태일 때 실행되야 한다.

onPause() 메서드의 실행이 완료되더라도 액티비티가 Paused 상태로 남을 수 있다. 오히려 액티비티는 다시 시작되거나 완전히 보이지 않게 될 때까지 이 상태에 머문다.
액티비티 Paused -> Resumed == 액티비티 인스턴스가 메모리에 -> 다시 인스턴스 호출.
이 때는 최상위 상태가 Resumed 상태인 콜백 메서드 중에 생성된 component는 다시 초기화할 필요가 없다. 액티비티가 완전히 보이지 않게 되면 onStop() 호출한다.

onStop()

액티비티가 사용자에게 더이상 표시되지 않으면 Stop 상태. onStop() 호출. (예 새로 실행된 액티비티가 화면 전체를 차지할 경우) 액티비티가 Stop 상태로 전환하면 생명주기와 연결된 모든 생명주기-aware component는 화면에 보이지 않을 때 실행할 필요가 없는 기능을 모두 정지할 수 있다.

onStop() 메서드에서는 앱이 사용자에게 보이지 않는 동안 앱은 불필요한 리소스를 해제하거나 조정해야 한다.(애니메이션을 일시중지 | 세밀한 위치 업데이트에서 대략적인 업데이트로 전환 등) onPause() 대신 onStop()을 사용하면 사용자가 멀티 윈도우 모드에서 액티비티가 보여도 UI 관련 작업이 계속 진행됨.

또한 onStop()을 사용하면 CPU를 많이 사용하는 종료 작업 이 때 해야 한다.(정보를 데이터베이스에 저장할 시기가 없었다면 onStop() 중에 실행!) 아래 예는 초안 내용을 영구 저장소에 저장하는 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 token
            null,      // 여기서는 사용되지 않는 cookie
            uri,       // 업데이트할 노트의 URI
            values,    // 열 이름과 여기에 적용할 새 값의 map
            null,      // 사용될 기준이 선택되지 않음
            null       // 열이 사용될 장소가 선택되지 않음.
    )
}

액티비티가 Stop 상태에 들어가면 Activity 객체는 메모리 안에 머문다. 이 객체가 모든 상태 및 멤버 정보를 관리하지만 창 관리자와 연결되어 있지 않다. 액티비티가 다시 시작되면 이 정보를 다시 호출한다. 최상위 상태가 Resumed 상태인 콜백 메서드 일 때 생성된 구성요소는 다시 초기화할 필요는 없다. 또 시스템은 레이아웃에 있는 각 View 객체의 현재 상태도 기록한다.
따라서 사용자가 EditText 위젯에 텍스트를 입력하면 해당 내용이 저장되기 때문에 이를 따로 저장하거나 복원할 필요없다.

액티비티가 Stop 상태에서 다시 시작되어 사용자와 상호작용 | 종료되고 Destroy된다. 액티비티가 다시 시작되면 onRestart() 호출, 액티비티가 실행을 종료하면 onDestroy() 호출.

onDestroy()

onDestroy()는 액티비티가 소멸되기 전에 호출된다.

  • 사용자가 액티비티를 완전히 닫거나 finish()가 호출되어 액티비티 종료될 때
  • 구성변경(기기회전 | 멀티 윈도우 모드)로 인해 시스템이 일시적으로 액티비티 Destroy 때
    이 때 이 액티비티의 생명주기와 연결된 모든 생명주기-aware component는 ON_DESTROY 이벤트 수신. 이 때 생명주기 구성요소는 액티비티 destroy 전에 필요한 것들을 정리할 수 있다.

액티비티 Destroy 이유를 결정하는 로직을 입력하는 대신 ViewModel 객체를 사용해 액티비티의 관련 뷰 데이터를 포함해야 한다. 액티비티가 구성변경으로 인해 Destroy 후 Restart하면 ViewModel은 그대로 보존되어 다음 액티비티에 전달돼서 추가 작업 불필요하다. 액티비티가 Restart하지 않으면 ViewModel은 onCleared() 메서드를 호출하여 액티비티 Destroy 전에 모든 데이터를 정리해야 한다.

위 두 상황은 isFinishing() 메서드로 구분할 수 있다.
액티비티 Stop 시 onDestroy() 는 액티비티가 수신하는 마지막 생명주기 콜백.
구성변경으로 onDestroy() 호출될 시 시스템이 바로 새 액티비티 인스턴스 생성, 새로운 구성에서 새로운 인스턴스에 관한 onCreate() 호출.

onDestroy() 콜백은 이전 콜백에서 아직 해제되지 않은 모든 리소스를 해제해야 한다.

Activity 상태 및 메모리에서 killed

시스템은 RAM에 여유공간이 필요하면 프로세스를 kill 한다. 이 가능성은 프로세스의 상태에 따라 다르다.

kill 가능성프로세스 상태Activitiy 상태
최소Foreground (포커스가 있거나 포커스를 가져올 예정)Created, Started, Resumed
중간Background (포커스 상실)Paused
최대Background(보이지 않음)
Empty
Stopped
Destroyed

시스템은 메모리 공간 확보를 위해 액티비티를 직접 종료하지 않고 프로세스를 kill한다. 그러면 프로세스가 담당하는 모든 액티비티가 kill된다.

요약

앱 개발 환경에서 액티비티뿐 아니라 생명주기를 이해하는 것은 매우 중요하다. 이를 통해 앱이 특정시점에 어떤 이벤트를 받으면 어떻게 반응할지 등을 결정할 수 있으므로 앞으로 프로그래밍할 때 자주 쓰이게 될 것이다.

출처 : https://bbaktaeho-95.tistory.com/62
https://thinkground.studio/android-%EC%95%A1%ED%8B%B0%EB%B9%84%ED%8B%B0-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0-activity-lifecycle/
https://brunch.co.kr/@mystoryg/80

profile
안드로이드 개발 공부

0개의 댓글