저는 AndroidManifest에서 한 Activity가 여러 번 인스턴스화 되는 것을 피하기 위해 SingleTask 또는 SingleInstance를 구분하지 않고😓 적어주곤 했습니다. 특히 비밀번호 재입력 같이 똑같은 뷰에서 로직만 조금 달라져야 할 때 자주 사용하였습니다.
각 launchMode를 헷갈리지 않고 쓰기 위해 테스트 해보고 정리해보았습니다.
관련 Android Docs 링크 : https://developer.android.com/guide/components/activities/tasks-and-back-stack
아래 테스트 내용들은 깃헙 레포에서 브랜치별로 확인하고 다른 궁금한 것들을 테스트해보면 재밌을 것 같습니다. 🤗
https://github.com/yebonkim/test-launchmode
Standard는 AndroidManifest의 launchMode의 기본값으로 launchMode 필드를 생략해도 설정됩니다. 해당 launchMode가 설정된 Activity는 한 Task 안에서도 여러 번 인스턴스화 될 수 있으며 여러 다른 Task안에서도 속할 수 있습니다. 아래 예시의 B Activity가 standard 모드의 예입니다. Task 1 에서도 여러 번 인스턴스화 되어있으며 Task 2 에도 존재하고 있습니다.
final static String STARTED_CNT = "STARTED_CNT";
final static int RETRY_CNT = 3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int startedCnt = getIntent().getIntExtra(STARTED_CNT, 0);
Intent recursiveIntent = new Intent(this, MainActivity.class);
recursiveIntent.putExtra(STARTED_CNT, ++startedCnt);
if (startedCnt < RETRY_CNT) {
startActivity(recursiveIntent);
}
}
[소스 1]
위와 같은 소스로 같은 Activity를 여러 번 띄웠을 때 onNewIntent를 실행되지 않으며 onCreate 만 실행이 되었습니다. 또한 아래와 같이 한 스택 안에 여러 번 인스턴스 화 된 것을 확인할 수 있습니다.
말 그대로 이 launchMode를 설정한 Activity는 Task의 Top에서 연달아 존재할 수 없습니다. Top에서는 Single, 1개만 존재할 수 있습니다. Activity의 인스턴스가 Task의 최상단에 있을 경우에 해당 Activity를 다시 startActivity할 경우 onCreate가 불리지 않고 onNewIntent가 호출되며 Acitivity의 인스턴스가 Task에 추가되지 않습니다. 기존 instance를 재활용한다고 생각할 수 있습니다. 하지만 Top이 아닌 Task의 다른 위치에서는 여러 번 인스턴스화 될 수 있습니다.
위와 같이 B Activity에 singleTop이 설정되어있는 경우 왼쪽 모형은 불가능하며 오른쪽 모형은 가능합니다.
launchMode를 바꾸고 [소스 1]을 재실행해보면 아래와 같은 결과를 얻을 수 있습니다.
singleTop이 상위에 있을 때만 기존 인스턴스로 라우팅해준다면, singleTask는 Task내의 먼저 시작된 자신에게로 라우팅해줍니다. 즉 Task내에 자신이 존재한다면 자신으로 라우팅하고, 없다면 새 인스턴스를 추가합니다. 때문에 위 두 launchMode보다 이 singleTask모드가 모호한 부분이 있었습니다. 아래 실험을 통해서 이 launchMode의 특이한 점들을 확인해보았습니다.
1. SingleTask로 설정된 Activity로 onNewIntent될 때, 그 중간에 있는 Activity들은 모두 onDestroy된다.
MainActivity에 SingleTask가 설정된 경우 왼쪽의 Task 상황에서 MainActivity를 startActivity하면 중간의 Second, ThirdActivity는 onDestroy되고 오른쪽 Task모형처럼 MainActivity만 남게 됩니다.
위 그림에서 각 3개의 Activity는 아래와 같은 설정을 가지고 있습니다.
MainActivity(A TaskAffinity, SingleTask)
SecondActivity(A TaskAffinity, Standard)
ThirdActivity(A TaskAffinity, Standard)
Main -> Second -> Third -> Main
순으로 호출하면 그림과 같은 결과를 얻을 수 있습니다. 위 실험은 비교적 간단하여 로그캣으로만 결과를 관찰하였습니다.
ThirdActivity onDestroy가 MainActivity onNewIntent보다 늦게 불렸다는 변수가 있지만 결론적으로 Second, ThirdActivity는 onDestroy되었습니다.
(참고로 Galaxy s8+(Android 9)에서 5번 실험하여도 아래 결과의 순서는 똑같았습니다.)
2. 다른 Task안에 있는 singleTask Activity에게도 라우팅된다.
위와 같은 실험을 하기 위해서 taskAffinity를 달리하는 Activity를 하나 추가하였습니다. 또 새로 생성되는 Acitivity는 이전 Activity의 Task를 따라가는 점을 이용하여 아래와 같은 테스트 케이스를 만들었습니다.
우선 각 4개의 Activity는 아래와 같은 설정이 되어있습니다.
SecondActivity(A TaskAffinity, standard)
ThirdActivity(A TaskAffinity, standard)
OtherTaskActivity(B TaskAffinity, standard)
Main -> Second -> Other -> Third -> Main
위와 같은 설정으로 실험을 하면 아래와 같은 결과를 얻습니다.
위와 같이 ThirdActivity 까지 호출한 경우 왼쪽 모형과 같은 상황이 되며 ThirdActivity에서 다시 MainActivity를 부를 경우 A Task 안으로 라우팅 됩니다. 독특한 점은 A Task의 상위 Activity인 SecondActivity만 onDestory되었으며 B Task의 Activity들은 남아있었습니다.
위 실험을 adb를 통해서 관찰하면
즉 singleTask를 지정할 경우 Activity는 전체 Task에서 하나만 존재할 수 있습니다. 또 라우팅을 시작하는 Task에 있는 Activity들은 종료되지 않으며(B Task) 라우팅이 도착하는 곳에 있는 기존에 있던 singleTask상위의 Activity들은 onDestroy됩니다.(A Task) (이 부분은 앞으로도 예외가 있다면 추가하겠습니다. 또 반대케이스를 알고 계신다면 댓글로 알려주세요😊)
singleInstance는 singleTask와 비슷하나 singleInstance로 지정된 Activity가 있는 Task에는 다른 Activity가 추가되지 않는다는 점이 다릅니다. 따라서 A activity가 singleInstance이고 startActivity를 시작한다면 다음 Activity는 다른 Task안에 위치하게 됩니다. 이 차이로 인해 singleTask에서 한 실험의 1번은 결과가 달라지게 됩니다.
아래와 같이 Activity 3개를 설정해두었습니다.
MainAcitivty(A TaskAffinity, singleInstance)
SecondActivity(A TaskAffinity, standard)
ThirdActivity(A TaskAffinity, standard)
Main -> Second -> Third -> Main
위와 같은 순서로 Activity들을 호출하였을 때 Second, Third는 다른 Task로 분리가 됩니다. 이 후 ThirdActivity에서 MainActivity를 호출하였을 때 A Task의 MainActivity로 라우팅되어 onNewIntent가 호출됩니다.
이 때 singleTask의 2번 실험에서 라우팅이 시작되는 Task의 Activity들은 onDestroy되지 않았던 것처럼, singleInstance 역시 다른 Task에 다른 Activity들이 분리되어있으니 다른 Activity들이 onDestroy되지 않습니다.
즉 위 그림과 같은 상황이 되어 adb로 관찰하여도 결과는 같습니다. SecondActivity, ThirdActivity 는 MainActivity와 다른 Task에 위치하고 있는 것을 확인할 수 있습니다.
(+ singleInstance 추가 실험)
그렇다면 다른 Activity가 singleInstance가 선언된 Activity를 호출하여도 Task가 분리될 수 있을까?
아래 테스트 순서를 통해 위의 상황도 테스트해보았습니다. 똑같이 Activity 3개를 설정해두었습니다.
MainAcitivty(A TaskAffinity, standard)
SecondActivity(A TaskAffinity, singleInstance)
ThirdActivity(A TaskAffinity, standard)
Main -> Second -> Third -> Main
위와 같은 세팅으로 테스트를 할 경우 SecondActivity는 다른 Task에 분리되며 그 이후에 실행된 ThirdActivity는 A Task로 다시 돌아오는 것을 알 수 있습니다.
adb로 확인해 보아도 같은 결과를 볼 수 있습니다.
항상 Android Docs 첫 부분에 있어 자주 읽어도 대강만 알고 있던 launchMode에 대해 여러 테스트를 해보았습니다.👩💻이번 테스트가 꽤 재미있어서 더 오래 기억할 수 있을 것 같습니다.☺️ 다음에는 비슷한 기능을 하는 Intent Flag에 대해 알아보겠습니다.
댓글 영역