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

hee09·2021년 10월 25일
0

안드로이드

목록 보기
10/20
post-thumbnail

이 글은 깡쌤의 안드로이드 프로그래밍 책을 보고 작성하였습니다.

액티비티 생명주기

안드로이드 앱은 '앱티비티', '서비스', '콘텐츠 프로바이더', '브로드캐스트 리시버' 등 4개의 컴포넌트 조합으로 개발합니다. 이 중 이용빈도가 가장 높고 생명주기가 가장 복잡한 컴포넌트인 액티비티에 대해 알아보겠습니다

1 생명주기

액티비티는 실행부터 종료까지 많은 상태 변화를 거치며 상태가 변할 때마다 생명주기 함수가 자동으로 호출됩니다. 액티비티의 상태는 '활성 상태', '일시 정지 상태', '비활성 상태'가 있습니다.

  • 활성 상태(activity running) : 현재 액티비티가 화면을 점유하여 출력되고 있으며 사용자 이벤트 처리가 정상으로 처리되는 상태
  • 일시 정지 상태(pause) : 현재 액티비티가 일시적으로 사용이 불가능한 상태
  • 비활성 상태(stop) : 현재 액티비티가 다른 액티비티로 인해 화면이 완벽하게 가려진 상태

액티비티 생명주기 이미지


1.1 활성 상태

액티비티가 사용자 화면에 보이고 있으며 포커스를 가지고 있어서 사용자 이벤트에 반응할 수 있는 상태입니다. 생성된 액티비티는 onCreate() -> onStart() -> onResume() 함수가 호출되면서 활성상태가 됩니다

일반적으로 setContentView() 함수를 이용하여 액티비티 화면을 출력하는데, setContentView() 함수가 호출되는 시점이 화면 출력 순간이 아니라, onResume() 함수까지 실행하고 그 후 setContentView() 함수에서 출력한 내용이 화면에 나오는 구조이다. 따라서 onResume()이 함수가 실행되기까지 onCreate(), onStart(), onResume() 안에서 setContentView()를 호출하면 화면에 레이아웃이 출력됩니다.


1.2 일시 정지 상태

일시정시 상태는 액티비티가 여전히 화면에 보이지만, 포커스를 잃은 상태입니다. 대표적인 예가 다른 액티비티가 반투명하게 실행되거나 다이얼로그 스타일로 실행되어 여전히 자신이 화면에 보이지만, 포커스를 잃은 상태입니다. 하지만 일시 정지 상태로 멈출 때도 있지만, 대부분 정지 상태(onStop)로 전환되기 전에 호출되어 곧 정지될 것임을 나타내기 위해 사용됩니다. 일시 정시 상태가 되면 onPause() 함수가 자동으로 호출됩니다.

onPause() 함수에서는 대부분 다음의 내용을 구현하기를 권장합니다

  • 애니메이션이나 CPU 소비를 야기할 수 있는 기타 지속적인 작업 정지
  • GPS와 같은 센서값 수신, 서버 네트워킹 등 액티비티가 일시 정지된 동안 불필요한 동작 정지

이러한 정지를 onPause() 함수에서 구현하는 이유는 위와 같은 작업은 정지 상태에서 더 이상 실행될 필요가 없는 경우이므로 onPause() 함수에서 멈추었다가 다시 활성 상태로 변경될 때 동작하게 합니다.

  • 일시 정시 상태의 예제

1.3 비활성 상태

비활성 상태는 다른 액티비티로 인해 화면이 완전히 가려진 상태입니다. 예로는 다른 액티비티로 화면이 전환되어 안 보이는 경우입니다. 이렇게 되면 onPause() -> onStop() 함수까지 호출됩니다. 이후 화면을 가렸던 액티비티에서 뒤로가기 버튼등으로 화면을 가렸던 액티비티가 사라지면 다시 활성상태로 전환되는데, 이때는 onRestart() -> onStart() -> onResume() 함수가 차례로 호출됩니다.

MainActivitySecondActivityBack to MainActivity
First ImageSecond ImageThird Image
  • 위의 예제는 처음 실행된 액티비티에서 활성 상태로 있다가 사용자가 버튼을 눌러 다른 액티비티로 전환한 후 뒤로가기 버튼을 눌러 원래의 액티비티로 돌아왔을 때의 생명주기입니다.

1.2 액티비티 상태 저장

