2010년 12월 30일 목요일

Activities and Tasks

아래 글의 원문은 http://jcjeon.tistory.com/25 입니다.




Activities and Tasks 

하나의 Activity는 다른 Applicaiton에 정의된 것을 포함해서, 동일한 Application내이 또 다른 Activity를 실행 시킬 수 있다. 비록 Activity가 정의된 곳과 사용되는 Applicaiton이 다르긴 하지만, 사용자에게는 실행된 Activity가 마치 동일한 Application의 일부라고 느껴질 수 있다. Android는 동일한 Task에 두 개의 Activity를 유지하게 함으로써 이와 같은 동작(사용자가 경험하는)을 유지하게 된다.

간단히 말하자면, Task는 사용자가 "Application"으로 경험하는 것이다. 즉, Stack에 정렬되는 Activities의 관계 그룹이 된다. Stack에서 Root Activity는 Task를 시작하는 Activity로, 일반적으로 사용자가 Applicaiton Launcher에서 선택한 Activity가 된다. Stack의 맨위에 있는 Activity는 현재 동작중이 되며 사용자 Actions에 대한 포커스를 갖는다. 하나의 Activity가 다른 Activity를 실행시키면, 새로운 Activity는 Stack에 넣어지고 Running Activity가 되며 이전 Activity는 Stack에 유지된다. 사용자가 BACK Key를 누르면 현재 Activity는 Stack에서 꺼내지고 이전 Activity가 Running Activity로 Resume된다.

Stack은 Objects을 포함한다. 그래서 만일 Multiple Map viewer와 같이 같은 Activity subclass의 인스턴스를 하나 이상 Open하여 가지고 있다면, Stack은 각각의 Instance에 대한 분리된 Entry를 가지게 된다. Stack에 있는 Activities은 절대로 재정렬되지 않고 Push와 Pop만 된다.
Task는 Activities의 Stack이지 Manifest파일에서의 요소나 부류등이 아니다. 그래서 Activity와 독립적으로 Task에 대한 값을 설정할 수 있는 방법이 없다. Task에 대한 값들은 대체로 Root Activity에서 설정된다. 

Stack에 존재하는 모든 Activities은 집합체로 함께 이동 된다. 전체 Task(entire activity stack)는 Forground나 Background로 보내질 수 있다. 현재 Task가 자신의 Stack에 4개의 Activity(현재 Activity아래에는 3개가 있음)를 가지고 있다고 가정해보자. 사용자는 HOME key를 누르고 Applicaion Launcher로 이동한다. 그리고 새로운 Applicaiton (실제로는 새로운 Task)을 선택한다. 현재 Task는 Background로 바뀌고 새로운 Task에 대한 Root Activity가 보여지게 된다. 그 다음, 잠깐 기다렸다가 사용자는 Home 화면으로 되돌아 가서 다시 이전 Application을 선택한다. Stack에 4개의 Activity를 가지고 있던 그 Task가 앞으로 나오게 된다. 사용자가 BACK Key를 누르면 화면에는 이전 Task의 Root Activity가 보여지지 않고 Stack의 맨 위에 있던 Activity가 제거되고 동일한 Task내의 이전 Activity가 보여지게 되는 것이다.

위에 언급된 동작은 Activities와 Tasks에 대한 기본 동작이고 그러한 동작의 대부분을 변경하는 방법이 존재한다. Activities와 Task의 관계, 즉 Task내에서의 Activity의 동작은 Activity를 시작하게 하는 Intent Object에서 설정된 Flags과 Manifest에 있는 Activity의 요소에 설정된 속성값들 사이의 상호작용에 의해 제어될 수 있다. 요청한는 쪽과 응답하는 쪽 모두 무엇이 발생하는지에 대해 말할 자격이 있게 된다.

중요한 Intent Flags :
FLAG_ACTIVITY_NEW_TASK 
FLAG_ACTIVITY_CLEAR_TOP 
FLAG_ACTIVITY_RESET_TASK_IF_NEEDED 
FLAG_ACTIVITY_SINGLE_TOP

