: 태스크는 액티비티 작업 묶음 단위이다. 여러 개 앱의 액티비티가 하나의 태스크가 될 수 있고 하나의 앱에서의 액티비티들이 다수의 태스크로 이루어질 수 있다. 태스크는 흔히 액티비티 스택, 백스택이라고 불리기도 한다. 태스크의 저장 방식이 스택 방식(LIFO)이기 때문이다. 태스크에서 액티비티가 쌓이는 방식은 중요하다. 예로 알람 앱을 실행 후 메인 화면(A)에서 알람 설정(B) 액티비티로 이동했고 홈 버튼을 눌러 태스크를 백그라운드로 보냈다. 그리고 내가 예전에 맞춰 놓은 알람 시간이 되 알람 대화상자(C) 액티비티가 떴다. 그렇다면 알람 대화상자(C) 액티비티가 백그라운드에 있는 태스크에 속하게 되 기존의 알람 태스크를 포그라운드로 불러오고 백 버튼을 누르면 C에서 B 액티비티로 가도록 해야 할까? 아니면 알람 대화상자(C) 액티비티를 새로운 태스크로 생성해 백 버튼을 눌러도 백그라운드에 기존의 알람 앱 태스크가 있도록 해야 할까? 이런 경우 때문에 규칙을 정해야 한다. 규칙을 정하기 위해서는 태스크 관리가 중요하다.


- 태스크 상태

: 태스크는 홈 키를 통해서 언제든지 포그라운드에서 백그라운드로 갈 수 있다. 또한 백그라운드에서 포그라운드로도 갈 수 있다. 이 때 이용하는 메서드가 moveTaskToBack(boolean) 이다. 인자에 false가 들어가면 태스크 루트일 때만 백그라운드로 이동이 가능하고 true이면 언제든지 이동 가능하다. 이 메서드는 카카오톡의 '암호 잠금 해제' 액티비티를 구현할 때 무조건 사용된다. 카카오톡을 백그라운드에서 포그라운드로 불러올 때나 처음 시작할 때 암호 설정을 해놓았다면 '암호 잠금 해제' 액티비티가 무조건 나타난다. '암호 잠금 해제' 액티비티는 기존 카카오톡 액티비티 백스택의 맨 위로 올라가기 때문에 암호를 맞게 입력했다면 '암호 잠금 해제' 액티비티를 종료하면 된다. 하지만 사용자가 Back 버튼을 누를 시에는 해당 액티비티를 그냥 종료시키면 안 된다. 백스택의 가장 위에 있기 때문에 액티비티를 종료하면 기존 카카오톡 액티비티가 나오기 때문에 암호 액티비티로써 역할을 못하게 된다. 그래서 사용자가 Back 버튼을 누를 시 원래 카카오톡 액티비티를 보이지 않게 하기 위해서 태스크를 백그라운드로 이동하는 방법을 사용한다. 이 때 '암호 잠금 해제' 액티비티의 onBackPressed() 메서드를 오버라이드해 moveTaskToBack(true)를 호출하면 된다.  


- taskAffinity 속성

: taskAffinity 속성은 AndroidManifest.xml에 기술하는 액티비티 속성값이고 액티비티가 관련된 태스크에 들어갈 때 참고하는 값이다. 값을 명시하지 않을 시 디폴트로 앱의 패키지명이 된다. taskAffinity 속성은 각 액티비티에도 있지만 태스크에도 해당 속성을 가진다. 태스크의 taskAffinity 속성값은 태스크를 시작한 액티비티의 taskAffinity 값이 된다. 태스크의 taskAffinity가 언제 이용되는 지 예로 살펴보자. 만약 액티비티가 android:launchMode=singleTask로 설정되거나 Intent에 FLAG_ACTIVITY_NEW_TASK가 설정되 있다면 액티비티는 태스크의 taskAffinity가 액티비티의 taskAffinity와 동일한 것을 찾아 그 태스크에 액티비티가 속하게 된다. 참고로 FLAG_ACTIVITY_NEW_TASK는 무조건 새로운 태스크를 생성하지 않는다. taskAffinity 값에 따라 달라진다. 액티비티 자신과 같은 taskAffinity 값을 가진 태스크가 있다면 그 태스크에 액티비티가 포함되고 같은 게 없다면 자신의 taskAffinity 값으로 태스크를 새로 만든다. 

추가로 startActivity를 할 시 특별한 옵션이 없다면 피호출자 액비비티는 호출자 액티비티의 백스택에 쌓인다. 따라서 액티비티가 아닌 서비스나 브로드캐스트 리시버에서 startActivity를 그냥 호출하면 오류가 발생한다. FLAG_ACTIVITY_NEW_TASK 옵션을 붙여 특정 태스크에 들어가도록 해줘야 한다.

