https://developer.android.com/guide/components/activities/tasks-and-back-stack
task와 back stack 이란?
task는 유저가 앱과 소통하는 액티비티의 모음이다.
이러한 액티비티들은 스택 형태로 저장된다. 이를 back stack이라고 하며 액티비티를 open한 순서대로 저장된다.
예를 들면 메일 앱을 사용한다고 하자.
메일앱은 하나의 액티비티를 가지고 있고 여러개의 메일 리스트를 보여준다.
유저가 새로온 메일의 내용을 보기위해 누르면 메일의 내용을 자세하게 나타내주는 새로운 액티비티가 하나 생기고 back stack에 쌓이게 된다.
그리고 메일내용을 다 읽고 백버튼을 누르면 back stack에서 메일내용에 대한 액티비티가 pop된다.
task와 back stack의 라이프사이클
device의 home screen은 대부분의 task들의 시작점이다. 유저가 앱아이콘을 클릭하여 실행하면, 앱의 task가 foreground로 나오게 된다.
만약 앱의 task가 존재하지 않는다면 새로운 task가 생성되고 앱의 main activity가 stack의 root로 자리잡게 된다.
현재 액티비티가 새로운 activity를 실행하면, 새로운 액티비티는 back stack의 top에 push되고 focus를 가지게 된다.
이전의 activity는 스택에 남아있으나 stop 상태가 된다. activity가 stop되면 시스템은 현재 유저인터페이스의 state를 유지한다.
유저가 백버튼을 누르면 현재 activity는 pop 되버리고 이전 activity는 resume이 된다.
back stack에 있는 activity들은 rearraged 되지 않는다. 오직 push, pop만 한다.
root launcher activity에서 back 동작
아까 앱을 실행하면 activity가 root로 자리잡게 된다고 하였다.
이는 root launcher activity라 하며 Intent filter로 ACTION_MAIN 과 CATEGORY_LAUNCHER 속성을 부여받는다.
그리고 이 액티비티들은 unique하다. 홈스크린 또는 앱런처와의 entry point이면서 task의 시작점으로 사용된다.
이 root activity에서 백버튼을 클릭하면 어떻게 될 까? 이는 안드로이드 os 버전마다 다르다
Android 11이하의 버전
액티비티가 종료되버린다. 즉 모든 액티비티들이 stack에서 pop 되어버리고 task가 사라진다.
앱이 꺼진다는 뜻.
Android 12이상의 버전
시스템이 액티비티와 task를 종료하지 않고 백그라운드로 보내버린다. 즉 앱이 종료되지 않고 홈버튼 누르면 백그라운드로 가듯이 되버린다. 한마디로 홈버튼 동작이랑 똑같음.
이렇게 함으로써 좀더 빠르게 앱을 킬 수 있다고 하는데 어쨋든 아이폰 따라한 느낌
만약 커스텀 back navigation을 만들고 싶다면 구글에서는 onBackPressed()를 오버라이딩 하기 보다는 AndroidX Activity API를 사용하는것을 권장하고 있다.
Background task와 Foreground task
유저가 새로운 task를 부르거나 홈스크린으로 이동하면 기존에 있던 task들은 stop되고 background로 이동한다
허나 background로 이동된 task들은 단순히 stop만 되어있지 액티비티 자체는 intact(온전)하다.
즉 백그라운드에 있는 task들은 단순히 focus만 잃었을 뿐이다.
백그라운드에 있는 task가 다시 foreground로 오면 resume된다
Multiple activity instances
backstack에 있는 activity는 절대 rearranged 되지 않는다.
만약 어떤 특정한 activity가 다른 여러 액티비티에 의해 호출되면 어떻게 될 까?
그리고 특정한 activity가 이미 backstack에 있는데 새로 호출된다면?
정답은 새로운 인스턴스를 생성하여 백스택에 넣는다.
위 그림에서 Home Activity가 이미 백스택에 있다. 여기서 Activity2가 Home Activity를 호출한다.
그러면 기존에 백스택에 있던 Home Activity는 스택의 위로 올라오지 않고, 새로 인스턴스가 생성되어 백스택에 쌓인다.
즉 스택에 있는 두개의 Home Activity는 다른 인스턴스가 된다.
한마디로 쓸데없는 자원낭비를 하고 있다는 소리. 구글에서는 이 문제를 해결하는 방법은 이후 설명하겠다.
Multi-window environments
멀티윈도우 환경에서(안드로이드 7부터 지원) task관리는 어떻게 될 까? 정답은 각 윈도우마다 따로 관리 된다.
이는 크롬북에서 실행되는 안드로이드 앱에도 적용되는 사항이다.
Manage tasks
이 토픽에서는 tasks를 커스텀하게 다뤄보는 법을 배우겠다.
task를 커스텀하게 다루면 다음과 같은 일을 할 수 있다.
- 액티비티를 실행할 때 새로은 task로 실행할 수 있다
- 유저가 task를 떠날 때 root activity를 제외하고 백스택에서 전부 clear 할 수 있다.
- 앞서 마주한 문제, 똑같은 액티비티에 다른 인스턴스가 생성되는 문제를 해결 할 수 있다.
이러한 작업들은 매니페스트의 <activity> 엘리먼트를 통해 구현하거나 startActivity()에 넘겨주기전에 intent에 flag를 세움으로써 구현이 가능하다.
<activity> 엘리먼트로는 다음과 같은 요소를 사용할 수 있다
- taskAffinity
- launchMode
- allowTaskReparenting
- clearTaskOnLaunch
- alwaysRetainTaskState
- finishOnTaskLaunch
그리고 아래 flag들은 Intent에서 사용 가능하다
- FLAG_ACTIVITY_NEW_TASK
- FLAG_ACTIVITY_CLEAR_TOP
- FLAG_ACTIVITY_SINGLE_TOP
Defining launch mode
launch mode는 새로운 액티비티가 현재 액티비티와 어떻게 연결되느냐를 정의할 수 있다.
런치모드를 define 하는 방법은 앞에서 설명한 방법과 같다
- 매니페스트에서 정의하기
- 인텐트 flag로 정의하기
액티비티A가 액티비티B를 실행한다고 하자. 런치모드를 통해서 액티비티B가 액티비티A에 어떻게 연결될지를 정의할 수 있고, 그 반대로 액티비티A가 새로 생성될 액티비티B에 어떻게 연결될지도 정의할 수 있다.
만약 액티비티A, 액티비티B 둘다 상대방에 대해 런치모드를 정의했을 경우 액티비티A의 런치모드가 우선된다.
매니페스트에서 런치모드를 정의하려면 <activity>엘리먼트에 launchMode atrribute를 부여한다.
다음은 launchMode의 attribute에 대한 설명이다
- "standard" (디폴트 값)
스탠다드 속성은 기본 런치모드 값으로, 새로운 액티비티가 생성되면 백스택에서 이전 액티비티 위에 쌓인다. - "singleTop"
새로운 액티비티를 생성할 때, 만약 동일한 액티비티의 인스턴스가 이미 task에 존재하고, 그 위치가 backstack의 TOP일 경우, 안드로이드에서는 새로운 액티비를 생성하지 않고 인텐트를 이미 존재하는 액티비티를 onNewIntent() 메소드를 통하여 인텐트에 연결한다.
예를들어 백스택에 A-B-C-D 이렇게 액티비티가 쌓여있다고 치자(A가 root이고 D가 top).
새로운 액티비티로 D가 들어온다 하자.
그리고 D의 런치모드가 singleTop일 경우 새로운 액티비티를 생성하지 않고 기존에 있는 인스턴스를 이용한다.
고로 백스택의 구성은 A-B-C-D가 된다.
만약 위 예제에서 새로운 인스턴스 B가 온다면, 기존에 있는 인스턴스의 위치가 top이라는 조건을 만족하지 못해서 새로운 인스턴스를 생성하게 된다. 고로 백스택은 A-B-C-D-B가 된다 - "singleTask"
액티비티를 실행할 때 새로운 task를 만들어 실행한다. 그러나 만약 백그라운드에 실행할 액티비티가 이미 존재하는 경우 백그라운드에 있는 task를 가져와 기존에 있던 task에 올린다. 아래 그림에서 자세히 설명하겠다 - "singleInstance"
singleTask와 동일하다. 다만 이 런치모드로 실행되면 task는 더이상 다른 액티비티를 쌓지 않늗나. 즉 이 런치모드로 실행되면 언제나 sperated 된 task로 실행된다
SingleTask에 대표적인 설명으로는 웹브라우저가 있다. 특정앱에서 브라우저를 열 때 singleTask로 실행시킨다. 브라우저는 그러면 새로운 task에서 실행된다.
만약 백그라운드에서 이미 브라우저 task가 있을경우 foreground로 나오게 된다
좀더 자세히 설명하자면, 액티비티가 같은 task든 다른 task든, 백버튼이나 백제스처는 항상 유저에게 이전 액티비티를 보여준다. 그러나 만약 액티비티의 런치모드가 singleTask이고, 인스턴스가 백그라운드에 이미 존재하는경우, 백그라운드에 존재하는 task는 전부 foreground로 가져와 지고 스택의 top에 쌓이게 된다. 이에대한 flow를 나타낸게 아래 그림이다.
플래그로 런치모드 정의하기
이번에는 인텐트에 플래그를 통하여 런치모드를 정의하는 방법을 알아보자
FLAG_ACTIVITY_NEW_TASK
앞서 얘기한 singleTask 와 동작이 같다. 액티비티를 새로운 태스크로 실행한다. 만약 이미 인스턴스가 존재하는경우 task가 foreground로 가져와 지진다.
FLAG_ACTIVITY_SINGLE_TOP
실행할 액티비티가 이미 존재하고, 백스택에 탑에 있는경우 새로운 액티비티를 만들지 않고 기존에 사용하던 액티비티를 활용한다. 이는 앞서말한 singleTop과 같다
FLAG_ACTIVITY_CLEAR_TOP
만약 액티비티가 이미 태스크에 존재할 경우, 새로운 액티비티 인스턴스를 만들어 백스택에 올리지 않고, 기존에 있던 인스턴의 위에 있는 모든 액티비티를 destroy하여 백스택의 탑의 위치에 올리고 라우팅 한다.
FLAG_ACTIVITY_CLEAR_TOP은 보통 FLAG_ACTIVITY_NEW_TASK와 같이 쓰인다. 이렇게 함께 사용하면 다른 task에 있는 activity를 인텐트가 respond할 수 있는 위치에 액티비티를 놓게된다.
Affinity 어피니티 다루기
affinity란 액티비티가 어느 task에 속해 있을지를 나타낸다. 기본적으로, 같은앱에서 파생된 모든 액티비티들은 서로에게 어피니티를 가지고 있다. 따라서 동일한앱의 액티비티들은 동일한 task에 있는걸 선호한다.
하지만 개발자는 액티비티에 대한 affinity를 수정할 수 있다. 서로 다른 task에 있는 액티비티라도 affinity를 공유할 수 있고, 동일한 앱에 정의된 액티비티들도 다양한 affinity를 가질 수 있다
'Android > 안드로이드 기본지식' 카테고리의 다른 글
[Android] Custom View 만들기 (0) | 2022.04.13 |
---|---|
[Android] Content Provider 컨텐트 프로바이더 (0) | 2022.04.05 |
안드로이드 inflate, inflater란? (0) | 2021.07.19 |
안드로이드 onSaveInstanceState (0) | 2021.07.08 |
안드로이드 ViewModel 사용하기 (0) | 2021.07.08 |
댓글