study record

[Activities] Understand Tasks and Back Stack 테스크와 백스택 본문

안드로이드/android developers 정복하기

[Activities] Understand Tasks and Back Stack 테스크와 백스택

asong 2021. 3. 30. 11:19

*안드로이드 개발자 공식 사이트 android developers docs를 번역, 정리하는 글입니다.

 

이번 글:

developer.android.com/guide/components/activities/tasks-and-back-stack

 

 테스크란 특정한 일을 수행할 때 사용자와 상호작용하는 액티비티들의 모음이다. 액티비티들은 하나의 스택(백스택)에 각 액티비티가 열린 순서에 맞추어 정리되어 있다. 예를 들어, 이메일 앱은 새로운 메시지들의 하나의 목록을 보여주는 하나의 액티비티를 가질지도 모른다. 유저가 한 메시지를 선택할 때, 새 액티비티가 그 메시지를 보기위해 열린다. 이 새 액티비티는 백스택에 더해진다. 만약 유저가 뒤로가기 버튼을 누르면 새 액티비티가 끝나고 스택이 꺼진다. 

 

 앱들이 안드로이드 7.0에서 지원하는 멀티윈도우 환경에서 동시에 돌아갈 때,  시스템은 각 윈도우에서 분리하여 테스크들을 관리한다. 각 윈도우는 다양한 테스크들을 가질 수 있다.

 

 홈스크린 디바이스는 대부분의 테스크들을 위한 플레이스를 시작한다. 유저가 앱런처의 아이콘을 터치할 때, 앱의 테스크는 앞으로 나온다. 만약 앱을 위한 테스크가 존재하지 않을 경우(최근에 앱이 사용되지 않은 경우), 새 테스크가 만들어지고, 앱의 메인 액티비티가 스택의 뿌리 액티비티로써 열린다. 

 

 최근의 액티비티가 시작될 때, 새 액티비티는 스택의 위로 push되고, 포커스가 된다. 이전의 액티비티는 스택에 남아있지만 멈추게 된다. 액티비티가 멈출 때, 시스템은 유저인터페이스의 현재 상태를 보유한다. 유저가 뒤로가기 버튼을 눌렀을 때 현재 액티비티는 스택의 맨위로부터 pop된다(액티비티는 파괴된다). 그리고 이전의 액티비티가 다시 시작한다. (UI이전 상태가 회복된다.) 스택의 액티비티들은 결코 다시 정리되지 않는다. 오직 스택으로부터 push, pop된다 .현재 액티비티에 의해 시작되어 스택에 push되고, 유저가 뒤로가기 버튼을 사용해 떠나고자할 때 pop되는 것이다. 이와 같이, 백스택은 last in first out 객체 구조로 작동한다.  그림 1은 각 시점의 백스택에 따른 액티비티들 사이의 과정을 타임라인으로 보여준다. 

 

그림 1

 만약 유저가 뒤로가기 버튼을 계속 누른다면, 스택의 액티비티는 유저가 홈스크린에 돌아갈때까지 pop된다. 모든 액티비티가 스택으로부터 제거될 때, 테스크는 더 이상 존재하지 않는다. 

 그림2에서 Task B는 포그라운드에서 유저 상호작용을 받는다. Task A는 백그라운드에서 다시 시작되기를 기다린다. 

그림 2

 하나의 테스크는 홈버튼을 통해 홈스크린으로 가거나 새로운 테스크를 유저가 시작할 때 백그라운드로 갈 수 있게 하는 집약된 유닛이다. 백그라운드에 있는 동안 테스크의 모든 액티비티들은 멈추게 되고, 테스크의 백 스택의 테스크는 그림2처럼 또다른 테스크가 일어나는 동안 포커스를 잃고 있다. 하나의 테스크는 포그라운드로 돌아갈 수 있으며, 유저가 주울 수 있다. 예를 들어 보자면, 현재 테스크 (Task A)는 스택에 3개의 액티비티를 가진다. 유저가 홈버튼을 누르면 앱 런처로부터 새 앱이 시작된다. 새 앱이 시작될 때, 시스템인 Task B를 시작한다. 앱과 상호작용한 후에 유저는 홈을 다시 누르고 Task A의 앱을 선택한다. 지금 Task A는 포그라운드로 왔으며 세 액티비티들은 포커스를 잃고 스택의 탑에서 기다리고 있다. 이 점에서 유저는 Task B로 스위치를 돌릴 수 있다. 홈으로 가거나 앱 아이콘을 누르면서. 이것이 안드로이드 멀티테스킹의 예이다. 

 * 멀티테스크들은 백그라운드에서 한 번만 열릴 수 있다. 그러나 유저가 동시에 많은 백그라운드 테스크들을 돌리고 있다면 시스템은 액티비티 상태를 잃게 야기하는 메모리를 회복하기 위해 백그라운드 액티비티들을 파괴하기 시작할지도 모른다.  

 

 

