출처
: http://wikiware-textcube.blogspot.kr/2009_12_01_archive.html
: http://blog.daum.net/kinpmanse/38
: http://blog.daum.net/kinpmanse/39
액티비티와 태스크
(1) 태스크(task)
어떤 어플리케이션에서 다른 어플리케이션의 기능을 이용하고자 할 경우에 다른 어플리케이션에 Intent 객체를 인자로 startActivity() 메소드를 호출하면 됩니다. 그러면 사용자에게는 타 어플리케이션이 현 어플리케이션의 일부인 것처럼 보이게 됩니다. 실제로 안드로이드는 호출한 액티비티와 호출된 액티비티를 같은 태스크(task) 안에 유지시킵니다.
태스크(task)는 사용자가 마치 하나의 어플리케이션인 것처럼 경험하게 하는 것들을 말합니다. 즉, 태스크는 "스택에 쌓여 있는 관련된 액티비티들의 그룹"이라고 말할 수 있습니다. 스택의 밑바닥에 있는 루트 액티비티(root activity)가 태스크를 시작한 것입니다. 보통은 어플리케이션 론처에서 사용자가 선택한 액티비티가 루트 액티비티가 됩니다. 스택의 꼭대기에 있는 액티비티는 현재 동작 중인 포커스된 액티비티입니다.
어떤 액티비티가 다른 액티비티를 동작시키면 새로운 액티비티가 스택에 쌓입니다. 휴대폰에서 사용자가 BACK 키를 누르면 이전 화면으로 돌아가는데, 이 때 현재 액티비티가 스택에서 팝(pop)되고 이전 액티비티가 실행됩니다. 따라서 태스크는 "액티비티들이 쌓여 있는 스택"으로 단순히 생각하는 것이 쉽게 이해가 됩니다. 태스크가 매니패스트 파일 안에 있는 어떤 클래스나 요소(element)가 아니므로 오해를 하지 않도록 주의하세요.
하나의 태스크 안에 있는 액티비티들은 태스크 단위로 움직입니다. 태스크가 포그라운드(foreground)나 백그라운드(background)로 전환될 때 관련 태스크가 통째로 옮겨집니다. 이런 동작을 바꾸려면 매니패스트 파일의 <activity> 요소의 플래그(flag)나 속성을 변경해야 합니다. 매니패스트 파일의 인텐트 플래그(flag)에는 다음과 같은 것들이 있습니다.
- FLAG_ACTIVITY_NEW_TASK
- FLAG_ACTIVITY_CLEAR_TOP
- FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
- FLAG_ACTIVITY_SINGLE_TOP
<activity> 요소의 속성에는 다음과 같은 것들이 있습니다.
- taskAffinity
- launchMode
- allowTaskReparenting
- clearTaskOnLaunch
- alwaysRetainTaskState
- finishOnTaskLaunch
위 플래그나 속성들에 대해서는 자세한 설명을 생략합니다. 후에 다시 설명할 기회가 있을 것입니다.
(2) 인척 관계(affinity)
기본적으로 한 어플리케이션 안의 모든 액티비티들은 서로 인척 관계(affinity)에 있습니다. 인척 관계라고 하는 것은 같은 태스크에 속할 수 있는지 여부를 나타냅니다. 이런 인척 관계는 다른 어플리케이션과도 맺어질 수가 있습니다. 또한 같은 어플리케이션 안의 액티비티들이 서로 다른 인척 관계를 가질 수도 있습니다. 이런 인척 관계를 설정하는 방법은 매니패스트 파일의 <activity> 요소의 taskAffinity 속성으로 제어를 할 수 있습니다. 인척 관계를 설정하는 2 가지 방법은 다음과 같습니다.
- FLAG_ACTIVITY_NEW_TASK 플래그를 이용한 방법
기본적으로 startActivity()로 호출된 액티비티는 호출한 액티비티의 태스크에 포함됩니다. 단, startActivity()를 부를 때 intent 객체 인자에 FLAG_ACTIVITY_NEW_TASK 플래그가 설정되어 있으면, 시스템이 새 태스크로 새 액티비티를 시작합니다. 기존 태스크에 포함된 경우는 해당 태스크에 포함시킵니다. - allowTaskReparenting 속성을 이용한 방법
이 속성이 “true”인 경우, 어떤 태스크에 포함되어 있다가도 다른 인척 관계에 있는 태스크가 포그라운드가 될 때 이 태스크로 옮겨갈 수 있습니다.
(3) 론치 모드(launch mode)
<activity> 요소의 launchMode 속성에는 다음과 같은 4 가지가 있습니다.
- "standard" (the default mode)
- "singleTop”
- "singleTask”
- "singleInstance“
위 속성들을 해석하는 방법은 다음과 같이 4 가지 관점이 있습니다.
- 인턴트에 반응한 액티비티를 어느 태스크가 소유하는가?
"standard”나 "singleTop”의 경우는 startActivity()를 호출한 태스크가 호출된 액티비티를 소유합니다. (단, FLAG_ACTIVITY_NEW_TASK 플래그가 인턴트에 있는 경우는 예외입니다.) "singleTask”나 "singleInstance”의 경우는 항상 루트 액티비티가 됩니다. 즉, 항상 새 태스크가 됩니다. - 태스크가 여러 개의 인스턴스를 가질 수 있는가?
"standard”나 "singleTop”의 경우는 여러 번 인스턴스화될 수 있습니다. 이 말은 하나의 액티비티가 여러 태스크에 속할 수 있다거나, 하나의 태스크에 여러 개의 같은 액티비티가 생길 수 있다는 뜻입니다. "singleTask”나 "singleInstance“의 경우는 단지 하나의 인스턴스만 가집니다. - 한 태스크 안에 같은 액티비티 인스턴스를 2 개 이상 가질 수 있는가?
"singleInstance”인 경우는 단지 하나의 액티비티만 가지는 태스크를 만듭니다. 다른 액티비티를 부르면 불린 것은 마치 FLAG_ACTIVITY_NEW_TASK 플래그가 설정된 것처럼 새로운 태스크로 들어갑니다. "standard”나 "singleTop” 또는 "singleTask”의 경우는 하나의 태스크 안에 여러 개의 액티비티가 포함될 수 있습니다. - 새로운 인턴트를 처리할 때 항상 새로운 인스턴스가 생성되는가?
"standard”인 경우는 신규 인텐트는 항상 신규 인스턴스로 반응합니다. "singleTop”인 경우는 신규 인텐트는 기존 인스턴스를 재사용하여 반응합니다. (단, 재사용된 액티비티는 타켓 태스크의 꼭대기에 있는 액티비티여야 합니다.) 꼭대기에 있는 액티비티가 아니면 재사용되지 않고 새 인스턴스가 생성되어 스택에 푸쉬(push)됩니다. 아래 그림을 참조 바랍니다.
[참고] singleTask 와 singleInstance 는 오직 한 개의 instance 만을 가질 수 있습니다. 게다가 이 option으로 설정된 녀석들은 task 를 시작하게 됩니다. 그래서 항상 stack 의 root 를 차지하고 있죠. singleTask 는 다른 activity 들이 자신의 instance 위에 쌓이는 것을 허락해줍니다. 하지만 singleInstance 는 다른 activity 를 자신의 task 안에 포함시키지 않습니다. 그래서 singleInstance 에서 어떤 activity 를 launch 시키면 자신의 task 위에 쌓지 않고, 다른 task 또는 새로운 task 위에 쌓아 놓습니다. ( 이것은 FLAG_ACTIVITY_NEW_TASK 와 비슷한 효과입니다. ) Developer 를 보면 singleTask 와 singleInstance에 대해서는 빨간색 으로 표시하면서까지 이 옵션들은 대부분의 application 에서 사용하지 않기를 권고하고 있습니다. 왜냐면, 일반적인 User 의 상식에서 벗어나서 UX 측면에서도 안 좋을 뿐더러, 오작동을 초래하기도 쉽기 때문입니다. 또한 어떤 launchMode 를 적용하든간에 Back key 를 누르는 등의 테스트를 충분히 하여 원하는 데로 task 관리가 되고 있는지를 확인할 것도 권장 하고 있습니다. (단, 위의 그림과 같이 singleTask 옵션이 동작하려면 C의 taskAffinity 가 A, B의 taskAffinity와 달라야 한다.) |
(4) 스택 지우기
사용자가 오랫동안 태스크를 떠나 있으면, 시스템이 자동으로 그 태스크의 액티비티들을 지웁니다. 단 루트 액티비티는 남겨 놓습니다. 이런 경우 사용자가 다시 이 태스크로 돌아 왔을 때 루트 액티비티가 보여지게 됩니다. 이와 같은 동작이 기본 동작입니다. 만약 다른 동작을 하도록 하고 싶으면 앞에서 설명드린 <activity> 요소의 속성으로 제어할 수 있습니다. 다음의 3 가지 속성이 이를 제어하는 속성들입니다.
- alwaysRetainTaskState 속성
루트 액티비티의 속성이 “true”이면, 오랜 시간이 지나도 그대로 남아 있습니다. - clearTaskOnLaunch 속성
루트 액티비티의 이 속성이 “true”이면, 사용자가 이 태스크를 떠났다가 돌아올 때마다 루트 액티비티만 남겨 놓고 나머지는 스택에서 삭제합니다. - finishOnTaskLaunch 속성
사용자가 이 태스크를 떠났다가 돌아올 때마다 이 속성이 “true”인 액티비티만 스택에서 삭제합니다. 이 경우는 루트 액티비티도 삭제될 수 있습니다.
스택을 지우는 다른 방법으로 인턴트 객체의 FLAG_ACTIVITY_CLEAR_TOP 플래그를 사용하는 방법이 있습니다. 이 플래그를 사용하면 수신한 인턴트 객체를 처리할 수 없는 액티비티들이 타켓 태스크 스택에서 모두 삭제됩니다. 즉, 수신한 인턴트 객체를 처리할 수 있는 액티비티가 스택의 꼭대기에 나올 때까지 삭제가 일어납니다. 다만 처리 가능한 액티비티의 론치 모드가 "standard"인 경우는 이 액티비티도 스택에서 삭제되고, 새 액티비티가 생성되어 스택에 푸쉬됩니다. "standard"가 의미하는 것이 새 인턴트는 새 액티비티로 처리하라는 것이기 때문입니다.
(5) 진입점(entry point)
어떤 태스크의 진입점을 지정하는 방법은 앞에서도 설명하였듯이 매니패스트 파일의 액티비티의 <intent-filter>에 액션은 "android.intent.action.MAIN"을 사용하고, 카테고리는 "android.intent.category.LAUNCHER"를 사용하면 됩니다. 이런 진입점을 가지는 필터에는 아이콘과 라벨도 추가로 지정하여야 어플리케이션 론처에 표시가 됩니다.
이런 진입점을 가진 액티비티는 항상 "singleTask”나 "singleInstance”로 시작해야 합니다. 왜냐하면 어플리케이션 론처에서 실행 가능한 것이 "singleTask”나 "singleInstance”로 되어 있지 않으면, 실행 후 홈 키를 누른 후에는 다시 기존 태스크로 돌아갈 방법이 업습니다. 이와 비슷한 문제가 FLAG_ACTIVITY_CLEAR_TOP 플래그를 가진 액티비티에도 있습니다. 이런 것들은 활성화된 후에 다시 돌아올 다른 방법을 부가적으로 제공해 주어야 합니다. 만약 다시 돌아올 방법을 제공하지 못하는 경우는 <activity> 요소에 finishOnTaskLaunch를 "true"로 설정하여 종료가 되도록 해야 함에 주의하세요.
출처: http://www.kandroid.org/board/board.php?board=AndroidApp&command=body&no=87
activity와 task가 안드로이드에 특화된 개념인데다 개념이 좀 헤깔려서 정리하는 차원에서 Android Fundamental web page를 나름 막장번역해보았습니다. 막장번역이다 보니 의역도 많고 잘못 번역된 곳도 있을 것 같아서 많은 분들이 한번씩 보시고 교정을 좀 보아주시면 안될까요?
http://developer.android.com/guide/topics/fundamentals.html
<원문 번역>
Activity & Task
하나의 activity는 다른 activity를 시작할 수 있고 다른 application의 activity도 시작할 수 있다. 예를 들어 임의위치의 street map을 display한다고 가정하자, 이런 작업을 하는 activity는 이미 제공된다. 따라서 당신의 activity에서 이 activity를 시작하기 위해 해야 할 일은 필요한 정보를 포함하는 intent object를 startActivity()에 파라메터로 전달하여 호출하는것 뿐이다. 이렇게 하면 street map viewer가 표시될것이다. 이 때 BACK버튼을 누르면 viewer를 시작한 당신의 activity가 다시 보여질 것이다.
이는 사용자로 하여금 street map viewer가 당신의 application의 일부라고 느끼게 한다. 하지만 실제로는 그 activity는 다른 app의 다른 process상의 activity이다.
안드로이드는 이처럼 사용자가 사용한 activity들을 task로 하여 그 정보를 유지한다. 관련된 activity는 group으로 stack에 저장된다.
root activity는 task상의 첫번째 activity이고 top activity는 현재 화면에 보여지는 activity이다. activity가 다른 activity를 시작하면 그 새로운 activity가 stack에 push되고 그 activity가 top activity가 된다. 그리고 이전 activity는 stack에 남아 있는다. 이 상태에서 사용자가 BACK버튼을 누르면 이전 activity가 stack에서 POP되어 화면에 보여지게 되어 resume된다.
stack은 activity의 object(instance)를 가지고 있다. 따라서 같은 activity의 여러개의 instance가 가능하다. 같은 activity를 여러개 시작할수 있다는 의미이다.
stack내의 activity는 stack이므로 재정렬되지 않는다. 순서는 그대로 유지되게 된다. 단지 PUSH, POP만 된다.
Task는 activity들의 stack이다. 따라서 task내의 activity에 어떤 값을 설정하는 방법은 없다. root activity만이 affinity(친밀도) set을 이용하여 read, set이 가능하다.
Task의 모든 activity들은 하나의 집합으로 background또는 foreground로 이동한다. 현재 Task가 4개의 activity를 가진다고 가정해보자. HOME 키를 누르면 application launcher로 이동한다. 이어서 새로운 application을 실행한다. 그러면 현재 task는 background로 가고 새로운 task의 root activity가 표시된다. 이어 사용자가 다시 HOME으로 갔다가 이전 application을 다시 선택한다면 그 task가 다시 앞으로 나온다. 이 때 BACK키를 누르면 root activity가 표시되지 않고 task상의 이전 activity가 표시된다.
A1 -> A2 -> A3 -> A4 -> HOME -> B 1-> B2 -> HOME -> A4 -> BACK -> A3
task와 activity간의 결합과 동작에 대한 제어는 intent object의 flag 파라메터와 minifest의 <activity> element의 몇가지 속성으로 제어가 가능하다.
flag -> FLAG_ACTIVITY_NEW_TASKFLAG_ACTIVITY_CLEAR_TOP, FLAG_ACTIVITY_RESET_TASK_IF_NEEDED, FLAG_ACTIVITY_SINGLE_TOP
<activity>'s attributes -> taskAffinity, launchMode, allowTaskReparenting, clearTaskOnLaunch, allowRetainTaskState, finishOnTaskLaunch
Affinityes and new Tasks
기본적으로 하나의 application의 activity들은 각기 하나의 affinity를 갖는다. 그러나 각각의 affinity들은 <activity> element의 taskAffinity속성으로 affinity set을 이룰수 있다. 서로 다른 application의 activity들이 동일한 affinity를 공유할 수 있으며 한 application의 activity들이 서로 다른 affinity를 가질수 있다. affinity는 intent object에 FLAG_ACTIVITY_NEW_TASK로 activity를 적재할 때와 activity가 allowTaskReparenting속성을 true로 set하였을 때 시작된다.
FLAG_ACTIVITY_NEW_TASK 적용시
앞서 기술한대로 기본적으로 activity는 startActivity()로 task안에 적재된다. caller와 동일한 stack에 push된다. 그러나 startActivity()가 FLAG_ACTIVITY_NEW_TASK 로 flag를 set하여 호출하면 시스템은 새로운 activity를 담기위한 task를 찾는다. 보통 새로운 task가 생성되지만 동일한 affinity를 갖는 task가 검색되면 그 태스크에 가서 달라붙는다.
allowTaskReparenting 적용시
특정 activity가 allowTaskReparenting속성이 "true"이면, 시작된 task에서 동일한 affinity를 갖는 다른 task가 foreground로 올라올때 그 task로 activity가 이동될 수 있다. 예를 들면, 특정도시의 날씨를 보여주는 activity를 가지고 있는 travel application이 있다고 하자. travel application에 동일한 affinity를 갖는 다른 activity가 있고 reparenting이 가능하다. 이 상태에서 당신의 application의 activity가 travel application의 날씨 activity를 시작하면 날씨 activity는 당신의 task에 적재되게 된다. 그러나 이 때 travel application이 적재되게 되면 날씨 activity는 새로 시작된 travel application의 task에 재위치지정이 되고 화면에 표시되어진다.
travel application : Weather activity, ... -> allowTaskReparenting이 true이고 모두 동일 affinity를 갖는다.
your application : A, B, C, D activity
launch travel application -> (1)start Weather activity -> HOME -> launch your application -> start A activity -> (2)start Weather activity -> HOME -> (3)travel application -> display Weather activity
(1) 시점에서 weather activity는 task1(travel app의 task)에 적재된다. (2)시점에서 weather activity는 task2(your app의 task)에 적재된다. (3)의 시점에서 travel app가 다시 시작될때 task2에 있던 weather activity가 task1으로 재지정되게 된다.
하나의 패키지(*.apk)에 여러 application이 정의되어 있다면 app단위로 각기 다른 affinity를 부여하는것이 좋다.
Launch Mode
<activity> element에 lounchMode 속성을 조정하여 activity를 컨트롤할 수 있다.
standard, singleTop, singleTask, singleInstance
위 4가지 모드는 4가지 관점에서 다르게 동작한다.
* 임의 Intent에 대해 어떤 task가 그 activity를 받을 것인가?
standard, singleTop모드는 intent가 발생된 task에 push된다. flag를 FLAG_ACTIVITY_NEW_TASK로 설정해도 호출한 동일한 task내에 push된다. 위에 기술한 affinity & new task에 따라 다른 task가 선택될수 있다.
singleTask및 singleInstance는 task를 정의하여 root activity로 적재되고 다른 task에 적재되지 않는다.
* 다중 instance activity가 가능한가?
standard, singleTop모드는 여러 task에 소속될수도 있고 한 task에 동일한 activity가 여러 instance가 적재될수도 있다.
singleTask, singleInstance는 task내에서 오로지 한개의 instance만 적재된다. root activity만 가능하기 때문에 device내에서 한번에 하나의 Instance만 존재할 수 있다.
* Instance가 task내에서 다른 activity를 가질수 있는가?
singleInstance는 task내에서 오직 하나의 instance만 가능하며, 만일 다른 activity를 시작하면 launchMode에 관계없이 새로운 task가 생성되어 적재된다.
standard, singleTask, singleTop은 모두 multi instance가 가능하다. singleTask는 root activity로 생성되며 다른 activity를 task내에 적재가 가능하다. singleTop과 standard모드는 stack내에서 자유롭게 다른 activity를 생성가능하다.
* 특정 class의 새로운 instance가 새로운 intent를 다룰것인가?
standard모드는 새로운 instance가 새로운 intent의 응답으로 생성된다. 각 instance는 오직 하나의 intent를 다룬다.
singleTop : target-task의 stack에 top activity로 있다면 그 class의 instance가 intent를 재사용하여 처리한다. top activity가 아니면 재사용하지 않고 새로운 instance가 생성되어 intent를 처리하고 stack에 push된다.
예) A - B - C - D에서 D를 시작하려고 할 때 D가 singleTop이면 A - B - C - D 로된다.
A - B - C - D에서 D를 시작하려고 할 때 D가 standard이면 A - B - C - D - D 로된다.
B가 singleTop이나 standare이면 A - B - C - D - B 가 가능하다.
Clearing the stack
기본적으로 사용자가 task를 오랫동안 사용하지 않으면 system은 task의 root activity만을 제외하고 모든 activity들을 clear한다. <activity>element의 몇가지 속성은 이를 조정할 수 있게 해준다.
alwaysRetainState 속성
task의 root activity에 이 속성을 set하면 이 task는 오랜시가이 지나도 생존하게 된다.
clearTaskOnLaunch 속성
task의 root activity에 이 속성을 set하면 task를 나가고 돌아올때 clear된다.
finishOnTaskLaunch
clearTaskOnLaunch와 유사하나 이 속성은 하나의 activity에만 유효하다. root activity를 포함하여 현재 세션인 경우에만 살아있고 task를 떠나면 clear된다.
stack에서 activity를 제거하는 다른 방법이 있다.
intent object의 flag를 FLAG_ACTIVITY_CLEAR_TOP로 하고 이 intent를 처리할 activity가 target task에 이미 instance를 가지고 있다면 상위 activity들이 모두 clear되고 그 activity가 top activity가 된다. launchMode가 "standard"라면 stack에서 마찬가지로 삭제되고 새로운 activity가 그 intent를 처리할 것이다.
FLAG_ACTIVITY_NEW_TASK와 FLAG_ACTIVITY_CLEAR_TOP이 함께 사용되면 존재하는 activity가 새 task에 생성되어 intent를 처리한다.
Starting task
activity는 intent filter중에 action filter로 android.intent.action.MAIN를 그리고 category filter로 android.intent.category.LAUNCHER로 entry point가 설정된다. 이런 설정은 icon과 label정보를 가지로 화면에 표시하고 task에 적재하고 적재후 언제든지 다시 돌아올수 있도록 해준다.
사용자는 언제든 task를 떠날수 있고 다시 돌아올수 있다. singleTask와 singleInstance로 설정된 activity는 반드시 MAIN과 LAUNCHER를 filter로 적용해야 한다. 그러지 않으면 activity를 수행후 다른 화면으로 갔다가 다시 돌아올 수 있는 방법이 없게 된다.
FLAG_ACTIVITY_NEW_TASK 는 activity하나가 새로운 task에 시작되고 HOME key를 눌렀을 경우 다시 복귀하기 위해 다른 방법이 있다.
외부 task에서 notification manager같은 entity에서 항상 activity들을 시작할 수 있다. 이런 방식으로 외부에서 activity를 invoke할 수 있다면 사용자가 다른 방법으로 그 task를 시작할 수 있음을 유의해야 한다. 이런 경우 그 activity로 복귀하기를 원하지 않는다면 finishOnTaskLaunch를 사용하면 된다.
'IT_Programming > Android_Java' 카테고리의 다른 글
Android Layout Tricks #2 : Reusing Layouts. (0) | 2014.06.17 |
---|---|
[펌] Android Layout Tricks #1 ( Layout 최적화하기 ) (0) | 2014.06.17 |
Parcelable 를 구현할 때 주의점 (ArrayList) (0) | 2014.06.02 |
액티비티의 상태를 저장하고 복원하기 (0) | 2014.06.02 |
[펌] Android MediaCodec과 MediaMuxer! API 살펴보기 (0) | 2014.05.30 |