taskAffinity 속성은 언제 명시해야 할까? taskAffinity 속성은 보통 명시하지 않는다. 그래서 디폴트 값인 패키지명이 들어가 대부분 같은 태스크에 액티비티가 쌓인다. taskAffinity 속성을 명시해야 할 때는 독립적인 화면을 구성해야 할 때이다. 예로 해당 포스트 맨 위 부분에서 알람 앱을 예로 들어서 설명했는 데 보통 알람 대화상자(C) 액티비티는 독립적이어야 한다. C에서 Back 버튼을 눌렀을 때 알람 설정(B) 액티비티가 있다면 이상하지 않는가? 이럴 때 알람 대화상자(C) 액티비티에 taskAffinity 속성을 추가한 후 FLAG_ACTIVITY_NEW_TASK 플래그를 더해서 액티비티가 실행하게 하면 독립적이게 된다. 참고로 알람 대화상자(C) 액티비티의 경우 최근 앱 목록에 안 보이는 게 더 좋아보인다. 이 때는 AndroidManifest.xml에서 해당 Activity에 android:excludeFromRecents 속성을 true로 하면 된다.



- android:launchMode 태스크 속성

1. standard : 디폴트 값이다. 백스택의 가장 위에 액티비티를 추가한다.


2. singleTop : 호출할 액티비티가 백스택 가장 위에 있을 시 새로 생성하지 않고 onNewIntent() 메서드로 Intent를 전달한다. 가장 위에 없다면 standard 방식이 된다.


3. singleTask : 태스크에 해당 액티비티가 하나 밖에 없어야 한다. 액티비티의 taskAffinity와 동일한 태스크가 존재하고 해당 액티비티가 태스크에 있을 시 onNewIntent()를 호출한다. 해당 액티비티가 없다면 태스크의 가장 위에 추가된다. 태스크도 없다면 새로 태스크를 생성한다. 예로 B 액티비티를 singleTask라고 설정했다고 하자. A->B->C로 호출했을 때 모두 같은 taskAffinity 값을 가진다면 태스크에 [A,B,C]가 된다. A->B->C->B 순서로 호출했을 때는 C가 제거 되고 onNewIntent가 호출되고 태스크는 [A,B] 가 된다. 만일 B의 taskAffinity만 다를 때는 A->B->C 순서로 호출했다면 [A], [B,C] 가 된다. 특히하게 C는 A와 같은 태스크에 있지 않는다. C가 singleTask이거나 FLAG_ACTIVITY_NEW_TASK 플래그가 있어야 [A,C], [B]가 된다. 여기서 A->B로 이동했을 때 즉 다른 태스크로 전환했을 때는 약간의 딜레이가 발생한다. B 액티비티가 보이기 전에 검은 화면이 잠시 보이게 된다. 따라서 태스크를 바꾸게 되면 이런 딜레이에 고려해야 한다.


4. singleInstance : 태스크에 들어가는 액티비티는 1개 밖에 없고 액티비티 또한 백스택 안에 하나여야 한다. 예로 B 액티비티가 singleInstance이고 A,B,C의 taskAffinity 값이 같다고 하자. A->B->C 순서로 호출했을 시 [B], [A,C]가 된다. C 액티비티에서 Back 버튼을 누를 시 A 액티비티가 보인다. 만약 C가 Dialog Activity여서 배경에 다른 액티비티가 보인다고 하면 배경으로 B가 아닌 A 액티비티가 보인다. 참고로 최근 앱 목록에서는 [B], [A,C]가 별도로 보이지 않는다. 최근 앱 목록은 taskAffinity 값을 분류 기준으로 삼기 때문이다. B의 taskAffinity 값을 다른 값으로 바꾸면 최근 앱 목록에 [B]와 [A,C]가 보인다.



- Intent 플래그

1. FLAG_ACTIVITY_SINGLE_TOP : singleTop 과 동일하다.

2. FLAG_ACTIVITY_NEW_TASK : singleTask 와 같다.

3. FLAG_ACTIVITY_CLEAR_TOP : 백스택에 [A,B,C] 가 있고 C에서 B를 시작할 때 해당 플래그가 있으면 [A,B]가 된다. 보통 FLAG_ACTIVITY_SINGLE_TOP과 같이 쓰인다. 같이 쓰면 B를 시작할 때 onNewIntent()메서드가 호출되고 같이 안 쓰면 onCreate()가 호출된다. 만약 [A,B,A,B] 백스택 일 때 CLEAR_TOP 플래그로 A를 시작한다면 [A,B,A]가 된다. [A]가 되려면 <activity-alias>를 사용해야 한다.
<activity-alias
	android:name=".FirstActivityA"
	android:targetActivity=".ActivityA"/>

Intent intent = new Intent().setComponent(
		new Component(this,"com.example.app.FirstActivityA"));
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);

4. FLAG_ACTIVITY_CLEAR_TASK : 피호출자 액티비티가 시작하기 전에 관련된 스택이 모두 제거되고 피호출자 액티비티가 빈 태스크의 맨 아래에 놓인다. 보통 FLAG_ACTIVITY_NEW_TASK와 같이 사용된다. 예로 앱을 사용할 때 로그아웃하고 다른 아이디로 로그인하면 기존 태스크를 정리하고 새로운 메인 액티비티를 실행해야 한다. 이 때 두 플래그를 같이 사용한다.


5. FLAG_ACTIVITY_REORDER_TO_FRONT : 스택에 동일한 액티비티가 있다면 그 액티비티를 스택의 맨 위로 올린다. 단 FLAG_ACTIVITY_CLEAR_TOP과 같이 사용 못 한다. 

+ Recent posts