안드로이드에서 Activity
의 실행 방식과 Task
관리 방식 이해를 위해, 매니페스트 및 Intent에서 설정하는 launchMode
와 개념을 포스팅하고자 한다.
launchMode
는 액티비티가 새로운 인스턴스로 생성될지, 기존 인스턴스를 재사용할지를 정의하는 속성으로 이는 Manifest
와 Intent의 flag
로 설정 가능하다.
Manifest
든, Intent.flag
든, 아무것도 설정하지 않았을 때 선택되는 기본 실행모드로, 액티비티 객체를 새로 생성한다. 아래 그림과 같이 BackStack
or Task
에 존재하는 Activity
를 생성할 때, 무조건적으로 새로운 객체가 생성된다.
위 사진은 A Task
최 상단에 동일한 Activity
가 있음에도 불구, 재활용 없이 새로운 객체를 생성한다. 즉, Back Stack
에 어떤 Activity
객체가 있든 새로운 것을 생성한다.
위 사진은 A Task
와 B Task
가 나누어 존재할 뿐, 위와 같은 흐름이다. 즉, Task
에 어떤 Activity
객체가 있든 새로운 것을 생성한다.
위 사진은 A Task
와 B Task
가 나뉘어 있다. 하지만 B Task
에 C_Activity
가 존재함에도 불구, A Task
에 C_Activity
생성 시도 시, 그대로 생성한다. 즉, Task
에 어떤 Activity
객체가 있든 새로운 것을 생성한다.
[그 이후 launchMode의 공통점?]
이후 다룰Manifest
의 3가지 실행모드(singleTop
,singleTask
,singleInstance
)는standard
와 달리 기존Activity
를 재사용한다는 공통점이 있다. 풀어 말하자면, 특정Activity
에서BackStack
orTask
에 남아있는Activity
를 또 다시 호출한다고 했을 때, 이는 기존Activity
의 인스턴스를 재호출함과 동시에onNewIntent()
생명주기 콜백 메서드를 호출한다는 의미이다.
[adb를 사용한
Activity
백스택 조회 법]
adb shell dumpsys activity activities | grep "Hist.*com.sample.app"
하나의 Back Stack
의 최 상단엔 동일한 Activity
는 1개임을 선언하는 실행모드이다.
아래 사진은 백스택 최 상단에 C_Activity
가 존재한다. 이때 동일한 것을 호출했을 경우, 새로운 객체를 생성하는게 아니라 onNewIntent
를 호출하며 기존 객체를 재사용하게 된다. 또한 사진 밑엔 adb명령어를 사용해 C_Actiity
를 연속적으로 호출해서 결과를 확인해 보면, C_Activity
는 최 상단에 1개만 존재하는 걸 볼 수 있다.
[Before]
[After]
하지만 아래 사진을 보면, 적재하려는 Activity
가 연속적이지 않을 땐 재활용하는게 아닌, 새로운 C_Activity
객체를 새로 생성하여 적재한다. 즉, singleTop
이란 이름에 맞게, BackStack
의 최 상단, Top엔 오로지 단 하나, single만 존재한다는 것이다.
[Before]
[After]
전체 Task Stack
에서 유일한 액티비티임을 선언하는 실행모드로 보통 2가지 시나리오를 인지하면 된다.
첫 번째는 자신을 시작시킨 Activity
와 taskAffinity
를 비교 후, 동일하다면 기존 Task
에 적재한다. 반면, 그렇지 않은 경우 새로운 Task
를 만들어 해당 Activity
를 적재한다는 점이다.
두 번째는 실행시킨 singleTask
가 선언된 Activity
를 실행했을 때, 이 Activity
가 이미 특정 Task
에 존재하고, 그 위에 다른 Activity
들이 존재할 경우, 이들을 모두 onDestroy()
시켜버린 후, 호출한 singleTask
의 Activity
를 onNewIntent()
호출해가며 재활용 한다는 점이다.
아래 사진은 첫 번째 시나리오를 나타내고 있다. singleTask
는 해당 Activity
시작 시, 자신을 호출한 Activity
의 taskAffinity
와 자신의 taskAffinity
의 동등성 비교를 진행한다. 만약 같다면 기존에 존재하는 Task
에 적재하지만, 그렇지 않다면 새로운 Task
를 생성해 적재한다.
[Before]
[After]
아래 사진은 새롭게 실행시키는 A_Activity
의 taskAffinity
가 다른 경우이다. taskAffinity
가 다르므로 새로운 Task
를 생성 후 이를 적재하는 모습이다.
[Before]
[After]
아래 사진은 두 번째 시나리오를 나타내고 있다. A Task
의 밑에서 2번째에 A_Activity
가 singleTask
로 존재한다. 이때, 최 상단에서 이를 재호출 한다면, A_Activity
의 onNewIntent()
호출로 기존 Activity
를 재활용함과 동시에, 그 상위에 있는 Activity
들을 모두 onDestroy()
시킨다.
[Before]
[After]
하지만 아래 사진과 같이, 이미 존재하는 Task
가 2개 이상인 경우도 있다. 만약 A Task
에 singleTask
로 선언 한 A_Activity
가 존재하며, 그 상단에 B_Activity
/C_Activity
가 존재한다. 이때, 포그라운드 상태인 B_Task
에서 singleTask
로 선언 한 A_Activity
를 호출하면 어떻게 될까? singleTask
는 앱 전체 Task
상 오로지 1개의 Activity
만 존재하도록 선언하는 시작모드인 만큼, A Task
가 포그라운드로 올라오며, A_Activity
의 onNewIntent()
를 호출함과 동시에 그 상위 Activity
들을 onDestroy
시킨다.
[Before]
[After]
singleInstance
로 선언 한 Activity
를 실행시킨다면 무조건 새로운 Task
를 할당한다. 그 후, 이 Task
에서는 그 어떠한 Activity
는 적재될 수 없으며, 이전에 가용 가능한 Task
에서 생성되도록 하는 시작모드이다.
[Before]
[After]
[singleInstancePerTask]
singleInstance
와 기본동작은 동일하다. 다만, singleInstance
에 존재하는 Activity
는 전체 Task
상 1개만 존재할 수 있지만 singleInstancePerTask
는 여러 Task
에서 해당 Activity
를 포함 한 형태로 여럿이 존재할 수 있다는 점이 차이이다.
다만,
singleInstancePerTask
사용을 위해,Intent.flag
를 아래와 같이 꼭 선언해줘야 한다.Intent(this@A_Activity, A_Activity::class.java).apply { flags = FLAG_ACTIVITY_MULTIPLE_TASK or FLAG_ACTIVITY_NEW_DOCUMENT startActivity(this) }
아래는 singleInstance
를 선언 한 A_Activity
의 재호출이다. 물론 Intent.flag
로 위와 동일하게 준 상태이다. 그럼에도 불구, A_Activity
객체 재생성이 아닌, onNewIntent()
의 재호출 및 재활용하고 있음을 확인할 수 있다.
[Before]
[After]
반면, 아래는 singleInstancePerTask
로 선언된 Activity
를 여러번 반복 실행했을 때이다. 해당 Activity
가 매번 새로운 Task
와 함께 생성되는 것을 확인할 수 있다.
[Before]
[After]
이는 Manifest의 launchMode 중, 위에서 설명 한 singleTop
과 동일한 동작이다. 설명은 이하 생략한다.
이는 Manifest의 launchMode 중, 위에서 설명 한 singleTask
과 동일한 동작이다. FLAG_ACTIVITY_NEW_TASK
는 새로 시작할 Activity
에게 새로운 Task
를 할당해주며, FLAG_ACTIVITY_CLEAR_TOP
는 새로 시작하려는 Activity
가 이미 특정 Task
상에 존재하며 그 위에 다른 Activity
들이 있을 때, 이들을 모두 onDestroy
를 시켜준다. 그림을 포함한 설명은 생략한다.