그림3

 백스택의 액티비티들이 결코 재 정리되지 않기 때문에 만약 앱이 유저가 특정 액티비티를 많이 시작하도록 허락한다면 액티비티의 새 인스턴스가 만들어지고 스택에 넣어진다. 앱의 하나의 액티비티가 그림3과 같이 여러번 인스턴스화될지도 모른다. 그래서 만약 유저가 뒤로가기 버튼으로 뒤로 이동한다면 액티비티의 각 인스턴스가 오픈한 순서대로 나타난다. 그러나 우리는 액티비티가 한 번 이상 인스턴스화되는 것을 원치 않는다면 이것을 바꿀 수 있다. 그렇게 하기 위해서는 Managing Tasks 섹션을 봐야 한다. 

 

액티비티와 테스크들의 기본 행동들을 요약하자면:

  • 액티비티 A가 액티비티 B를 시작할 때 A는 멈추고 시스템은 상태를 가지고 있다. 만약 유저가 뒤로가기 버튼을 눌렀을 때, B에 있는 동안 A는 상태를 가진채로 다시 시작하게 된다. 
  • 유저가 홈버튼을 눌러 테스크를 떠나고자 할 때, 현재 액티비티는 멈추고 테스크는 백그라운드로 가게 된다. 시스템은 테스크의 각 액티비티 상태를 가지고 있다. 만약 유저가 테스크를 시작하는 런처아이콘을 선택하여 테스크를 다시시작한다면 테스크는 포그라운드에 오고 스택의 탑에 액티비티가 다시 시작된다. 
  • 만약 유저가 뒤로가기 버튼을 누르면 현재 액티비티는 스택으로부터 pop되고 파괴된다. 스택의 이전 액티비티가 다시 시작된다. 액티비티가 파괴될때, 시스템은 액티비티의 상태를 가지고 있지 않는다.
  • 액티비티는 여러번 인스턴스화될 수 있고, 심지어 다른 테스크에서도 가능하다. 

Managing Tasks

안드로이드가 테스크와 백스택을 관리하는 방법은 위에서도 설명 됐듯이 대부분의 앱들에 훌륭히 작동하고 우리의 액티비티들이 테스크와 어떻게 연관되어 있는지 그들이 어떻게 백스택에 존재하는지 걱정할 필요가 없다. 그러나 우리는 보통의 행동을 가로막는 것을 원할지도 모른다. 아마도 우리는 현재의 테스크 안을 대신하는 것 대신에 우리의 앱의 액티비티가 새 테스크를 시작하기를 원할지도 모른다. 또한 하나의 액티비티가 시작되었을 때 백 스택 위의 새 인스턴스를 만드는 것 대신에 존재하는 인스턴스를 위로 올리고 싶을지도 모른다. 또한 유저가 테스크를 떠났을 때 너의 백스택을 정리하고 싶을지도 모른다. 

 

우리는 <activity> manifest안의 속성들과 startActivity()에 우리가 전달할 intent의 flag와 함께 이것들을 그리고 더 많은 것들을 할 수 있다.

 

기초적인 <activity> attributes

  • taskAffinity
  • launchMode
  • allowTaskReparenting
  • clearTaskOnLaunch
  • alwaysRetainTaskState
  • finishOnTaskLaunch

기초적인 intent flags

  • FLAG_ACTIVITY_NEW_TASK
  • FLAG_ACTIVITY_CLEAR_TOP
  • FLAG_ACTIVITY_SINGLE_TOP

다음 섹션들에서 우리는 우리가 어떻게 이 manifest속성들을 사용할 수 있는지와 어떻게 액티비티들이 테스크들과 연관되어있는지를 정의하는 intent flags, 어떻게 그들이 백스택에서 행동하는지를 알 수 있다.

 

