본문 바로가기
프로그래밍 놀이터/안드로이드, Java

[android] Activity Task 에 대한 이야기 with allowTaskReparenting 실험 (심화)

by 돼지왕왕돼지 2020. 8. 13.


android activity task



-

아래 명령을 통해 activity stack 을 볼 수 있다.

$ adb shell dumpsys activity 


여기서 TaskRecord 가 Task 의 group 구분이며,

이 안에 Hist #N 로 표기된 HistoryRecord 부분을 보면 그 안에 쌓인 Stack 을 볼 수 있다.

stack 은 packageName/Activity 형태로 표기된다.



-

FLAG_ACTIVITY_NEW_TASK 는 affinity 가 같은 task 위에 쌓이게 된다.

따라서 A affinity 를 가진 A1 Activity 에서 A2 Activity 를 해당 flag 와 함께 띄워도 A stack 에 쌓인다.



-

taskAffinity 의 기본값은 packageName 이다.



-

taskAffinity 가 같은 Activity 들을 따로 띄우려면..

FLAG_ACTIVITY_MULTIPLE_TASK 를 적용하면 된다.

(권장되지는 않는다.)



-

launchMode 의 standard 값은 ActivityInfo.LAUNCH_MULTIPLE 과 같다.

이는 default launchMode 값이다.



-

Manifest 의 Activity 속성보다 Intent flag 의 우선순위가 높다.



-

launchMode 의 singleTop 은 FLAG_ACTIVITY_SINGLE_TOP 과 같다.

A 가 다시 A 를 띄울 때 A 가 새로 생기지 않고 onNewIntent 가 불린다.

이 때 onPause, onNewIntent, onResume 로 불린다.



-

여기에 tricky 함에 또 하나 있는데..

FLAG_ACTIVITY_CLEAR_TOP flag 를 사용할 경우 2가지 케이스로 Activity 동작이 분기한다.

1. singleTop 으로 정의되거나 FLAG_ACTIVITY_SINGLE_TOP 도 flag setting 한 경우에는..

해당 Activity 가 onNewIntent 가 불리며 그 위의 activity 를 clear 한다.

2. multiple (default) 인 경우에는..

해당 Activity 를 포함해서 clear 시킨 후에, 해당 Activity 를 relaunch 시킨다.



-

launchMode 의 singleTask 는 가장 까리한 항목이다.

instance 자체는 1개를 유지하지만, 같은 affinity 를 가진것 위에는 올라갈 수 있다.

그리고 그 위에 같은 affinity 를 가지지 않은 것도 쌓을 수도 있다.

따라서 singleTask 를 새로운 task 에서 띄우려면 affinity 를 unique 하게 지정해야 한다. (FLAG_ACTIVITY_NEW_TASK 없이도 된다.)



-

singleTask 에는 또 하나의 중요한 특성이 있는데..

이 녀석이 root 로 있는 task 는 bg -> fg 로 돌아오면, root 만 남고 모두 제거된다. (root 일때만 valid)

이는 FLAG_ACTIVITY_BROUGHT_TO_FRONT 가 작동하기 때문이다.



-

singleTask 는 single instance 를 보장하기 때문에

A1 -> A2(singleTask) -> A3 -> A2 로 intent 를 호출한 경우

A3 는 종료되면서 A2 가 올라오고, onNewIntent -> onRestart -> onStart -> onResume 의 순서로 재개된다.


A1 -> A2(singleTask) -> A2 의 경우 singleTop 과 같이

onPause -> onNewIntent -> onResume 을 타게 된다.


결론적으로만 보면 singleTask 의 launch 는 FLAG_ACTIVITY_SINGLE_TOP | FLAG_ACTIVITY_CLEAR_TOP 이라고 보면 된다.



-

singleInstance 는 singleTask 와 비슷하게 single instance 를 지원하면서, task 에 유일하다는 추가적 특성도 갖는다.

항상 root 에 있으며, 그 위에 다른 activity 도 올라가지 않는다.

또한 single instance 이기 때문에 재사용이 된다. (singleTask 와 같은 life cycle 코드를 탄다.)

따라서 이 녀석이 실행시키는 모든 activity 는 사실 자체적으로 FLAG_ACTIVITY_NEW_TASK 를 갖는 것과 같다.



-

FLAG_ACTIVITY_NO_HISTORY 는 해당 Activity 라 bg 상태가 되면 자동으로 finish 가 된다.



