/* 본 게시물은 '이것이 안드로이드다 with Kotlin | 고돈호 지음' 의 내용을 토대로 작성되었습니다. */
#액티비티 생명 주기
안드로이드는 앱이 실행된 후 다른 액티비티 화면으로 전환되거나, 스마트폰 화면이 꺼지거나 혹은 앱이 종료될 때와 같이 상태 변화가 있을 때마다 화면에 보여지는 액티비티의 생명 주기 메서드를 호출해서 상태 변화를 알려준다.
# 액티비티 생명 주기 메서드
메서드 | 설명 | 다음 메서드 |
onCreate() | 액티비티가 생성될 때 호출되며 사용자 인터페이스 초기화에 사용됨. | onStart() |
onRestart() | 액티비티가 멈췄다가 다시 시작되기 바로 전에 호출됨. | onStart() |
onStart() | 액티비티가 사용자에게 보여지기 바로 직전에 호출됨. | onResume() 또는 onStop() |
onResume() | 액티비티가 사용자와 상호작용하기 바로 전에 호출됨. | onPause() |
onPause() | 다른 액티비티가 보여질 때 호출됨. 데이터 저장, 스레드 중지 등의 처리를 하기에 적당한 메소드. | onResume() 또는 onStop() |
onStop() | 액티비티가 더이상 사용자에게 보여지지 않을 때 호출됨. 메모리가 부족할 경우에는 onStop() 메소드가 호출되지 않을 수도 있음. | onRestart() 또는 onDestroy() |
onDestroy() | 액티비티가 소멸될 때 호출됨. finish() 메소드가 호출되거나 시스템이 메모리 확보를 위해 액티비티를 제거할 때 호출됨. | 없음 |
※ onStop(), onDestory()는 호출되지 않을 수도 있음
※ 각각의 메서드는 상태 변화에 따라 안드로이드가 호출하므로 activity.onStop()의 형태로 직접 호출해서는 안됨.
# 생명 주기 콜백의 이해
액티비티는 인스턴스 생성과 동시에 생성과 관련된 생명 주기 메서드가 순차적으로 호출된다. 그리고 finish() 메서드나 뒤로가기로 액티비티를 종료하면 소멸과 관련된 생명 주기 메서드가 순차적으로 호출된다.
- 먼저 액티비티를 생성해서 화면에 나타내는 생명 주기를 살펴보자. 액티비티는 onCreate() 메서드로 생성된 다음 화면 구성요소를 메모리에 로드하고, onStart()와 onResume()에서 화면의 구성요소를 나타내고 사용자와의 상호작용을 시작한다. onResume() 메서드 다음의 상태 표시인 Resumed(실행중)는 액티비티가 화면에서 실행되고 있음을 의미한다.
- 다음으로 액티비티를 화면에서 제거하는 생명 주기를 살펴보자. 액티비티를 벗어나게 되면 소멸과 관련된 생명 주기가 시작되는데 뒤로가기를 하거나 finish() 메서드로 액티비티를 종료하면 onPaused()와 onStop()이 동시에 실행되고, 최종적으로 onDestory()가 호출되면서 액티비티가 메모리에서 제거된다.
- 새로운 액티비티가 생성될 때 현재 액티비티의 생명 주기를 살펴보자. 액티비티를 종료하지 않고 현재 액티비티에서 새로운 액티비티를 실행하면 현재 액티비티의 생명 주기가 onPause()를 거쳐서 onStop()까지만 호출되고 종료되지는 않는다. 그리고 새로 생성된 액티비티는 onStart()와 onResume()을 연속적으로 호출한 후 실행 상태가 된다.
- 새로운 액티비티가 현재 액티비티를 모두 가리지 않고 생성될 때 현재 액티비티의 생명 주기를 살펴보자. 현재 액티비티에서 실행되는 새로운 액티비티가 반투명하거나 전체 화면이 아니라서 현재 액티비티의 영역이 1dp라도 화면에 표시되면 onPause()까지만 진행된 후 Paused 상태에서 대기하고, 새로 생성됐던 액티비티가 종료되면 onStart()를 거치지 않고 바로 onResume()이 호출된다.
# 액티비티 백스택
백스택은 액티비티 또는 화면 컴포넌트를 담는 안드로이드의 저장 공간이다. 액티비티 A에서 액티비티 B를 실행하고, 다시 액티비티 B에서 액티비티 C를 실행하면 마치 종이가 쌓이듯이 액티비티가 화면(백스택)에 쌓이게 되고, 사용자는 가장 위에 있는 액티비티를 보게 된다. 뒤로가기 버튼(onBackPressed())을 누르거나 현재 액티비티를 종료하면 현재 액티비티가 스택에서 제거되므로 현재 액티비티가 백스택의 가장 위로 오면서 화면에 나타난다.
# 태스크와 프로세스
태스크는 애플리케이션에서 실행되는 프로세스를 관리하는 작업 단위이다. 안드로이드는 애플리케이션의 실행 단위로 프로세스를 사용하는데 먼저 애플리케이션의 실행 단위인 프로세스를 살펴보자. 하나의 앱을 만들고 실행하면 앱당 하나의 프로세스가 생성되고 액티비티를 처리한다.
안드로이드에서 태스크는 다른 프로세스의 액티비티를 함께 담을 수 있다. 안드로이드는 서로 다른 애플리케이션의 액티비티를 공유할 수 있는데 카메라와 갤러리 액티비티를 예로 들 수 있다. 카메라 기능을 간단한 코드로 호출해서 사용하면 실제로는 카메라 앱의 독자적인 프로세스가 실행되고 카메라 액티비티 또한 카메라 앱의 프로세스에 의해 처리된다.
다음은 특정 앱의 액티비티에서 카메라를 사용할 때 인텐트를 시스템을 통해 카메라 앱에 전달하는 예제 코드이다.
class Activity_B : AppCompatActivity() {
val REQ_CAMERA = 100
//.. 중략
fun openCamera() {
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
startActivityForResult(intent, REQ_CAMERA)
}
}
카메라를 사용하기 위한 인텐트를 시스템으로 전달하면 카메라 액티비티가 다른 앱에 있기 때문에 프로세스를 새로 생성한다. 호출된 카메라 액티비티가 새로운 프로세스를 통해 동작하지만 하나의 작업 단위인 태스크로 묶인다. 또한 마치 하나의 앱처럼 동일한 태스크로 묶이고 백스택에 쌓이게 된다.
같은 태스크의 백스택에 쌓이기 때문에 뒤로가기 버튼을 누르면 같은 앱의 액티비티처럼 백스택에서 제거되고, 홈 버튼을 누르면 마치 하나의 앱처럼 태스크 전체가 백그라운드로 이동한다.
# 액티비티 태스크 관리하기
액티비티 태스크는 두 가지 방법으로 관리할 수 있다. 먼저 매니페스트의 설정으로 관리하는 방법이다. 태스크와 백스택으로 관리되는 액티비티는 설정 파일인 AndroidMainfest.xml에 작성되는 <activity> 태그 안에 속성으로 다음 코드처럼 사용할 수 있다.
<activity android:name = ".MainActivity" android:launchMode="singleInstance"></activity>
속성 | 설명 |
taskAffinity | 기본값은 manifest에 정의된 패키지명으로 기본적으로 한 앱의 모든 액티비티는 동일한 affinity를 가진다. allowTaskReparenting의 값에 따라 액티비티가 쌓일 태스크 스택을 결정할 수 있다. 입력값은 패키지명과 같은 형태이다. |
launchMode | 호출할 액티비티를 새로 생성할 것인지 재사용할 것인지를 결정한다. 기본값은 항상 새로 생성하도록 되어 있다. 네 가지 모드: standard, singleTop, singleTask, singleInstance |
allowTaskReparenting | 호출한 액티비티를 동일한 affinity를 가진 태스크에 쌓이도록 한다. |
clearTaksOnLaunch | true면 액티비티가 재실행 될 때 실행된 액티비티의 수와 관계없이 메인 액티비티를 제외하고 모두 제거한다. 기본 값은 false이다. |
alwaysRetainTaskState | 기본 설정값이 false면 사용자가 특정 시간 동안 앱을 사용하지 않을 경우 시스템이 메인 액티비티를 제외한 액티비티들을 제거한다. true일 경우는 시스템이 관여하지 않는다. |
finishOnTaskLaunch | 앱을 다시 사용할 때 기존 액티비티를 종료할지 여부를 결정한다. 기본값이 false일 경우 종료하지 않는다. |
액티비티 태스크를 관리하는 또 다른 방법으로는 소스 코드에서 startActivity() 메서드에 전달하는 플래그 값으로 태스크를 관리하는 방법이다.
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
보통 많이 사용하는 플래그를 표로 정리했다.
플래그 | 설명 |
FLAG_ACTIVITY_CLEAR_TOP | 호출하는 액티비티가 스택에 있으면 해당 액티비티를 Top으로 이동시키기 위해 그 위에 존재하던 액티비티를 모두 삭제한다. 예를 들어, 액티비티 A/B/C/D가 스택에 있을 때 B를 호출하면 C/D를 삭제해서 B를 화면에 나타낸다. |
FLAG_ACTIVITY_MULTIPLE_TASK | 호출되는 액티비티를 메인으로 하는 새로운 태스크를 생성한다. 이렇게 하면 동일한 액티비티를 하나 이상의 태스크에서 열 수 있다. |
FLAG_ACTIVITY_NEW_TASK | 새로운 태스크를 생성하여 생성된 태스크 안에 액티비티를 추가할 때 사용한다. 단, 기존에 존재하는 태스크 중에 생성하려는 액티비티와 동일한 affinity를 가지고 있는 태스크가 있으면 해당 태스크로 액티비티가 들어간다. 하나의 애플리케이션 안에서는 모든 액티비티의 기본 affinity가 같은 태스크 안에서 작동하지만, 무조건 태스크가 새로 생성되는 것은 아니고 FLAG_ACTIVITY_MULTIPLE_TASK 플래그와 함께 사용해야 한다. |
FLAG_ACTIVITY_SINGLE_TOP | 호출되는 액티비티가 TOP에 있으면 해당 액티비티를 다시 생성하지 않고, 존재하던 액티비티를 다시 사용한다. 액티비티 A/B/C가 있을 때 C를 호출하면 기존과 동일하게 A/B/C가 나온다. |
예시)
override fun onClickProduct(productId: Long?) {
startActivity<ProductDetailActivity>{
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
putExtra(ProductDetailActivity.PRODUCT_ID, productId)
}
}