중요한 Attributes :
taskAffinity 
launchMode 
allowTaskReparenting 
clearTaskOnLaunch 
alwaysRetainTaskState 
finishOnTaskLaunch


Affinities and new tasks 

Applicaiton내의 모든 Activities은 기본적으로 서로에 대한 Affinity(친밀도, 연관성)를 가지고 있다. 즉, 동일 Task에 속한 모든 것들에 대한 Preference가 있다. 그러나, 각각의 Affinity는 요소의 taskAffinity 속성을 가지고 각 Activity에 대하여 설정이 될 수 있다. 서로 다른 Application에 정의된 Activities은 Affinity를 공유할 수 있고 동일한 Application에 정의된 Activities은 서로 다른 Affinities로 할당 될 수 있게 된다.
Affinity는 두 가지 상황(Activity를 시작시킨 Intent Object가 "FLAG_ACTIVITY_NEW_TASK" flag를 포함하고 있을 때와 Activity가 "allowTaskReparenting" 속성을 "true"로 가질 때)에서 동작이 시작된다.
FLAG_ACTIVITY_NEW_TASK flag
앞서 언급한대로 새로운 Activity는 startActivity()를 호출한 Activity의 Task에서 시작된다. 즉, 호출자와 같은 Stack에 넣어진다. 그러나, startActivity()로 넘겨진 Intent Object가 FLAG_ACTIVITY_NEW_TASK flag를 포함하고 있다면 시스템은 새로운 Activity를 거주시키기 위한 다른 Task를 찾게 된다. 가끔 그러한 Flag는 그렇게 될 필요는 없지만, 새로운 Task를 의미하기도 한다.  새로운 Activity와 같은 Affinity를 가지는 Task가 이미 존재한다면 Activity는 그 Task에서 시작되면 된다. 그게 없다면 새로운 Task를 시작하면 된다.

allowTaskReparenting attribute
Activity가 allowTaskReparenting 속성을 "true"로 가지면 Activity를 시작시킨 Task로부터 Affinity를 가지는 Task가 앞으로 나왔을 때를 위해 Affinity를 가지는 Task로 이동이 가능해진다. 예를 들어, 선택된 도시에서 날씨 정보를 리포트해주는 Activity가 Travel Applicaion의 일부로 선언되어 있다고 가정해 보자. 이러한 Activity는 같은 Application내에서 다른 Activities과 같은 Affinity를 가지게 되고 Re-parenting이 가능하도록 해준다. 사용자의 Activities중 하나가 날씨 리포터를 시작시키면, 그것은 사용자 Activity와 같은 Task에 속하게 된다. 그러나, Travel Application이 이후에 앞으로 나오게 되었을 때, 날씨 리포터는 재할당되고(이동되고) 그 Task와 함께 보여지게 될 것이다. 다시 사용자 Activity가 보여지게 되면 날씨 리포터는 보이지 않게 된다. 비슷한 예제로, e-mail message가 web page에 대한 링크를 가지고 있고 링크를 클릭하면 해당 페이지가 보여지는 Activity가 실행되게 된다. 그러한 Activity는 Browser Application에 의해 정의되어 있지만 e-mail Task의 일부로 실행된 상태이다. 만일 해당 속성값이 "true"로 설정된 상태이고 Browser Task로 Reparenting되면 Browser가 실행될 때 그 페이지가 보여질 것이고 다시 e-mail Task가 보여지게 되면 그 Activity는 보이지 않게 되는 것이다.
만약 .apk파일이 사용자가 보는 관점에서의 "Application"을 하나 이상 포함하고 있다면 그것들 각각에 연관된 Activities에 대해 다른 Affinity를 할당하고 싶을 것이다.


Launch modes 

요소의 launchMode 속성으로 할당될 수 있는 것은 다음과 같이 4가지 launch mode가 있다.
   "standard" (the default mode) 
   "singleTop" 
   "singleTask" 
   "singleInstance"

