
안드로이드에서 Activity의 실행 방식과 Task관리 방식 이해를 위해, 매니페스트 및 Intent에서 설정하는 launchMode와 개념을 포스팅하고자 한다.
launchMode는 액티비티가 새로운 인스턴스로 생성될지, 기존 인스턴스를 재사용할지를 정의하는 속성으로 이는 Manifest와 Intent의 flag로 설정 가능하다.
Manifest든, Intent.flag든, 아무것도 설정하지 않았을 때 선택되는 기본 실행모드로, 액티비티 객체를 새로 생성한다. 아래 그림과 같이 Task에 존재하는 Activity를 생성할 때, 무조건적으로 새로운 객체가 생성된다.

위 사진은 A Task 최 상단에 동일한 Activity가 있음에도 불구, 재활용 없이 새로운 객체를 생성한다. 즉, Task에 어떤 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에서Task에 남아있는Activity를 또 다시 호출한다고 했을 때, 이는 기존Activity의 인스턴스를 재호출함과 동시에onNewIntent()생명주기 콜백 메서드를 호출한다는 의미이다.
[adb를 사용한
Activity백스택 조회 법]
adb shell dumpsys activity activities | grep "Hist.*com.sample.app"
하나의 Task의 최 상단엔 동일한 Activity는 1개임을 선언하는 실행모드이다.
아래 사진은 Task 최 상단에 C_Activity가 존재한다. 이때 동일한 것을 호출했을 경우, 새로운 객체를 생성하는게 아니라 onNewIntent를 호출하며 기존 객체를 재사용하게 된다. 또한 사진 밑엔 adb명령어를 사용해 C_Actiity를 연속적으로 호출해서 결과를 확인해 보면, C_Activity는 최 상단에 1개만 존재하는 걸 볼 수 있다.

[Before]
[After]
하지만 아래 사진을 보면, 적재하려는 Activity가 연속적이지 않을 땐 재활용하는게 아닌, 새로운 C_Activity객체를 새로 생성하여 적재한다. 즉, singleTop이란 이름에 맞게, Task의 최 상단, Top엔 오로지 단 하나, single만 존재한다는 것이다.

[Before]
[After]
전체 Task에서 유일한 액티비티임을 선언하는 실행모드로 보통 2가지 시나리오를 인지하면 된다.
첫 번째는 자신을 시작시킨 Activity와 taskAffinity를 비교 후, 동일하다면 기존 Task에 적재한다. 반면, 그렇지 않은 경우 새로운 Task를 만들어 해당 Activity를 적재한다는 점이다.
두 번째는 Activity(singleTask선언)를 실행했을 때, 이것이 이미 특정 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(=D_Activity)에서 singleTask로 선언 한 A_Activity를 호출하면 어떻게 될까? singleTask는 앱 전체 Task상 오로지 1개의 Activity만 존재하도록 선언하는 시작모드인 만큼, A Task가 포그라운드로 올라오며, A_Activity의 onNewIntent()를 호출함과 동시에 그 상위 Activity들을 onDestroy시킨다.

[Before]
[After]
singleInstance로 선언 한 Activity를 실행시킨다면 무조건 새로운 Task를 할당한다. 그 후, 이 Task에서는 그 어떠한 Activity는 적재될 수 없으며, 이전에 가용 가능한 Task에서 생성되도록 하는 시작모드이다.

[Before]
[After]
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를 시켜준다. 그림을 포함한 설명은 생략한다.