딥링크 이동 로직을 개선하던 중, 단순 화면 이동이 아니라 onNewIntent, Launch Mode를 활용하는 Task 관리를 마주하게 되었다. 딥링크 이동 로직을 수정하기 위해서는 앱 전체의 이동 로직을 수정해야 했기에 함부로 수정하기가 무서워서 우선 onNewIntent와 Launch Mode에 대해 알아보기로 했다. 😅
onNewIntent
보통 화면 전환을 할 때 아래와 같은 코드를 사용한다.
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
그런데 동일한 액티비티를 계속 startActivity() 메서드로 호출하게 되면 동일한 액티비티가 메모리에 여러 개 만들어지게 된다. 이를 방지하기 위해서 Flag(플래그)를 사용할 수 있다.
즉 플래그를 사용하면 동일한 액티비티가 계속해서 생성되지 않고 하나의 액티비티만 존재하게 할 수 있는 것이다.
이 때 생명주기는 “onCreate”는 불려지지 않고 "onPause” → “onNewIntent” → “onResume” → .. 이런 식으로 불려지는데, 액티비티의 onCreate 메서드가 호출되지 않기 때문에 재사용되는 액티비티에서는 Intent를 전달 받아 처리하기 위해서는 onNewIntent 메서드를 오버라이드해서 사용한다.
- 파라미터 값으로 Intent를 받음
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
...
val data = intent?.getStringExtra("extra_key")
}
onNewIntent가 무엇인지 알았으니 다음으로 Flag를 설정하는 방법과 (Android LaunchMode)에 대해 알아보자!
* 시작하기 전.. 앱과 엑티비티의 특성에 맞지 않는 무분별한 LaunchMode 설정은 사용자들에게 불편함을 주기 때문에 주의해야함..
Launch Mode
사용 방법
1️⃣ Manifest에서 Flag 사용
<activity android:launchMode="standard|singleTop|singleTask|singleInstance" />
- standard
- 가장 일반적으로 사용되는 속성으로 default launchMode이다.
- 기존 Task에 액티비티를 계속 쌓고, 같은 액티비티가 2개 이상 쌓일 수 있다. (A → B → A → A)
- singleTop
- Task의 Top에 생성하려는 Activity가 있는 경우, 새로운 Activity를 생성해서 Top에 올리는 것이 아닌 기존의 Activity를 재사용한다. (Top이 아닌 액티비티의 경우 여러 번 인스턴스화 할 수 있다.)
- 이 때 재사용이란, Activity의 Instance가 새로 생성(onCreate)되는 것이 아니라 기존의 Instance가 onPause → onNewIntent → onResume 의 생명주기를 반복하는 것이다.
- 푸쉬, 공유하기 등을 통해 외부에서 접근이 가능한 액티비티의 경우 singleTop 속성을 사용하면 불필요한 호출(Instance 생성) 줄일 수 있다.
- singleTask
- singleTop이 상위에 있을 때만 기존 인스턴스로 라우팅해준다면, singleTask는 Task 내에 먼저 생성되어있는 자신에게로 라우팅한다. 즉 Task 내에 자신이 존재한다면 자신으로 라우팅하고, 없다면 새 인스턴스를 추가한다.
- singleTask로 설정한 액티비티로 라우팅(onNewIntent) 될 때, 그 중간에 있는 Activity들은 모두 onDestroy된다.
- singleInstance
- singleTask와 비슷하지만 singleInstance로 지정된 Activity가 있는 Task에는 다른 Activity가 추가되지 않는다. 즉, A Activity를 singleInstance로 생성한다면 다음 Activity는 다른 Task에 위치하게 된다.
2️⃣ 소스코드에서 Flag 제어
Intent.addFlags(): 새로운 플래그를 기존 플래그에 붙임
Intent.setFlags(): 기존 플래그 전체를 대체
- FLAG_ACTIVITY_BROUGHT_TO_FRONT *
시스템에 의해 스택 관리되며, 가장 기본 값이다.(default flag) - FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
Task가 리셋될 때 플래그가 사용된 액티비티부터 위의 액티비티가 모두 삭제된다. ex) ABCD → B call → AB - FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
시스템에 의해 홈에서 사용자에 의해 백그라운드에 있던 Task가 포그라운드로 전환될 때 항상 붙는다. - FLAG_ACTIVITY_CLEAR_TOP *
호출하는 액티비티가 스택에 존재할 경우, 해당 액티비티를 최상위로 올리면서 그 위에 존재하던 액티비티들은 모두 삭제한다. ex) ABCD → B call → AB cf) 재사용(onNewIntent)만 하고 싶은 경우에는 FLAG_ACTIVITY_SINGLE_TOP과 함께 사용하면 됨 - FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
실행되는 액티비티들은 최근 실행목록에 표시가 되게 되는데, 그러한 표시를 하지 않기 원하는 경우 해당 플래그를 넣어주면 최근 실행목록에 표시되지 않는다. - FLAG_ACTIVITY_FORWARD_RESULT
startActivityForResult를 이용해서 Activity를 호출한 경우에, 호출한 쪽이 아닌 한 번 더 거쳐서 결과 값을 받고 싶을 경우가 있다. 이 때 해당 플래그를 사용하면 ABC인 상황에서 C에서 결과 값을 설정해주고, B를 finish()하면 A에서 C의 결과 값을 받을 수 있다. - FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
시스템에 의해 설정되는 플래그로, 최근 실행목록에서 실행되게 되면 자동으로 설정됨 - FLAG_ACTIVITY_MULTIPLE_TASK
FLAG_ACTIVITY_NEW_TASK와 함께 사용해야만 한다!! 두개의 플래그를 동시에 사용할 경우 새로운 Task는 재활용되지 않고 무조건 새로 생성되며 피호출되는 액티비티는 이 새로운 Task의 최상위 액티비티가 된다. - FLAG_ACTIVITY_NEW_TASK *
실행하는 액티비티를 새 Task로 생성. 단, 기존에 존재하는 Task들 중에 생성하려는 액티비티와 동일한 Affinity를 가지고 있는 Task가 있다면 그 곳으로 액티비티가 들어가게 된다.
하나의 어플리케이션 안에서는 모든 액티비티가 기본 Affinity를 가지고 같은 Task 안에서 동작하는 것이 기본적이지만, FLAG_ACTIVITY_MULTIPLE_TASK 플래그와 함께 사용하지 않을 경우 무조건적으로 Task가 새로 생성되는 것은 아님을 주의해야 한다.
❓ Affinity란?
- Activity들은 하나의 Affinity(번역: 친화력)를 가지고 있다.
- 같은 taskAffinity를 가지고 있는 액티비티들은 개념상 같은 Task에 속한다.
- 기본적으로 한 어플리케이션 안의 모든 액티비티들은 같은 Affinity를 가지고 있다.
- taskAffinity의 기본 값은 <manifest>에 정의된 packageName이다.
- taskAffinity 값은 allowTaskReparenting 속성이나 FLAG_ACTIVITY_NEW_TASK 와 상호작용하여 activity 의 task 를 결정한다
- FLAG_ACTIVITY_NO_ANIMATION
액티비티 전환시 애니메이션을 무시한다. - FLAG_ACTIVITY_NO_HISTORY
액티비티가 스택에 존재하지 않게 되도록 한다. (로딩화면 등에 이용) - FLAG_ACTIVITY_NO_USER_ACTION
이 플래그를 설정하면 자동적으로 액티비티가 호출될 경우 호출되는 onUserLeaveHint()가 실행되는 것을 차단한다. onUserLeaveHint() 콜백 메서드는 어플리케이션 사용 중에 전화가 온다거나 하는 등 사용자의 액션 없이 액티비티가 실행/전환되는 경우 호출되는 메서드이다. - FLAG_ACTIVITY_REORDER_TO_FRONT
이 플래그를 사용하면 호출하려는 Activity가 스택에 존재할 경우에 최상위로 올려주는 효과를 가지게 된다.
ex) ABCDE → C call → ABDEC - FLAG_ACTIVITY_SINGLE_TOP
호출되는 Activity가 최상위에 존재할 경우 해당 Activity를 다시 생성하지 않고, 존재하던 Activity를 다시 사용하게 된다.
ex) ABC → C call → ABC (+ onPause → onNewIntent() → onResume)