[Android] Activity Task, Back Stack

leeeha·2023년 4월 7일
0

안드로이드

목록 보기
3/6
post-thumbnail

Tasks and the back stack

태스크는 사용자가 앱에서 무언가를 수행하려고 할 때 상호 작용하는 액티비티들의 모음입니다. 이러한 액티비티는 각 액티비티가 실행된 순서대로 스택 (백 스택)에 배치됩니다. 예를 들어 이메일 앱에는 새 메시지 목록을 표시하는 액티비티가 하나 있을 수 있습니다. 사용자가 메시지를 선택하면 해당 메시지를 볼 수 있는 새 액티비티가 열립니다. 이 새 액티비티가 백 스택에 추가됩니다. 그런 다음 사용자가 뒤로가기 버튼이나 제스처를 취하면 새 액티비티가 종료되어 스택에서 pop 됩니다.

Lifecycle of a task and its back stack

디바이스의 홈 화면은 대부분의 태스크들이 시작되는 곳입니다. 사용자가 앱 런처나 홈 화면에서 앱의 아이콘이나 바로가기를 누르면, 앱의 태스크가 포그라운드로 나오게 됩니다. 만약 앱이 최근에 사용된 적이 없어서 태스크가 존재하지 않는다면, 새로운 태스크가 생성되며 앱의 메인 액티비티가 스택에서 루트 액티비티로 열립니다.

현재 액티비티가 다른 액티비티를 시작하면, 새 액티비티는 스택의 top에 push 되고 포커스가 놓이게 됩니다. 이전 액티비티는 스택에 남아있지만 일시정지 됩니다. 액티비티가 일시 정지되면, 시스템은 해당 액티비티 UI의 현재 상태를 저장합니다. 사용자가 뒤로가기 액션을 수행하면, 현재 액티비티가 스택의 top에서 pop 되고 (destroy 되면서), 이전 액티비티가 resume 됩니다. (UI의 이전 상태가 복원됩니다.)

스택에 있는 액티비티들의 위치는 결코 바뀌지 않으며, 단지 스택으로부터 push 되고, pop 됩니다. (현재 액티비티가 시작될 때는 스택에 push, 유저가 뒤로가기 버튼이나 제스처로 액티비티를 떠날 때는 스택에서 pop 됩니다.) 즉, 백 스택은 Last In First Out (LIFO) 구조로 작동합니다.

사용자가 계속해서 뒤로가기 버튼이나 제스처를 취하면, 유저가 홈 화면 (또는 태스크를 시작할 때 실행 중이던 액티비티) 으로 돌아올 때까지, 스택에 있는 각 액티비티는 하나씩 pop 되며 이전 액티비티가 표시됩니다. 모든 액티비티들이 스택에서 제거되면, 태스크는 더 이상 존재하지 않습니다.

Manage tasks

위에 기술한 대로 안드로이드가 태스크와 백 스택을 관리하는 방식은 대부분의 앱에서 잘 작동할 것이며, 개발자는 앱의 액티비티가 태스크 내에서 작동하는 방식, 그리고 백 스택에 존재하는 방식 등에 대해 고민할 필요가 없습니다. 그러나! 개발자는 이러한 일반적인 작동 방식에서 벗어나고 싶을 때가 있을 수 있습니다.

  • 앱의 액티비티가 시작될 때, (현재 태스크 내에 배치되는 게 아니라) 새 태스크를 시작하고 싶은 경우
  • 액티비티를 시작할 때, (백 스택의 top에 새 인스턴스를 생성하는 게 아니라) 기존에 있던 인스턴스를 앞으로 가져오려는 경우
  • 사용자가 태스크를 종료할 때, 루트 액티비티를 제외하고 모든 액티비티를 백 스택에서 제거하고 싶은 경우

<activity> 매니페스트 요소의 속성과 startActivity()에 전달하는 인텐트의 플래그를 사용하여 이러한 작업들을 수행할 수 있습니다.

이와 관련해서 사용할 수 있는 <activity> 태그의 주요 속성은 다음과 같습니다.

  • taskAffinity
  • launchMode
  • allowTaskReparenting
  • clearTaskOnLaunch
  • alwaysRetainTaskState
  • finishOnTaskLaunch

