태스크와 백 스택

kosdjs·2025년 6월 6일
0

Android

목록 보기
6/24
  • 태스크(task)는 사용자가 앱에서 무언가 하려고 할 때 상호작용하는 액티비티의 모음

  • 액티비티들은 백 스택(back stack)이라는 스택에 액티비티가 실행된 순서대로 정리됨

  • 예시로, 이메일 앱이 새 메시지의 리스트를 보여주는 액티비티를 가지고 있을 때, 사용자가 메시지를 선택하면 그 메시지를 보여줄 수 있는 새 액티비티가 열림, 이 새 액티비티는 백 스택에 추가되고, 사용자가 뒤로가기를 누르면 액티비티가 종료되고 스택에서 제거됨

태스크와 백 스택의 수명주기

  • 기기의 홈 화면은 대부분의 태스크의 시작점임, 이 때 사용자가 홈 화면이나 앱 런처에서 앱의 아이콘이나 바로가기를 누르면 앱의 태스크가 포그라운드로 올라오게 됨, 이 때 태스크가 없다면 새 태스크가 생성되고 앱의 메인 액티비티가 스택의 루트 액티비티로 열림

  • 현재 액티비티가 새로운 액티비티를 시작할 때, 새로운 액티비티는 스택의 상단에 추가되고 포커스를 가지게 됨, 이전 액티비티는 스택에 남지만 중단(Stopped)됨, 액티비티가 중단될 때 시스템은 UI 상태를 유지함, 사용자가 뒤로가기를 누르면 현재 액티비티는 스택의 상단에서 제거되고 파괴되고 이전 액티비티를 재개(resume)하고 UI 상태를 복원함

  • 스택에 있는 액티비티들은 절대 재정렬되지 않음, 새로운 액티비티를 시작하거나 사용자가 뒤로가기를 누를 때 스택에서는 삽입과 삭제만 이루어짐

  • 백 스택은 LIFO(last in, first out) 구조로 동작함

  • 사용자가 뒤로가기를 하면 스택에 있는 액티비티가 스택에서 삭제되어 이전 액티비티가 드러남, 뒤로가기를 계속 한다면 홈 화면이나 태스크가 시작될 때 실행된 액티비티로 돌아갈 때까지 계속되고 스택의 모든 액티비티가 삭제되면 태스크는 더 이상 존재하지 않게 됨

루트 런처 액티비티의 뒤로가기 동작

  • 루트 런처 액티비티는 ACTION_MAINCATEGORY_LAUNCHER를 인텐트 필터에 선언한 액티비티, 이런 액티비티들은 앱 런처에서 앱을 실행할 때 진입점 역할을 하고 태스크를 시작하는데 사용됨

  • 사용자가 루트 런처 액티비티에서 뒤로가기를 하면 기기의 안드로이드 버전에 따라 시스템이 다르게 처리함

    안드로이드 11 이하

    • 시스템이 액티비티를 종료함

    안드로이드 12 이상

    • 시스템이 액티비티를 종료하는 대신 액티비티를 백그라운드의 태스크로 이동함, 이는 홈 버튼을 눌렀을 때와 동일한 동작임

    • 대부분의 경우에 이 동작은 앱을 완전히 재시작하는 것 대신, 앱을 빠르게 재개할 수 있게 함

      본문에서 앱을 시작할 때 앱의 상태를 각각 cold state, warm state 라고 표현함
      cold state : 앱을 시작하기 위해 프로세스부터 시작
      warm state : 프로세스는 존재하고 액티비티부터 시작

    • 만약 뒤로가기 탐색을 커스텀해야 한다면 onBackPressed()를 오버라이딩하기 보다 AndroidX Activity API를 사용하길 권장함, 뒤로가기 동작을 가로채는 컴포넌트가 없을 경우 AndroidX Activity API는 자동으로 적절한 시스템 동작을 따름

    • 만약 onBackPressed()를 뒤로가기 탐색을 제어하고 액티비티를 끝내기 위해 오버라이딩했다면 액티비티를 끝내는 대신 super.onBackPressed()를 호출하게 구현해야 함, super.onBackPressed()를 호출하면 적절할 때에 액티비티와 태스크를 백그라운드로 이동시키며 사용자에게 보다 일관된 앱 간 탐색 경험을 제공함

백그라운드와 포그라운드 태스크

  • 태스크는 사용자가 새 태스크를 시작하거나 홈 화면으로 이동할 때 백그라운드로 이동할 수 있는 통합된 단위

  • 다른 태스크가 포그라운드에 올라오면 태스크는 포커스를 잃고 백그라운드에 진입함, 이 때 태스크에 존재하는 모든 액티비티가 중단되지만, 백 스택은 그대로 남아있음

  • 백그라운드에 있는 태스크가 다시 포그라운드로 올라올 수 있는데 백스택이 남아있기 때문에 사용자가 중단했던 부분부터 다시 시작할 수 있음