-

FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET 은 오묘한 녀석이다.

이 녀석을 set 한 Activity 부터 그 위로 stack 된 녀석은 어떤 조건이 되면 함께 제거된다.

그 조건은.. 해당 task 가 bg -> fg 가 될 때 활성화 될 activity 의 intent 속성에FLAG_ACTIVITY_RESET_TASK_IF_NEEDED 가 설정된 경우이다. (이 경우 요청된 Activity 가 안 뜰 수 있다.)


많은 launcher 가 앱을 실행시킬 때 FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag 를 추가한다.

이에 따라 bg -> fg 가 되었을 때 자동으로 RESET 되기도 한다.



-

alwaysRetainTaskState 값은 ICS 부터 deprecate 되었다. (실제 동작하지 않는다.)



-

clearOnBackground=true 속성을 주면, bg 가 되는 순간 root 를 제외하고 모두 제거된다. 이는 root activity 에 설정한다.

finishOnTaskLaunch=true 가 되면 bg 가 되는 순간 설정된 Activity 만 제거 대상이 된다. 이는 root activity 에는 설정할 수 없다.




-

allowTaskReparenting 은 원래의 부모가 해당 task 의 activity 를 가지고 온다는 이야기이다.

여기서 주의할 것은!!!!

실행 시점에 바로 해당 activity 를 띄운 task 의 stack 으로 쌓이는 것이 아니라 같은 affinity task 가 fg 로 올라올때, 다른 task 에 있던 같은 affinity 를 가진 녀석들이 이사오는 것이다.

또한 무조건 이사오는 것이 아니라, FLAG_ACTIVITY_RESET_TASK_IF_NEEDED 속성으로 fg 가 되어야만 이사를 온다.


A1, A2 는 allowTaskReparenting = true 이다.



-

1번 실험

(누군가의 test 결과 참조)


(POS 실제 실험 결과)

A0

B0 -> A1 -> A2


A앱이 fg 되어도 stack 을 유지한다.


이 상태에서 B앱을 수행시키면..


A0

B0

A1 -> A2


Launcher 에서 A 앱을 수행시키면 A0 로 이동한다.

A앱을 Recent app 에서 죽인 다음 A 앱을 다시 수행하면 A2 로 이동한다...


정리

1. 버전에 따라 다른가보다...

2. 오히려 최신 버전에서 reparenting 되어야 할 것 같은 상황에서 안 되고 있고,

B앱을 실행할 때 A1 -> A2 가 A0 에 붙는게 아니라 다른 task 로 분리된다.



-

2번 실험


(누군가의 test 결과 참조)

allowTaskReparenting 관련..

여기서 한가지 더 주의해 볼 것은!!!

B0 -> A1 -> A2


상태에서 B앱을  bg -> fg 로 하면 아래와 같이 된다. (이 역시 FLAG_ACTIVITY_RESET_TASK_IF_NEEDED 속성으로 실행시켰을 때이다. 대부분의 launcher 가 해당 flag 를 주고 띄운다.)

A1 -> A2

B0


그럼 이 상태에서 A 앱을 실행시키면..? A1 이 뜬다... (왜...인거죠..? 최소한 A0 도 아니고... A2 도 아니고... )


(POS 실제 test 결과)

실험 결과는 같으며, A2 앱이 뜬다. ( 기대 동작이라 다행이다. )


결론 : A앱이 떠 있지 않은 상태에서 B앱을 다시 실행시켰을 때만 reparenting 이 정상동작한다.



-

실험 3


(누군가의 test 결과 참조)


(POS 실제 test 결과)

B0 -> A1 -> A2

A0


이 상태에서 B앱을 다시 실행시키면 실험1 과 같은 결과가 된다.


B0

A0

A1 -> A2



-

최종 결론

1. 버전에 따라 동작이 다른 것으로 보인다.

2. POS 기준으로는 reparenting 은 B앱이 reparenting 속성을 가진 A앱의 Activity 들을 launch 했으며, FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag 와 함께 B앱이 다시 실행될 때, A앱과는 별개로 별도의 task 로 존재하게 된다. ( A앱의 실행이 reparenting 을 초래하지 않는다. )

따라서 B앱에서 A 의 Activity 들이 분리될 때 A 의 task 가 있다면 이 녀석이 A앱의 main 이 되고, 그렇지 않으면 분리된 녀석들이 A의 main 이 된다.



-

끝!!!




댓글0