태스크(task
)는 사용자가 앱에서 무언가 하려고 할 때 상호작용하는 액티비티의 모음
액티비티들은 백 스택(back stack
)이라는 스택에 액티비티가 실행된 순서대로 정리됨
예시로, 이메일 앱이 새 메시지의 리스트를 보여주는 액티비티를 가지고 있을 때, 사용자가 메시지를 선택하면 그 메시지를 보여줄 수 있는 새 액티비티가 열림, 이 새 액티비티는 백 스택에 추가되고, 사용자가 뒤로가기를 누르면 액티비티가 종료되고 스택에서 제거됨
기기의 홈 화면은 대부분의 태스크의 시작점임, 이 때 사용자가 홈 화면이나 앱 런처에서 앱의 아이콘이나 바로가기를 누르면 앱의 태스크가 포그라운드로 올라오게 됨, 이 때 태스크가 없다면 새 태스크가 생성되고 앱의 메인 액티비티가 스택의 루트 액티비티로 열림
현재 액티비티가 새로운 액티비티를 시작할 때, 새로운 액티비티는 스택의 상단에 추가되고 포커스를 가지게 됨, 이전 액티비티는 스택에 남지만 중단(Stopped
)됨, 액티비티가 중단될 때 시스템은 UI 상태를 유지함, 사용자가 뒤로가기를 누르면 현재 액티비티는 스택의 상단에서 제거되고 파괴되고 이전 액티비티를 재개(resume
)하고 UI 상태를 복원함
스택에 있는 액티비티들은 절대 재정렬되지 않음, 새로운 액티비티를 시작하거나 사용자가 뒤로가기를 누를 때 스택에서는 삽입과 삭제만 이루어짐
백 스택은 LIFO(last in, first out)
구조로 동작함
루트 런처 액티비티는 ACTION_MAIN
과 CATEGORY_LAUNCHER
를 인텐트 필터에 선언한 액티비티, 이런 액티비티들은 앱 런처에서 앱을 실행할 때 진입점 역할을 하고 태스크를 시작하는데 사용됨
사용자가 루트 런처 액티비티에서 뒤로가기를 하면 기기의 안드로이드 버전에 따라 시스템이 다르게 처리함
시스템이 액티비티를 종료하는 대신 액티비티를 백그라운드의 태스크로 이동함, 이는 홈 버튼을 눌렀을 때와 동일한 동작임
대부분의 경우에 이 동작은 앱을 완전히 재시작하는 것 대신, 앱을 빠르게 재개할 수 있게 함
본문에서 앱을 시작할 때 앱의 상태를 각각
cold state
,warm state
라고 표현함
cold state
: 앱을 시작하기 위해 프로세스부터 시작
warm state
: 프로세스는 존재하고 액티비티부터 시작
만약 뒤로가기 탐색을 커스텀해야 한다면 onBackPressed()
를 오버라이딩하기 보다 AndroidX Activity API를 사용하길 권장함, 뒤로가기 동작을 가로채는 컴포넌트가 없을 경우 AndroidX Activity API는 자동으로 적절한 시스템 동작을 따름
만약 onBackPressed()
를 뒤로가기 탐색을 제어하고 액티비티를 끝내기 위해 오버라이딩했다면 액티비티를 끝내는 대신 super.onBackPressed()
를 호출하게 구현해야 함, super.onBackPressed()
를 호출하면 적절할 때에 액티비티와 태스크를 백그라운드로 이동시키며 사용자에게 보다 일관된 앱 간 탐색 경험을 제공함
태스크는 사용자가 새 태스크를 시작하거나 홈 화면으로 이동할 때 백그라운드로 이동할 수 있는 통합된 단위
다른 태스크가 포그라운드에 올라오면 태스크는 포커스를 잃고 백그라운드에 진입함, 이 때 태스크에 존재하는 모든 액티비티가 중단되지만, 백 스택은 그대로 남아있음
백그라운드에 있는 태스크가 다시 포그라운드로 올라올 수 있는데 백스택이 남아있기 때문에 사용자가 중단했던 부분부터 다시 시작할 수 있음
노트: 여러 태스크를 백그라운드에서 동시에 가지고 있을 수 있습니다. 하지만 사용자가 너무 많은 백그라운드 태스크를 실행한다면 시스템은 메모리를 확보하기 위해 백그라운드 액티비티를 파괴할 수 있습니다. 이 경우 액티비티의 상태는 잃어버리게 됩니다.
백 스택의 액티비티들은 절대 재정렬되지 않기 때문에, 사용자가 이미 실행된 특정 액티비티를 다시 시작할 경우 새 인스턴스가 생성되고 스택에 추가됨
사용자가 뒤로가기를 하면 생성된 순서대로 자신의 UI 상태를 가지고 있는 인스턴스가 보이게 됨
안드로이드는 모든 연속적으로 실행된 액티비티를 같은 태스크, 스택에 저장해 관리함, 이는 대부분의 앱에서 잘 작동하므로 일반적으로는 액티비티가 태스크와 어떻게 연관되어 있는지, 백 스택에 어떻게 존재하는지 걱정할 필요가 없음
하지만 이런 일반적인 방식을 따르지 않고 싶을 수 있음, 이런 경우에 매니페스트의 <activity>
요소에 속성을 부여하거나 intent
에 flag
를 부여해 액티비티를 시작할 수 있음
매니페스트 파일에 액티비티를 선언할 때, <activity>
요소의 launchMode
속성을 통해 액티비티가 태스크에 어떻게 연관되어 있는지 지정할 수 있음
launchMode
속성에 부여할 수 있는 시작 모드는 5개가 있음
standard
: 기본 모드, 안드로이드의 기본 태스크 관리 방식을 따름singleTop
: 스택의 최상단에 이미 액티비티가 있다면 새로 인스턴스를 만들지 않고 onNewIntent()
메소드를 통해 intent
만 전달함, 액티비티가 이미 있더라도 태스크의 최상단이 아니면 새로 인스턴스를 만듬singleTask
: 액티비티가 태스크에 존재한다면 새로 인스턴스를 만들지 않고 기존에 존재하는 인스턴스를 사용함, 이 때 기존의 인스턴스로 돌아가면 같은 태스크의 상위 액티비티는 모두 파괴됨singleInstance
: 앱에서 하나의 인스턴스만 가질 수 있음, 인스턴스가 생성될 때 새로운 태스크의 루트에 생성되고 그 태스크에 다른 액티비티는 들어갈 수 없음singleInstancePerTask
: singleTask
처럼 태스크에 인스턴스가 존재하면 기존 인스턴스 사용, 이 때 상위 액티비티 파괴도 똑같음, 또한 singleInstance
처럼 인스턴스가 생성될 때 새로운 태스크의 루트에 생성되지만 FLAG_ACTIVITY_MULTIPLE_TASK
나 FLAG_ACTIVITY_NEW_DOCUMENT
플래그가 설정된 인텐트로 실행하면 singleInstance
와는 다르게 앱 내에 새로운 인스턴스가 생성될 수 있음, 이 때 새로운 태스크가 생기며 그 태스크의 루트에 생성됨액티비티를 시작할 때 플래그를 포함한 인텐트를 전달하면 태스크의 동작 방식을 바꿀 수 있음
FLAG_ACTIVITY_NEW_TASK
: 앞서 살펴본 singleTask
시작 모드와 동일함
FLAG_ACTIVITY_SINGLE_TOP
: 앞서 살펴본 singleTop
시작 모드와 동일함
FLAG_ACTIVITY_CLEAR_TOP
: 시작하려는 액티비티가 이미 존재하면 존재하는 인스턴스의 상위에 있는 모든 액티비티를 파괴해 인스턴스를 태스크의 상위로 올리고 인텐트를 전달함, 주로 FLAG_ACTIVITY_NEW_TASK
와 같이 사용됨, 두 플래그를 같이 사용하면 존재하는 액티비티를 인텐트에 대응할 수 있는 다른 태스크로 옮길 수 있음
액티비티가 선호하는 태스크를 나타냄, 기본적으로 같은 앱의 모든 액티비티는 같은 태스크에 있기를 선호함
매니페스트 파일에서 <activity>
요소의 taskAffinity
속성을 수정해 어피니티를 수정할 수 있음, 문자열 값을 가지며 앱의 패키지 이름을 기본 어피니티로 가지기 때문에 값을 지정할 때 패키지 이름을 피해야 함
어피니티는 두가지 상황에서 작동하기 시작함
FLAG_ACTIVITY_NEW_TASK
플래그가 포함되어 있다면, 시스템은 액티비티를 수용할 다른 태스크를 찾음, 보통은 새로운 태스크가 생성되지만 동일한 어피니티를 가지고 있는 태스크가 있으면 해당 태스크에 액티비티가 추가됨, 이 플래그를 통해 액티비티가 새로운 태스크에 생성되면 사용자가 홈 버튼을 눌러 해당 태스크를 빠져나갔을 때 태스크로 돌아올 방법이 있어야 함, 따라서 이 플래그를 사용한다면 태스크의 루트 액티비티가 CATEGORY_LAUNCHER
인텐트 필터가 있어야 함allowTaskReparenting
속성이 true
로 설정된 경우, 자신이 시작된 태스크에서 어피니티를 가지고 있는 태스크가 포그라운드로 왔을 때 옮겨질 수 있음, 예를 들어 액티비티가 다른 앱에서 실행되어 다른 태스크에 있을 때 액티비티가 속한 앱의 태스크(액티비티의 기본 어피니티가 본인이 속한 앱)가 포그라운드로 왔을 때 해당 앱의 태스크로 옮겨질 수 있음사용자가 태스크를 오랫동안 비워놓으면 시스템은 루트 액티비티를 제외한 나머지 액티비티를 태스크에서 제거함, 이 상태에서 사용자가 태스크로 돌아오면 루트 액티비티만 복원됨, 시스템은 사용자가 오랜 시간 동안 이전에 하던 일을 포기했다고 생각하고 태스크로 돌아왔을 때 새로운 일을 시작하려고 했다고 판단해 이런 동작을 함
이런 동작을 수정할 수 있는 방법이 있음
alwaysRetainTaskState
: 이 속성을 태스크의 루트 액티비티에 true
로 설정하면 기본 동작이 실행되지 않음, 태스크는 오랜 시간이 지나도 모든 액티비티를 유지clearTaskOnLaunch
: 이 속성을 태스크의 루트 액티비티에 true
로 설정하면 사용자가 태스크를 떠났다가 다시 돌아올 때 태스크는 루트 액티비티만 남기고 비워짐, alwaysRetainTaskState
의 반대 동작finishOnTaskLaunch
: clearTaskOnLaunch
와 비슷한 속성이지만 전체 태스크가 아닌 단일 액티비티에 적용, 루트 액티비티가 아닌 액티비티에 적용 가능, true
로 설정되면 적용된 액티비티는 사용자가 태스크를 나갔다 돌아오면 해당 액티비티는 더 이상 존재하지 않게 됨노트:
clearTaskOnLaunch
,finishOnTaskLaunch
는FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
플래그가 정해져 있지 않다면 동작하지 않음
android.intent.action.MAIN
, android.intent.category.LAUNCHER
를 인텐트 필터에 추가해 액티비티를 태스크의 진입점으로 설정할 수 있음<activity ... >
<intent-filter ... >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
...
</activity>
이런 인텐트 필터를 추가하게 되면, 앱 런처에 해당 액티비티의 아이콘과 라벨이 표시되어 사용자가 액티비티를 실행하거나 사용자가 태스크를 떠났다가 돌아올 수 있게 됨
사용자가 태스크를 떠났다가 돌아올 때 런처를 통해 액티비티로 돌아올 방법이 있어야 함, 따라서 액티비티를 항상 새로운 태스크에서 실행하는 singleTask
와 singleInstance
시작 모드를 사용하는 액티비티는 ACTION_MAIN
과 CATEGORY_LAUNCHER
인텐트 필터가 있어야 함
원문: https://developer.android.com/guide/components/activities/tasks-and-back-stack