앱이 종료될 경우의 데이터 저장은 데이터베이스와 같은 데이터 영속화를 사용하면 됩니다. 하지만 앱이 아닌 액티비티가 의도치 않게 종료될 수가 있습니다. 대표적인 예가 화면 회전입니다. 화면 회전을 하게되면 액티비티가 종료되었다가 다시 시작합니다. 이때, 생명주기는 onResume()까지 호출 된 액티비티에서 화면 회전이 발생하면 onPause() -> onStop() -> onDestroy()까지 호출되어 종료됩니다. 그 후 시스템에서 다시 onCreate() -> onStart() -> onResume()까지 호출합니다. 화면은 다시 나타나지만 만약 onResume()이 실행된 후에 발생한 이벤트나 네트워킹에 의해 데이터가 발생하였다면 모두 유실됩니다.

화면 회전 전화면 회전 이후
First ImageSecond Image
  • 위의 예제와 같이 사용자가 Count 버튼을 눌러 값을 증가시킨 후 만약 화면을 회전하면 화면은 정상적으로 나오지만, onDestroy()까지 갔다가 다시 나타난 것이므로 데이터가 사라집니다.

이를 위해 데이터를 저장했다가 다시 액티비티가 시작될 때 복원하여 사용하기 위한 생명주기 함수들이 있습니다.

// 액티비티 종료되는 상황에 액티비티의 데이터를 저장
// Bundle은 컴포넡트의 데이터를 저장하기 위한 일종의 Map 객체
// Key - Value 형식으로 데잉터를 저장
override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)

    outState.putString("data1", "hello")
    outState.putInt("data2", 100)
}

우선 onSaveInstanceState 입니다. 이 함수는 onPause() 함수 호출 후 자동으로 호출되며, 이 함수에서 데이터를 저장합니다. 저장하는 방법은 매개변수로 전달되는 Bundle 객체를 사용하면 됩니다. Bundle은 컴포넌트의 데이터를 저장하기 위한 일종의 Map 객체로, 데이터를 Key - Value 형식으로 담아주면 됩니다.

// 액티비티가 다시 시작하면서 호출되는데 매개변수로 Bundle 객체가 넘어온다.
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
    super.onRestoreInstanceState(savedInstanceState)

    // Bundle 객체를 사용하여 해당하는 값 가져오기
    val data1 = savedInstanceState.getString("data1")
    val data2 = savedInstanceState.getInt("data2")

    val toast = Toast.makeText(this, "$data1 : $data2", Toast.LENGTH_SHORT)
    toast.show()
}

// Bundle 객체가 매개변수로 넘어온다
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val data1 = savedInstanceState?.getString("data1")
    val data2 = savedInstanceState?.getInt("data2")

    val toast = Toast.makeText(this, "$data1 : $data2", Toast.LENGTH_SHORT)
    toast.show()
}

액티비티가 다시 시작하면서 onCreate()와 onResotreInstanceState()가 호출되는데, 저장된 데이터를 Bundle에 담아 매개변수로 전달합니다. 이 함수들을 사용하여 저장된 값을 불러옵니다.


  • 참조
    onSaveInstanceState()와 onRestoreInstanceState() 함수들은 API Level 21 버전부터 매개변수가 두 개인 함수가 추가되었습니다. 매개변수가 하나인 함수들과의 차이는 호출되는 시점에 있습니다.
override fun onSaveInstanceState(outState: Bundle, outPersistentState: PersistableBundle) {
    super.onSaveInstanceState(outState, outPersistentState)
}

override fun onRestoreInstanceState(
    savedInstanceState: Bundle?,
    persistentState: PersistableBundle?
) {
    super.onRestoreInstanceState(savedInstanceState, persistentState)
}

매개변수가 하나인 onSaveInstanceState() 함수는 onPause() 함수가 호출된 후 자동으로 호출됩니다. 그런데 화면이 회전될 때가 아닌 다른 액티비티로 화면이 전활될 때도 호출됩니다. 하지만 매개변수가 두 개인 onSaveInstanceState() 함수는 onPuase() 함수가 호출 후 무조건 호출되지 않고 화면 회전처럼 액티비티가 종료할 때만 호출됩니다.

또한, 매개변수가 하나인 onRestoreInstanceState() 함수는 화면이 회전하여 액티비티가 다시 시작될 때 무조건 호출되지만, 매개변수가 두 개인 onRestoreInstanceState() 함수는 Bundle에 저장된 데이터가 없으면 호출되지 않습니다.

profile
되새기기 위해 기록

0개의 댓글