Modes은 두 개의 main Group으로 나뉜다(standard, sigleTop / singleTAsk, singleInstance). "standrad" 혹은 "singleTop"의 Mode를 가지는 Activities은 여러번 인스턴스화될 수 있으나(Intent Object가 "FLAG_ACTIVITY_NEW_TASK" flag를 가지고 있지 않을 경우에 해당함) "singleTask" 혹은 "singleInstance" Activities은 단지 하나의 Task만 시작할 수 있는 차이가 있다.
"standard" vs. "singleTop" : "standard" Activity에 대한 새로운 Intent가 발생하면 언제나 해당 Class의 Instance가 만들어 지지만 "singleTop" Activity의 새로운 Instance는 생성될 수 도 있다. 즉, Target Task에 이미 해당 Activity의 Instance가 존재하면 새로 만들어지지 않고 기존 Instance가 새로운 Intent를 받게 된다.
"singleTask" vs. "singleInstance" : "singleTask" Activity는 다른 Activities이 해당 Task의 일부가 되는 것을 허락하여 "singleTask" Activity는 Activity Stack의 Root에 존재하게 되고 다른 Activities은 동일한 Task에서 시작될 수 있다. 이와 달리 "singleInstance" Activity는 어떤 다른 Activities도 해당 Task의 일부가 될 수 없다. 이로 인해 해당 Task에는 유일한 Activity만 존재하고 또 다른 Activity를 실행하려고 하면 새로운 Task로 할당이 된다.
각각의 Mode는 다음의 4가지 점에서 서로 구분된다 :
Intent에 응답하는 Activity를 어떤 Task가 가질 것인지. "standard"와 "singleTop" 모드에서는, 만일 Intent Object가 "FLAG_ACTIVITY_NEW_TASK" flag를 포함하고 있지 않다면 Intent를 발생시킨 Task가 된다. 그와 달리, "singleTask"와 "singleInstance" 모드는 Activities을 항상 Task의 Root에 있음을 표시한다. 두 가지 모드의 Activities은 하나의 Task를 정의한다. 즉, 해당 Activities은 절대로 다른 Task에서 시작되지 않는다.(하나의 Task에서만 실행된다.)
Activity의 Multiple Instances가 될 수 있는지의 여부.
 "standard"와 "singleTop" Activities은 여러번에 걸쳐 인스턴스화 될 수 있고 Multiple Tasks에 속할 수 있으며 주어진 Task는 동일한 Activity의 Multiple Instances를 가질 수 있다. 반대로, "singleTask"와 "singleInstance" Activities은 단지 하나의 Instance로 제한된다.

Instance가 그것의 Task내에 다른 Activities를 가질 수 있는지의 여부. "singleInstance" Activity는 그것의 Task내에서 단일 Activity로만 존재한다. 만일 다른 Activity를 시작시키면 그 Activity는 launch mode에 상관없이 다른 Task에서 시작된다(마치 "FLAG_ACTIVITY_NEW_TASK" flag가 Intent에 있는 것처럼 동작).
다른 3개의 모드들은 Multiple Activities이 Task로 포함되는 것을 허가해준다. "singleTask" Activity는 항상 Task의 Root Activity에 있을 수 있지만 그것의 Task에 할당될 수 있는 다른 Activities을 시작할 수 있다. "standard"와 "singleTop" Activities의 Instance는 Stack의 어느 곳에든 존재 할 수 있다.