주요 인텐트 플래그는 다음과 같습니다.

  • FLAG_ACTIVITY_NEW_TASK
  • FLAG_ACTIVITY_CLEAR_TOP
  • FLAG_ACTIVITY_SINGLE_TOP

주의: 대부분의 앱에서는 액티비티와 태스크의 기본 동작을 인터럽트 해서는 안 됩니다. 기본 동작을 수정할 필요가 있다고 판단되면 주의를 기울이고, 액티비티를 실행시킬 때 그리고 다른 액티비티나 태스크에 있다가 뒤로가기 버튼으로 다시 돌아갈 때 액티비티의 사용성을 테스트 해야 합니다. 사용자가 예상한 동작과 충돌할 수 있는 네비게이션 동작이 있는지 테스트 해야 합니다.

Defining launch modes

액티비티 A에서 액티비티 B로 전환할 때, 두 액티비티 모두 "B가 태스크 내에서 작동하는 방식"에 대해 정의한 경우, 인텐트 필터에서 정의한 A의 요청이 매니페스트 파일에서 정의한 B의 요청보다 우선하게 됩니다.

참고: 매니페스트 파일에 사용할 수 있는 일부 시작 모드는 인텐트에 대한 플래그로 사용할 수 없으며, 마찬가지로 인텐트에 대한 플래그로 사용할 수 있는 일부 시작 모드는 매니페스트에서 정의할 수 없습니다.

Define launch modes using the manifest file

매니페스트 파일에서 <activity> 태그의 launchMode 속성을 조정하여, 액티비티가 태스크 내에서 작동하는 방식에 대해 지정할 수 있습니다.

standard (default)

기본값입니다. 시스템은 액티비티가 시작된 태스크에서 액티비티의 새 인스턴스를 생성하며, 해당 인스턴스에 인텐트를 보냅니다.

액티비티는 여러 번 인스턴스화 할 수 있으며, 각 인스턴스는 서로 다른 태스크에 속할 수 있고, 하나의 태스크에 여러 인스턴스가 존재할 수 있습니다.

singleTop

현재 태스크의 top에 액티비티의 인스턴스가 이미 존재하는 경우, 시스템은 액티비티의 새 인스턴스를 만들지 않고 onNewIntent() 메서드를 호출하여 기존 인스턴스에 인텐트를 보냅니다.

액티비티는 여러 번 인스턴스화 될 수 있으며, 각 인스턴스는 서로 다른 태스크에 속할 수 있고, (백 스택의 top에 있는 액티비티가 기존 인스턴스가 "아닌" 경우에만) 하나의 태스크에 여러 개의 인스턴스가 존재할 수 있습니다.

예를 들어 태스크의 백 스택이 루트 액티비티 A와 그 위에 액티비티 B, C 및 D로 구성되어 있다고 가정해봅시다. (스택은 A-B-C-D이며, D가 맨 위에 있음)

액티비티 D에 대한 인텐트가 도착합니다. D의 시작 모드가 standard 이면, 클래스의 새 인스턴스가 생성되어 스택은 A-B-C-D-D가 됩니다. 그러나 D의 시작 모드가 singleTop 인 경우, 기존 D 인스턴스는 스택의 top에 있기 때문에 onNewIntent()로 인텐트를 수신하므로, 새 액티비티 인스턴스가 생성되지 않고 스택은 A-B-C-D로 유지됩니다.

그러나 액티비티 B에 대한 인텐트가 도착하면 실행 모드가 singleTop 이더라도 B의 새 인스턴스가 스택에 추가됩니다. (A-B-C-D-B)

singleTask

시스템은 새 태스크의 루트에 액티비티를 생성하거나, 동일한 affinity를 가진 기존 태스크에서 액티비티를 찾습니다. 액티비티의 인스턴스가 이미 존재하는 경우, 시스템은 새 인스턴스를 생성하지 않고 onNewIntent() 메서드를 호출하여 기존 인스턴스에 인텐트를 보냅니다. 이때 인텐트를 수신하는 인스턴스 위에 있는 다른 모든 액티비티들은 종료됩니다.

