Android 앱 개발을 시작한 초보자부터 다양한 경험을 가진 시니어 개발자까지, Android Task에 대한 이해는 모두에게 중요한 부분입니다. 이번 시간에는 Android의 Task에 대한 개념을 함께 살펴보도록 하겠습니다.
TASK 란?
Task는 사용자가 앱 내 무언가를 수행하려고 할 때 상호 작용하는 Activity의 집합 입니다. 이러한 Activity은 백스택(Back Stack)에 열린 순서대로 정렬됩니다.
예를들어 문자 앱은 문자를 보여주는 Activity을 가질 수 있습니다. 사용자가 메시지를 선택하면 해당 메시지를 보기위한 새로운 Activity가 열립니다. 이 때 새로운 Activity는 Back Stack
에 추가됩니다. 사용자가 뒤로가기를 탭하거나 제스처를 수행하면 현재 Activity인 사용자가 선택한 메시지 Activity 가 Back Stack
에서 Pop 되어 활동이 제거되고 이전 Activity 인 문자 목록 Activity가 활성화 됩니다.
TASK
는 Unit으로 구성되어 사용자가 새로운 TASK
을 시작 하던가 홈 버튼
을 통해 홈 화면으로 이동할 때 Background 로 전환 될 수 있습니다. TASK
의 모든 활동은 Background에 있는 동안 중지되지만 백스택 BackStack
은 그대로 유지됩니다.
예를 들어 현재 ‘Task A’의 백스택(Back Stack)
에 두 개의 Activity
가 있다고 가정합니다. 사용자가 홈 버튼
을 누러서 앱을 나가고 앱 런처
에서 다른 앱을 실행합니다. 그러면 'Task A'는 백그라운드(Background)로 들어가고, 동시에 새로운 'Task B'라는 앱이 시작됩니다. 'Task B'의 화면 중 가장 위에 있는`Activity`이 열리게 됩니다. 사용자가 앱과 상호작용한 후 다시 홈
으로 돌아가서 TASK A를 시작한 앱을 선택합니다. 이때 TASK A가 foreground로 나옵니다. TASK A 의 백스택(Back Stack) 맨 위에 있는 Activity가 다시 시작되며, TASK A 백스택(Back Stack)에 존재한 두 Activity은 모두 그대로 유지됩니다. 또한 이 시점에서 사용자는 TASK B로 다시 전환할 수도 있습니다.
실행모드 (Launcher Mode) 정의
Launcher Mode 활동의 새로운 인스턴스(Instance)의 현재 작업과 연결되는 방식을 정의할 수 있습니다. 연결되는 방식은 두 가지 방법으로 서로 다른 실행 모드(Launcher Mode) 를 정의할 수 있습니다.
- manifest 파일 사용manifest 파일에서 Activity을 선언하면 Activity가 시작될 때 Task과 연결되어야 하는 방식을 지정할 수 있습니다.
- 인텐트(Intent) 플래그 사용
startActivity()
를 호출할 때 새로운 Actiivity가 현재 Task 과 연결되어야 하는 방식(또는 연결되어야 하는지 여부)을 선언하는 Flag 를Intent
에 포함할 수 있습니다.
manifest 와 intent 동시에 실행모드(Launcher Mode) 정의 시 인텐트(Intent) 에 정의가 manifest에 정의보다 우선합니다.
Manifest 파일을 활용한 Launcher Mode
manifest 파일에서 활동을 선언할 때 <activity>
요소의 launchMode
속성을 사용하여 활동이 작업과 연결되어야 하는 방식을 지정할 수 있습니다.
launchMode
은 네 가지 실행 모드를 정의할 수 있습니다.
❑ Standard
"standard"
은 기본값입니다. 시스템은 활동이 시작된 Task의 새로운 Inastance 생성하고 Intent 을 생성된 Inastance로 라우팅합니다.
❑ SingleTop
"singleTop"
은 Activity가 이미 현재 Task의 맨 위에 있으면 시스템은 활동의 새로운 Instance 생성하지 않고 onNewIntent()
메서드를 호출하여 Intent를 기존 instance 로 라우팅합니다.
예를 들어 작업의 백 스택이 루트 활동 A와 활동 B 및 C 그리고 맨 위의 활동 D로 구성되어 있다고 가정합니다. 이때 D 유형 활동의 Intent가 도착합니다. D에 기본 "standard"
실행 모드(Launcher Mode)가 있으면 클래스의 새로운 Instance가 실행되고 스택이 A-B-C-D-D가 됩니다. 하지만 D의 실행 모드(Launcher Mode)가 "singleTop"
이면 D의 기존 인스턴스는 onNewIntent()
를 통해 도착한 인텐트를 받습니다. 따라서 스택은 계속 A-B-C-D로 유지됩니다. 만약, A-B-C-D로 구성된 백스택에 B 유형 Activity의 Intent가 도착하면 B의 새로운 Instance가 스택에 추가됩니다. B의 실행모드(Launcher Mode)가 "singleTop"
이더라도 동일하게 새로운 Instance 가 스택에 추가됩니다.
❑ SingleTask
"singleTask"
시스템이 새로운 Instance 를 생성하고 새로운 Task 루트에 있는 활동을 인스턴스화합니다. 만약, Activity의 인스턴스가 이미 별도의 Task 에 있다면 시스템은 새로운 Instance 를 생성하지 않고 onNewIntent()
메서드를 호출하여 인텐트를 기존 Instance로 라우팅합니다. Activity의 Instance가 한 번에 하나만 존재할 수 있습니다.
예를들어 <activity>
요소의 singleTask
실행 모드를 지정한 A 앱이 있다고 가정하겠습니다. 즉, 이 A 앱을 여는 Intent를 발행하면 이 활동은 앱과 동일한 TASK에 배치되지 않습니다. 대신 해당 A 앱에서 New Task
가 시작되거나, A 앱에 이미 백그라운드에서 실행 중인 작업이 있다면 그 작업이 포그라운드로 나와 New Intent
를 처리합니다.
Activity
가 New Task
에서 시작되었든 또는 Activity
가 시작된 Task과 동일한 Task에서 시작되었든 관계없이 사용자는 뒤로
버튼
을 통해 항상 이전 Activity 로 돌아갈 수 있습니다. 그러나 singleTask
로 실행 모드(Launcher Mode)를 지정한 Activity을 시작했는데 그때 Activity의 instance가 백그라운드 작업에 존재하면 해당 Task가 포그라운드로 이동합니다. 백스택(Back Stack)
에는 앞으로 나온 Task의 모든 Activity가 스택의 맨 위에 포함되어 있습니다.
참고: 활동이 새 작업에서 시작되더라도 사용자는 여전히 뒤로 버튼을 통해 이전 활동으로 돌아갈 수 있습니다.
❑ SingleInstance
"singleInstance"
는 "singleTask"
와 동일하지만 시스템이 Instance 보유한 Task으로는 어떤 다른 Activity도 실행하지 않는 점이 다릅니다. Activity는 Task에 하나만 존재합니다. singleInstance에서 startActivity 로 실행하는 모든 Activity은 별도의 작업으로 열립니다.
번외) Launcher Mode 를 SingleTask 로 선언 하지 않은 Activity을 타 앱에서 실행한다면 어떻게 될까?
일반적으로, 제휴 진입 시 호출하는 Activity
의 실행 모드(Launcher Mode)
는 주로 singleTask
로 설정되어 새로운 작업(New Task)
을 생성하거나 이미 백그라운드에 해당 Activity 인스턴스가 존재하는 경우 New Intent
가 발생하도록 하는 경우가 많습니다. 그러나 특정 상황을 가정해볼 때, 제휴 진입하는 Activity의 실행 모드(Luancher Mode)
가 manifest
에서 standard
또는 singleTop
으로 설정된 경우를 생각해보겠습니다. 이러한 경우에, 제휴 진입 시 호출되는 Activity의 작업(Task) 위치는 호출하는 앱의 실행 모드(Launcher Mode)에 따라 결과가 다를 수 있습니다.
호출부의 실행모드(Launcher Mode) | 제휴 Activity Task 위치 |
standard singleTop singeTask | 호출부의 Task |
singleInstance | New Task |
호출하는 앱의 실행 모드 중 singleInstance
를 제외한 다른 모든 실행 모드(Launcher Mode)
에서는 호출한 앱의 작업(Task)
을 활용하는 것을 확인할 수 있습니다. 이러한 접근 방식은 개발자의 의도와는 달리 사용자는 호출하는 앱을 통해 제휴 앱을 사용하게 되며, 동시에 별도의 제휴 앱 실행 할 수 있게 됩니다. 이러한 점으로 인해 동일한 제휴 앱이 중복 생성되는 문제가 발생할 수 있습니다.
호출하는 앱에서 제휴 앱을 실행시키는 과정에서 앱의 독립성이 상실되며, 사용자 경험 측면에서는 혼란스러운 상황이 발생할 수 있습니다. 따라서 제휴 앱 진입 시에는 해당 Activity
의 실행 모드(Launcher Mode)
를 singleTask
로 설정하여, 제휴 앱이 호출한 앱의 작업(Task)
내에서 실행되도록 조정하는 것이 바람직합니다.
이러한 접근 방식을 통해 사용자는 자연스럽게 제휴 앱을 이용할 수 있으며, 동시에 중복된 제휴 앱 생성으로 인한 문제도 방지할 수 있습니다.
마무리
시간을 내어 TASK에 대한 이해를 다시 해보는 것은 개발자들에게 중요한 일입니다.
이번 글에서는 다루지 않았지만, Taskaffinity를 활용한 Activity가 속하는 Task 설정 및 백스택(Back stack)을 클리어하는 처리 방법 등 다양한 Task 처리 방법이 있습니다. 자세한 내용은 Android Developer 사이트에서 확인하는 것을 추천합니다.
이번 글을 통해, 무심코 지나쳤던 Task에 대한 개념을 다시 한 번 살펴보고, 현재 진행 중인 프로젝트에서 이 개념을 얼마나 잘 활용하고 있는지 점검해 보는 것이 좋을 것입니다.
참고
https://programmingfbf7290.tistory.com/entry/안드로이드-태스크-launchMode-Intent-플래그-총정리