노트: 여러 태스크를 백그라운드에서 동시에 가지고 있을 수 있습니다. 하지만 사용자가 너무 많은 백그라운드 태스크를 실행한다면 시스템은 메모리를 확보하기 위해 백그라운드 액티비티를 파괴할 수 있습니다. 이 경우 액티비티의 상태는 잃어버리게 됩니다.

다중 액티비티 인스턴스

  • 백 스택의 액티비티들은 절대 재정렬되지 않기 때문에, 사용자가 이미 실행된 특정 액티비티를 다시 시작할 경우 새 인스턴스가 생성되고 스택에 추가됨

  • 사용자가 뒤로가기를 하면 생성된 순서대로 자신의 UI 상태를 가지고 있는 인스턴스가 보이게 됨

멀티 윈도우 환경

  • 앱이 멀티 윈도우 환경에서 동시에 실행된다면 시스템은 각 윈도우마다 별도의 태스크를 관리함, 각 윈도우는 여러개의 태스크를 가질 수 있음

태스크 관리

  • 안드로이드는 모든 연속적으로 실행된 액티비티를 같은 태스크, 스택에 저장해 관리함, 이는 대부분의 앱에서 잘 작동하므로 일반적으로는 액티비티가 태스크와 어떻게 연관되어 있는지, 백 스택에 어떻게 존재하는지 걱정할 필요가 없음

  • 하지만 이런 일반적인 방식을 따르지 않고 싶을 수 있음, 이런 경우에 매니페스트의 <activity> 요소에 속성을 부여하거나 intentflag를 부여해 액티비티를 시작할 수 있음

매니페스트 파일을 이용한 시작 모드 정의

  • 매니페스트 파일에 액티비티를 선언할 때, <activity> 요소의 launchMode 속성을 통해 액티비티가 태스크에 어떻게 연관되어 있는지 지정할 수 있음

  • launchMode 속성에 부여할 수 있는 시작 모드는 5개가 있음

    • standard : 기본 모드, 안드로이드의 기본 태스크 관리 방식을 따름
    • singleTop : 스택의 최상단에 이미 액티비티가 있다면 새로 인스턴스를 만들지 않고 onNewIntent() 메소드를 통해 intent만 전달함, 액티비티가 이미 있더라도 태스크의 최상단이 아니면 새로 인스턴스를 만듬
    • singleTask : 액티비티가 태스크에 존재한다면 새로 인스턴스를 만들지 않고 기존에 존재하는 인스턴스를 사용함, 이 때 기존의 인스턴스로 돌아가면 같은 태스크의 상위 액티비티는 모두 파괴됨
    • singleInstance : 앱에서 하나의 인스턴스만 가질 수 있음, 인스턴스가 생성될 때 새로운 태스크의 루트에 생성되고 그 태스크에 다른 액티비티는 들어갈 수 없음
    • singleInstancePerTask : singleTask처럼 태스크에 인스턴스가 존재하면 기존 인스턴스 사용, 이 때 상위 액티비티 파괴도 똑같음, 또한 singleInstance처럼 인스턴스가 생성될 때 새로운 태스크의 루트에 생성되지만 FLAG_ACTIVITY_MULTIPLE_TASKFLAG_ACTIVITY_NEW_DOCUMENT 플래그가 설정된 인텐트로 실행하면 singleInstance와는 다르게 앱 내에 새로운 인스턴스가 생성될 수 있음, 이 때 새로운 태스크가 생기며 그 태스크의 루트에 생성됨

인텐트 플래그를 이용한 시작 모드 정의

  • 액티비티를 시작할 때 플래그를 포함한 인텐트를 전달하면 태스크의 동작 방식을 바꿀 수 있음

  • FLAG_ACTIVITY_NEW_TASK : 앞서 살펴본 singleTask 시작 모드와 동일함

  • FLAG_ACTIVITY_SINGLE_TOP : 앞서 살펴본 singleTop 시작 모드와 동일함

  • FLAG_ACTIVITY_CLEAR_TOP : 시작하려는 액티비티가 이미 존재하면 존재하는 인스턴스의 상위에 있는 모든 액티비티를 파괴해 인스턴스를 태스크의 상위로 올리고 인텐트를 전달함, 주로 FLAG_ACTIVITY_NEW_TASK와 같이 사용됨, 두 플래그를 같이 사용하면 존재하는 액티비티를 인텐트에 대응할 수 있는 다른 태스크로 옮길 수 있음