클래스의 새로운 Instance가 새로운 Intent를 다루도록 시작될 수 있는지의 여부. "standard" 모드에서, 새로운 Instance는 모든 새로운 Intent에 대한 응답으로 만들어 진다. "singleTop" 모드에서는, Class의 Instance가 기존에 존재하고 그것이 Target Task의 Activity Stack의 맨 위에 있다면 새로운 Intent를 다루기 위해 재사용 된다. Stack의 맨위에 없다면 재사용 되지 않는 대신, 새로운 Instance가 새로운 Intent를 위해 생성되고 Stack에 넣어지게 된다.
예를 들어, Task의 Activity Stack이 root Activity A, Activity B, C, and D의 순서로 구성되고 Stack이 A-B-C-D의 순서가 된다고 가정해보자. Type D의 Activity를 위한 Intent가 받아지고 D가 "standard" launch mode를 갖는다면 Class의 새로운 Instance가 시작되고 그러한 Stack은 A-B-C-D-D가 된다. 그러나, 만일 D의 launch mode가 "singleTop"이면 기존 Instance가 새로운 Intent를 처리하는 것이 예상되는데 현재 Stack의 맨 위에 있으므로 Stack은 A-B-C-D를 그대로 유지하게 된다.

만약 Type B의 Activity를 위한 Intent가 도착한다면, B가 Stack의 맨 위에 있는 것이 아니기 때문에 B의 모드가 "standard" 혹은 "singleTop"이더라도 B의 Instance가 시작될 것이고 결과적으로, Stack은 A-B-C-D-B가 된다.

위에 말했듯이, "singleTask"나 "singleInstance" mode에서는 두개 이상의 Instance가 있을 수 없다. 그래서 그러한 Instance는 새로운 모든 Intents을 처리하도록 요구된다. "singleInstance" Activity는 Task에서 유일한 Activity로 항상 Stack의 맨위에 존재한다. 그러나, "singleTask" Activity는 Stack에 자신의 위에 다른 Activities을 가질수도 있고 갖지 못할 수도 있다. 만약 Intent를 다룰수 있는 위치에 있지 않다면 Intnet는 dropp된다.
기존의 Activity가 새로운 Intent를 처리하는 요청을 받았을 때, Intent Object는 onNewIntent() 호출을 통해 Activity로 전달된다. Activity의 새로운 Instance가 새로운 Intent를 생성할 때 사용자는 이전 상태로 돌아가기 위해서는 BACK Key를 언제나 사용할 수 있다. 그러나 Activity의 기존 Instance가 새로운 Intent를 다루게 될 때 사용자는 새로운 Intent가 도착하기 전에 Instance가 하고 있던 것으로 돌아가기 위해서 BACK Key를 사용할 수 없다.

Clearing the stack 

사용자가 Task를 오랫동안 방치하면 시스템은 Root Activity를 제외한 모든 Activities의 Task를 Clear시킨다. 사용자가 다시 Task로 돌아왔을 때 초기 Activity가 보여지는 것을 제외하고는 사용자가 남겨둔 그대로가 된다. 이런 생각은 시간이 지난 후 사용자는 전에 하고 있었던 일을 잊고 새로운 무엇가를 하기 위해 Task로 되돌아 오는 것으로 여기는 것이다. 

기본적인 동작은 이와 같으나 이러한 동작을 제어하거나 변경할 수 있는 Activity 속성이 몇가지 있다.
alwaysRetainTaskState attribute : Task의 Root Activity에 "true"로 설정되어 있다면 앞에 언급되었던 기본 동작은 발생하지 않는다. Task는 오랜 시간 이후에도 Stack에 있는 모든 Activity를 유지한다.
clearTaskOnLaunch attribute : 이 속성이 "true"로 설정되어 있으면 사용자가 Task를 떠났다가 다시 돌아올 때마다 Stack은 Root Activity로 정리된다. 다시말해, 이것은 "alwaysRetainTaskState"와 정반대이다. 아주 짧은 시간을 떠난 이후라도 사용자는 항상 초기 상태의 Task로 돌아온다.
finishOnTaskLaunch attrubute : 이 속성은 "clearTaskOnLaunch"와 유사하지만 전체 Task가 아닌 단일 Activity에서 동작한다. 그리고 그것은 Root Activity를 포함한 어떤 Activity가 사라지는 원인이 될 수도 있다. "true"로 설정되어 있을 때, Activity는 현재 Session 동안 Task의 일부만 유지한다. 만일 사용자가 해당 Task를 벗어났다가 다시 돌아오면 더이상 존재하지 않는다.