또한 어떻게 테스크들과 액티비티들이 Recents 스크린에서 보여지고 관리될 수 있는지에 대한 고려들이 토론되고 있다. Recents 스크린에 더 많은 정보들이 있다. 주로 우리는 어떻게 우리의 테스크와 액티비티들이 Recents 스크린에서 보여지는지를 정의하기 위해 시스템을 허락해야 한다. 그리고 우리는 이 행동을 수정할 필요가 없다. 

 

 

Defining launch modes

런치 모드는 어떻게 액티비티의 인스턴스가 현재의 테스크와 연관되어 있는지를 우리가 정의하도록 한다. 우리는 두가지 방법들로 런치모드를 정의할 수 있다.

 

  • manifest 파일을 사용하기
    • 우리가 manifest 파일에서 액티비티를 정의할 때, 우리는 액티비티가 시작할때 어떻게 테스크와 연결시켜야하는지 정의할 수 있다.
  • intent flags를 사용하기
    • 우리가 startActivity()를 호출할 때, 우리는 어떻게 새 액티비티가 현재의 테스크와 연결되야하는지를 선언하는 intent 속 flag를 포함할 수 있다.

이렇게 만약 액티비티 A가 Activity B를 시작한다면 B는 어떻게 현재의 테스크와 연결시킬지를 manifest파일 안에서 정의할 수 있고, A는 또한 어떻게 B가 현재의 테스크와 연결시킬지를 요청할 수 있다. 만약 두 액티비티들이 B가 한 테스크와 연결해야함을 정의한다면 A의 요청(intent안에 정의된)은 B의 요청(manifest 안에 정의된)보다 받아들여질 것이다. 

 

*몇몇 manifest 파일에 이용가능한 런치모드들은 intent의 flags로 이용가능하지 않고, 마찬가지로 몇몇 intent의 flags로 이용가능한 런치모드들은 manifest에 정의될 수 없다. 

 

 

manifest 파일 사용하기

 

manifest 파일안에 액티비티가 선언될 때, 우리는 어떻게 액티비티가 launchMode 속성을 사용하는 테스크와 연결시킬지를 지정할 수 있다. 

 

launchMode 속성은 어떻게 액티비티가 테스크로 시작되어야 하는지의 지시를 지정한다. 여기 4가지 launchMode들이 있다. 

 

  • "standard" (기본 모드)
    • 기본 디폴트이다. 시스템은 액티비티가 어디에서 시작됐는지와 인텐트의 루트로부터 테스크 안의 액티비티 인스턴스를 새롭게 만든다. 액티비티는 여러번 인스턴스화할 수 있고, 각 인스턴스는 다른 테스크들로 속해질 수 있고 하나의 테스크는 여러 인스턴스들을 가질 수 있다. 
  • "singleTop"
    • 만약 액티비티의 인스턴스가 이미 현재테스크 최상에 존재하다면 시스템은 액티비티의 새 인스턴스를 만드는 대신에 onNewIntent()메소드에 호출을 통해 그 인스턴스에 인텐트를 보낸다. 액티비티는 여러번 인스턴스화될 수 있고, 각 인스턴스는 다른 테스크들로 속해질 수 있고, 하나의 테스크가 여러 인스턴스를 가질 수 있다. (그러나 오직 백스택의 최상의 액티비티는 액티비티의 존재하는 인스턴스가 아니다.)
    • 예를 들어 액티비티A와 B,C,D들이 테스크의 백 스택이 있다고 가정하자. 인텐트는 D액티비티에 도착한다.  만약 D가 디폴트인 "standard" 런치모드이면 클래스의 새 인스턴스가 시작되고 스택은 A,B,C,D,D가 된다. 그러나 D의 런치모드가 "singleTop"이면 스택의 최상에 D가 존재하기 때문에 D의 존재하는 인스턴스가 onNewIntent()메소드를 통해 인텐트를 받는다. 그러나 인텐트가 B에 도착하면 런치모드가 "singleTop"이어도 B의 새 인스턴스가 스택에 더해진다. 
    • * 액티비티의 새 인스턴스가 만들어질 때, 유저가 뒤로가기 버튼을 누를 수 있다. 존재하는 인스턴스가 새 인텐트를 다룰때, 유저는 뒤로가기 버튼을 눌러 인텐트가 도착하기 전의 액티비티 상태로 돌릴 수 없다.