어피니티 처리

  • 액티비티가 선호하는 태스크를 나타냄, 기본적으로 같은 앱의 모든 액티비티는 같은 태스크에 있기를 선호함

  • 매니페스트 파일에서 <activity>요소의 taskAffinity속성을 수정해 어피니티를 수정할 수 있음, 문자열 값을 가지며 앱의 패키지 이름을 기본 어피니티로 가지기 때문에 값을 지정할 때 패키지 이름을 피해야 함

  • 어피니티는 두가지 상황에서 작동하기 시작함

    • 액티비티를 시작할 때 인텐트에 FLAG_ACTIVITY_NEW_TASK 플래그가 포함되어 있다면, 시스템은 액티비티를 수용할 다른 태스크를 찾음, 보통은 새로운 태스크가 생성되지만 동일한 어피니티를 가지고 있는 태스크가 있으면 해당 태스크에 액티비티가 추가됨, 이 플래그를 통해 액티비티가 새로운 태스크에 생성되면 사용자가 홈 버튼을 눌러 해당 태스크를 빠져나갔을 때 태스크로 돌아올 방법이 있어야 함, 따라서 이 플래그를 사용한다면 태스크의 루트 액티비티가 CATEGORY_LAUNCHER 인텐트 필터가 있어야 함
    • 액티비티에 allowTaskReparenting속성이 true로 설정된 경우, 자신이 시작된 태스크에서 어피니티를 가지고 있는 태스크가 포그라운드로 왔을 때 옮겨질 수 있음, 예를 들어 액티비티가 다른 앱에서 실행되어 다른 태스크에 있을 때 액티비티가 속한 앱의 태스크(액티비티의 기본 어피니티가 본인이 속한 앱)가 포그라운드로 왔을 때 해당 앱의 태스크로 옮겨질 수 있음

백 스택 비우기

  • 사용자가 태스크를 오랫동안 비워놓으면 시스템은 루트 액티비티를 제외한 나머지 액티비티를 태스크에서 제거함, 이 상태에서 사용자가 태스크로 돌아오면 루트 액티비티만 복원됨, 시스템은 사용자가 오랜 시간 동안 이전에 하던 일을 포기했다고 생각하고 태스크로 돌아왔을 때 새로운 일을 시작하려고 했다고 판단해 이런 동작을 함

  • 이런 동작을 수정할 수 있는 방법이 있음

    • alwaysRetainTaskState : 이 속성을 태스크의 루트 액티비티에 true로 설정하면 기본 동작이 실행되지 않음, 태스크는 오랜 시간이 지나도 모든 액티비티를 유지
    • clearTaskOnLaunch : 이 속성을 태스크의 루트 액티비티에 true로 설정하면 사용자가 태스크를 떠났다가 다시 돌아올 때 태스크는 루트 액티비티만 남기고 비워짐, alwaysRetainTaskState의 반대 동작
    • finishOnTaskLaunch : clearTaskOnLaunch와 비슷한 속성이지만 전체 태스크가 아닌 단일 액티비티에 적용, 루트 액티비티가 아닌 액티비티에 적용 가능, true로 설정되면 적용된 액티비티는 사용자가 태스크를 나갔다 돌아오면 해당 액티비티는 더 이상 존재하지 않게 됨

노트: clearTaskOnLaunch, finishOnTaskLaunchFLAG_ACTIVITY_RESET_TASK_IF_NEEDED 플래그가 정해져 있지 않다면 동작하지 않음

태스크 시작하기

  • android.intent.action.MAIN, android.intent.category.LAUNCHER를 인텐트 필터에 추가해 액티비티를 태스크의 진입점으로 설정할 수 있음
<activity ... >
    <intent-filter ... >
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    ...
</activity>
  • 이런 인텐트 필터를 추가하게 되면, 앱 런처에 해당 액티비티의 아이콘과 라벨이 표시되어 사용자가 액티비티를 실행하거나 사용자가 태스크를 떠났다가 돌아올 수 있게 됨

  • 사용자가 태스크를 떠났다가 돌아올 때 런처를 통해 액티비티로 돌아올 방법이 있어야 함, 따라서 액티비티를 항상 새로운 태스크에서 실행하는 singleTasksingleInstance 시작 모드를 사용하는 액티비티는 ACTION_MAINCATEGORY_LAUNCHER 인텐트 필터가 있어야 함

원문: https://developer.android.com/guide/components/activities/tasks-and-back-stack

0개의 댓글