Stack에서 Activities를 강제로 제거하는 또다른 방법이 있다. Intent Object가 FLAG_ACTIVITY_CLEAR_TOP flag를 포함하고 있고 Target Task가 이미 Intent를 다루는 Stack에 있는 Activity의 Type에 대한 Instance를 가지고 있다면
그러한 Instance위에 있는 모든 Activities은 사라지고 그것은 Stack의 맨 위에 있게 되어 Intent에 응답할 수 있다. 만일 지정된 Activity의 launch 모드가 "standard"이면 그것 역시 Stack에서 제거될 것이고 새로운 Instance가 들어오는 중인 Intent를 처리하기 위해 시작될 것이다. 그것은 launch mode가 "standard"일 때, 새로운 Instance는 항상 새로운 Intent를 위해 만들어지기 때문이다.

FLAG_ACTIVITY_CLEAR_TOP 은 FLAG_ACTIVITY_NEW_TASK와 합쳐져 가장 많이 사용된다. 함께 사용되면 이러한 Flags은 다른 Task로 기존의 Activity를 위치시키고 Intent에 응답할 수 있는 위치로 그것을 넣을 수 있는 방법이 된다.
 
Starting tasks 

Activity는 "android.intent.action.MAIN"의 Action과 "android.intent.category.LAUNCHER"의 Category를 가지는 Intent Filter를 제공함으로써 Task의 Entry Point로 셋업된다. 이러한 종류의 Filter는 사용자에게 해당 Task를 실행시킬 수 있는 방법과 실행 이후 언제든지 되돌아 갈 수 있는 방법을 제공함으로써 Activity를 위한 Icon과 Label이 Application Launch에 보여지도록 한다.

두번째 기능이 아주 중요한데 사용자는 Task를 떠났다가 이후에 다시 돌아올 수 있어야만 한다. 이러한 이유 때문에 Task를 항상 초기화하는 것으로 Activities을 표시하는 두 가지 Launch mode("singleTask" and "singleInstance")는 Activity가 MAIN과 LAUNCHER Filter를 가질 때에만 사용되어야 한다. Intent는 새로운 Task를 초기화함으로써 "singleTask" Activity를 시작시키고 사용자가 해당 Task에서 약간의 작업 시간을 보내게 된다. 그다음 사용자는 HOME Key를 누른다. Task는 이제 보이지 않는 곳에서 지시를 받게 되고 HOME 화면은 어두워 진다. 그리고 Applicaion Launcher에서 보여지지 않기 때문에 사용자는 그것으로 돌아갈 방법이 없게 된다.

비슷한 어려움이 FLAG_ACTIVITY_NEW_TASK flag에도 나타난다. 이러한 flag는 Activity로 하여금 새로운 Task를 시작시키고 사용자는 그곳에서 빠져나가기 위해 HOME Key를 누르면, 사용자가 다시 그곳으로 되돌아 갈 수 있는 어떤 방법이 있어야만 한다. Notification Manager와 같은 몇몇 Entities은 항상 Activities를 자신들의 Task의 일부가 아닌 외부 Task에서 시작한다. 그래서 startActivity()로 넘길 Intents에는 FLAG_ACTIVITY_NET_TASK를 항상 넣어준다. 만일 이런 Flag를 사용하는 외부 Entity에 의해 실행될 수 있는 Activity를 가지고 싶다면 사용자는 그들이 시작했던 Task로 되돌아 올 수 있는 독립적인 방법을 가져야만 함을 알고 있어야 한다. 

사용자가 Activity로 돌아올 수 있는 것을 원치 않는 경우에는, 요소의 finishOnTaskLaunch를 "true"로 세팅하면 된다.

댓글 없음:

댓글 쓰기