참고: singleTask는 태스크에서 시작 액티비티 위에 있는 모든 액티비티들을 제거합니다. 예를 들어, 태스크가 루트 액티비티 A와 액티비티 B, C로 구성되어 있다고 가정합시다. (A-B-C; C가 top에 있음) 액티비티 A의 인텐트가 도착했는데 A의 시작 모드가 singleTask라면, A의 기존 인스턴스가 onNewIntent()로 인텐트를 수신합니다. 그리고 B와 C는 종료되어 태스크에는 A만 남습니다.

예를 들어, 안드로이드 브라우저 앱이 웹 브라우저 액티비티의 시작 모드를 singleTask로 설정하여, 항상 자기 자신의 태스크에서만 열리도록 선언했다고 가정합시다. 이 말은, 만약 앱에서 브라우저를 열기 위해 인텐트를 발생시키면, 해당 액티비티가 앱과 동일한 태스크에 놓이지 "않는다"는 것을 의미합니다. 그 대신에, 브라우저를 위한 새로운 태스크가 시작되거나, 만약 브라우저가 백그라운드에서 실행 중인 태스크를 갖고 있다면, 그 태스크가 앞으로 나오게 됩니다.

액티비티가 새로운 태스크에서 실행되든, 액티비티가 시작되었던 태스크에서 동일하게 실행되든, 뒤로가기 버튼이나 제스처를 취하면 항상 유저는 이전 화면으로 돌아오게 됩니다. 그러나, 시작 모드가 singleTask로 지정된 액티비티 (위의 그림에서는 Y)를 실행시키면, 그리고 그 액티비티가 백그라운드 태스크를 갖고 있으면, 그 전체 태스크가 앞으로 나오게 됩니다. 이제 백 스택은 백그라운드 태스크에서 가져온 모든 액티비티들을 스택의 top에 포함하게 됩니다.

singleInstance

singleTask와 유사한데, 이름이 singleInstance인 만큼 인스턴스를 보유하고 있는 태스크에서는 시스템이 다른 어떤 액티비티도 실행시키지 않습니다. 이 모드에서 액티비티는 항상 태스크의 유일한 단일 멤버이며, 태스크에서 시작된 모든 액티비티들은 별도의 태스크에서 열립니다.

Define launch modes using Intent flags

FLAG_ACTIVITY_NEW_TASK

액티비티를 새로운 태스크에서 시작합니다. 지금 시작하려는 액티비티에 대한 태스크가 이미 실행 중인 경우, 해당 태스크는 마지막 상태가 복원되면서 포그라운드로 나오게 되고, 액티비티는 onNewIntent()에서 새 인텐트를 수신합니다.

이것은 위에서 설명했던 singleTask 시작 모드와 동일하게 작동합니다.

FLAG_ACTIVITY_SINGLE_TOP

시작하려는 액티비티가 현재 백 스택의 top에 있다면, 새 인스턴스를 생성하지 않고 기존의 인스턴스가 onNewIntent()로 인텐트를 수신합니다.

이것은 위에서 설명했던 singleTop 시작 모드와 동일하게 작동합니다.

FLAG_ACTIVITY_CLEAR_TOP

시작하려는 액티비티가 이미 현재 태스크에서 실행 중이라면, 새 인스턴스를 생성하지 않고 기존 인스턴스 위에 있는 모든 액티비티를 종료시키고, onNewIntent()를 통해 (현재 top에 위치한) 다시 재개된 인스턴스에 인텐트를 보냅니다.

이 작동과 일치하는 매니페스트의 시작 모드는 없습니다.

FLAG_ACTIVITY_CLEAR_TOP은 주로 FLAG_ACTIVITY_NEW_TASK와 같이 사용됩니다. 두 개의 플래그를 같이 사용하면, 다른 태스크에 있는 기존 액티비티의 위치를 찾아서 그것을 현재 인텐트에 반응할 수 있는 위치로 옮길 수 있습니다.

참고 자료

https://developer.android.com/guide/components/activities/tasks-and-back-stack

profile
습관이 될 때까지 📝

0개의 댓글