활동 삽입

화면이 큰 기기를 통해 사용자는 더 많은 것을 보고 작업하고 더 많이 경험할 수 있습니다. 대형 디스플레이를 사용하면 동시에 여러 활동을 실행하거나 같은 활동의 여러 인스턴스를 실행할 수 있습니다.

대형 화면의 추가적인 디스플레이 영역을 활용할 수 있도록 Jetpack WindowManager는 애플리케이션의 작업 창을 여러 활동 간에 분할하는 활동 삽입을 도입했습니다.

그림 1. 활동을 나란히 보여주는 설정 앱

대형 화면을 지원하기 위해 기존 코드베이스를 업데이트하는 데는 많은 노력이 필요하고 시간이 오래 걸릴 수 있습니다. 프래그먼트를 사용하여 활동 기반 앱을 다중 창 레이아웃으로 변환하려면 상당한 리팩터링이 필요합니다.

활동 삽입에는 앱 리팩터링이 거의 필요하지 않거나 전혀 필요하지 않습니다. XML 구성 파일을 만들거나 Jetpack WindowManager API를 호출하여 앱이 활동을 표시하는 방법, 즉 나란히 표시할지 스택으로 표시할지를 결정합니다.

작은 화면 지원은 자동으로 유지됩니다. 화면이 작은 기기에 앱이 있는 경우 활동이 서로 스택됩니다. 대형 화면에서는 활동이 나란히 표시됩니다. 생성한 구성에 따라 시스템에서 표현 방식이 결정되며 분기화 로직이 필요하지 않습니다.

활동 삽입은 기기 방향 변경을 지원하고 폴더블 기기에서 원활하게 작동하여 기기가 접히고 펼쳐질 때 활동을 스택하고 스택 해제합니다.

Modern Android Development에서는 프래그먼트, 탐색 구성요소, 다용도 레이아웃 관리자(예: SlidingPaneLayout)가 포함된 단일 활동 아키텍처를 사용합니다.

그러나 앱이 여러 활동으로 구성된 경우 활동 삽입을 사용하면 태블릿, 폴더블, Chrome OS 기기에서 향상된 사용자 환경을 쉽게 제공할 수 있습니다.

작업 창 분할

활동 삽입은 앱 작업 창을 기본 컨테이너와 보조 컨테이너로 분할합니다. 컨테이너는 기본 활동에서 실행된 활동, 또는 이미 컨테이너에 있는 다른 활동에서 실행된 활동을 보유합니다.

활동이 실행되면 보조 컨테이너에 스택되고, 작은 화면에서 보조 컨테이너는 기본 컨테이너 위에 스택됩니다. 따라서 활동 스택 및 뒤로 탐색이 앱에 이미 내장된 활동의 순서와 일치합니다.

활동 삽입을 사용하면 다양한 방식으로 활동을 표시할 수 있습니다. 앱은 두 활동을 나란히 동시 실행하여 작업 창을 분할할 수 있습니다.

그림 2. 나란히 표시되는 두 활동

또는 전체 작업 창을 차지하고 있는 활동은 새 활동을 나란히 실행하여 분할을 만들 수 있습니다.

그림 3. 활동 A가 측면에 활동 B를 시작함

참고: 이 초기 버전의 활동 삽입에서는 분할을 만든 활동만 기본 컨테이너를 차지할 수 있습니다. 보조 컨테이너에는 활동 스택이 포함될 수 있습니다.

이미 분할에 표시되며 작업 창을 공유하는 활동은 다음과 같은 방법으로 다른 활동을 실행할 수 있습니다.

  • 측면으로, 다른 활동 위:

    그림 4. 활동 A가 측면의 활동 B 위로 활동 C를 시작함
  • 측면으로, 분할의 좌우를 전환하여 이전 기본 활동을 숨김:

    그림 5. 활동 B가 측면에 활동 C를 시작하고 분할의 좌우를 전환함
  • 기존 위치의 위에(동일한 활동 스택) 활동 실행:

    그림 6. 활동 B가 추가 인텐트 플래그 없이 활동 C를 시작함
  • 동일한 작업에서 활동 전체 크기 창 실행:

    그림 7. 활동 A 또는 활동 B가 작업 창을 채우는 활동 C를 시작함

뒤로 탐색

다양한 유형의 애플리케이션은 분할 작업 창 상태에서 활동 간의 종속성이나 사용자가 뒤로 이벤트를 트리거하는 방식(아래의 예)에 따라 서로 다른 뒤로 탐색 규칙을 가질 수 있습니다.

  • 동시: 활동이 관련되어 있어서 두 활동을 따로 표시해서는 안 되는 경우 둘 다 종료하도록 뒤로 탐색을 구성할 수 있습니다.
  • 단독: 활동이 완전히 독립적인 경우 활동의 뒤로 탐색이 작업 창에 있는 다른 활동의 상태에 영향을 주지 않습니다.

버튼 탐색을 사용할 때 마지막으로 포커스가 맞춰진 활동으로 뒤로 이벤트가 전송됩니다. 동작 기반 탐색을 사용하면 동작이 발생한 활동으로 뒤로 이벤트가 전송됩니다.

다중 창 레이아웃

Jetpack WindowManager 1.0 Beta03을 사용하면 12L(API 수준 32)의 대형 화면 기기 및 이전 플랫폼 버전이 있는 일부 기기에서 활동이 포함된 다중 창 레이아웃을 빌드할 수 있습니다. 프래그먼트 또는 뷰 기반 레이아웃(예: SlidingPaneLayout)이 아닌 여러 활동 기반의 기존 앱은 큰 리팩터링 없이 개선된 대형 화면 사용자 환경을 제공할 수 있습니다.

한 가지 일반적인 예는 목록-세부정보 분할입니다. 높은 품질의 표현을 위해 시스템에서 목록 활동을 시작한 후에 애플리케이션이 즉시 세부정보 활동을 시작합니다. 전환 시스템은 두 활동이 모두 그려질 때까지 대기했다가 두 활동을 함께 표시합니다. 두 활동은 사용자의 관점에서 하나로 실행됩니다.

그림 8. 다중 창 레이아웃에서 동시에 시작된 두 활동

분할 비율

애플리케이션은 분할 구성의 ratio 속성에 따라 작업 창의 비율이 조정되는 방식을 지정할 수 있습니다(아래의 분할 구성 참고).

그림 9. 분할 비율이 서로 다른 두 활동 분할

자리표시자

자리표시자 활동은 활동 분할의 한 영역을 차지하는 빈 보조 활동입니다. 궁극적으로 콘텐츠가 포함된 다른 활동으로 대체됩니다. 예를 들어 자리표시자 활동은 목록의 항목이 선택될 때까지 목록-세부정보 레이아웃에서 활동 분할의 보조 측면을 차지할 수 있습니다. 목록의 항목이 선택되면 이 목록 항목에 관한 세부정보가 포함된 활동이 자리표시자를 대체합니다.

자리표시자는 분할을 위한 공간이 충분한 경우에만 표시됩니다. 너비가 너무 작아 활동 분할을 표시할 수 없는 디스플레이 크기가 되면 자리표시자는 자동으로 종료되지만 공간이 허용하면 자동으로 다시 실행(다시 초기화된 상태)됩니다.

그림 10. 폴더블 기기 접기 및 펼치기. 디스플레이 크기가 변경됨에 따라 자리표시자 활동이 종료되고 재생성됨

창 크기 변경

기기 설정이 변경되면 작업 창 너비가 다중 창 레이아웃에 맞게 충분히 크지 않을 때(예: 대형 화면의 폴더블 기기가 태블릿 크기에서 휴대전화 크기로 접히는 경우 또는 앱 창의 크기가 멀티 윈도우 모드에서 조절되는 경우) 작업 창의 보조 창에 있는 자리표시자 이외의 활동이 기본 창의 활동 위에 스택됩니다.

자리표시자 활동은 분할에 맞게 디스플레이 너비가 충분한 경우에만 표시됩니다. 작은 화면에서는 자리표시자가 자동으로 닫힙니다. 디스플레이 영역이 다시 충분히 커지면 자리표시자가 재생성됩니다. 위의 자리표시자를 참고하세요.

WindowManager가 기본 창의 활동 위에 보조 창의 활동을 z 순서로 지정하므로 활동 스택이 가능합니다.

보조 창의 여러 활동

활동 B가 추가 인텐트 플래그 없이 제자리에서 활동 C를 시작합니다.

B 위에 스택된 C를 포함하는 활동 A, B, C가 있는 활동 분할

동일한 작업에서 다음과 같은 활동 z 순서로 이어집니다.

B 위에 스택된 활동 C를 포함하는 보조 활동 스택.
          보조 스택이 활동 A를 포함하는 기본 활동 스택 위에 스택됨

따라서 작은 작업 창에서는 애플리케이션이 스택 맨 위에 있는 C를 포함한 단일 활동으로 축소됩니다.

활동 C만 보여주는 작은 창

작은 창에서 뒤로 이동하면 활동 위에 스택된 활동을 탐색하게 됩니다.

작업 창 구성이 여러 창을 수용할 수 있는 큰 크기로 복원되면 활동이 다시 나란히 표시됩니다.

스택된 분할

활동 B가 측면에 활동 C를 시작하고 분할의 좌우를 전환합니다.

활동 A와 B, 그리고 활동 B와 C를 보여주는 작업 창

동일한 작업에서 다음과 같은 활동 z 순서로 이어집니다.

단일 스택에 있는 활동 A, B, C. 활동은 위에서 아래로 C, B, A의 순서로 스택됨

작은 작업 창에서는 애플리케이션이 맨 위에 있는 C를 포함한 단일 활동으로 축소됩니다.

활동 C만 보여주는 작은 창

분할 구성

컨테이너와 분할은 분할 규칙에 따라 WindowManager 라이브러리에서 만들 수 있습니다. 분할 규칙을 구성하려면 몇 단계를 거쳐야 합니다.

  1. build.gradle 파일에 WindowManager 라이브러리 종속 항목을 추가합니다.

    implementation("androidx.window:window:1.0.0-beta03")

  2. 다음 작업을 하는 리소스 파일을 만듭니다.

    • 필터를 사용하여 분할해야 하는 활동 정의
    • 분할을 공유하는 모든 활동에 분할 옵션 구성
    • 분할해서는 안 되는 활동 지정

    예:

    <!-- The split configuration for activities. -->
    <resources
        xmlns:window="http://schemas.android.com/apk/res-auto">
    
        <!-- Automatically split the following activity pairs. -->
        <SplitPairRule
            window:splitRatio="0.3"
            window:splitMinWidth="600dp"
            window:finishPrimaryWithSecondary="true"
            window:finishSecondaryWithPrimary="true">
            <SplitPairFilter
                window:primaryActivityName=".SplitActivityList"
                window:secondaryActivityName=".SplitActivityDetail"/>
            <SplitPairFilter
                window:primaryActivityName="*"
                window:secondaryActivityName="*/*"
                window:secondaryActivityAction="android.intent.action.VIEW"/>
        </SplitPairRule>
    
        <!-- Automatically launch a placeholder for the list activity. -->
        <SplitPlaceholderRule
            window:placeholderActivityName=".SplitActivityListPlaceholder"
            window:splitRatio="0.3"
            window:splitMinWidth="600dp">
            <ActivityFilter
                window:activityName=".SplitActivityList"/>
        </SplitPlaceholderRule>
    
    </resources>
    
  3. 라이브러리에 규칙 정의에 관해 알립니다.

    이 예에서는 Jetpack Startup 라이브러리를 사용해 앱 로드 및 활동의 다른 구성요소가 시작되기 전에 초기화를 실행합니다. 시작 기능을 사용 설정하려면 앱의 빌드 파일에 라이브러리 종속 항목을 추가합니다.

    implementation("androidx.startup:startup-runtime:1.1.0")

    앱 매니페스트에 다음 항목을 추가합니다.

    <!-- AndroidManifest.xml -->
    
    <provider android:name="androidx.startup.InitializationProvider"
        android:authorities="${applicationId}.androidx-startup"
        android:exported="false"
        tools:node="merge">
        <!-- This entry makes ExampleWindowInitializer discoverable. -->
        <meta-data  android:name="androidx.window.sample.embedding.ExampleWindowInitializer"
            android:value="androidx.startup" />
    </provider>
    
  4. 마지막으로 이니셜라이저 클래스 구현을 추가합니다.

    정의(main_split_config)가 포함된 xml 리소스 파일의 ID를 SplitController.initialize()에 제공하여 규칙이 설정됩니다.

    Kotlin

    class ExampleWindowInitializer : Initializer<SplitController> {
       override fun create(context: Context): SplitController {
           SplitController.initialize(context, R.xml.main_split_config)
           return SplitController.getInstance(context)
       }
    
       override fun dependencies(): List<Class<out Initializer<*>>> {
           return emptyList()
       }
    }
    

    자바

    class ExampleWindowInitializer extends Initializer<SplitController> {
       @Override
       SplitController create(Context context) {
           SplitController.initialize(context, R.xml.main_split_config);
           return SplitController.getInstance(context);
       }
    
       @Override
       List<Class<? extends Initializer<?>>> dependencies() {
           return emptyList();
       }
    }
    

분할 예

전체 크기 창에서 분할

그림 11. 활동 A가 측면에 활동 B를 시작함

리팩터링할 필요가 없습니다. 정적으로 또는 런타임으로 분할의 구성을 정의한 다음 추가 매개변수 없이 Context#startActivity()를 호출할 수 있습니다.

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

기본적으로 분할

애플리케이션의 방문 페이지가 대형 화면에서 두 컨테이너로 분할되도록 설계된 경우 두 활동이 모두 생성되고 동시에 표현될 때 사용자 환경이 가장 좋습니다. 하지만 사용자가 기본 컨테이너의 활동과 상호작용(예: 사용자가 탐색 메뉴에서 항목 선택)할 때까지 분할의 보조 컨테이너에 콘텐츠를 사용하지 못할 수도 있습니다. 자리표시자 활동은 분할의 보조 컨테이너에 콘텐츠가 표시될 수 있을 때까지 공백을 채울 수 있습니다(위의 자리표시자 참고).

그림 12. 두 활동을 동시에 열어 분할 생성. 활동 하나는 자리표시자

자리표시자로 분할을 만들려면 자리표시자를 만들고 기본 활동과 연결합니다.

<SplitPlaceholderRule
    window:placeholderIntentName=".Placeholder">
    <ActivityFilter
        window:activityName=".Main"/>
</SplitPlaceholderRule>

앱이 인텐트를 수신하면 대상 활동이 활동 분할의 보조 부분으로 표시될 수 있습니다. 예를 들어 목록의 항목에 관한 정보가 포함된 세부정보 화면을 표시하는 요청이 있습니다. 작은 디스플레이에서는 전체 크기 작업 창에, 큰 기기에서는 목록 옆에 세부정보가 표시됩니다.

그림 13. 작은 화면에서는 단독으로, 대형 화면에서는 목록 활동과 함께 표시되는 딥 링크 세부정보 활동

실행 요청은 기본 활동으로 라우팅되고 대상 세부정보 활동은 분할에서 실행됩니다. SplitController는 사용 가능한 디스플레이 너비에 따라 올바른 표현(스택 또는 나란히)을 자동으로 선택합니다.

Kotlin

override fun onCreate(savedInstanceState Bundle?) {
    …
    splitController.registerRule(SplitPairRule(newFilters))
    startActivity(Intent(this, DetailActivity::class.java))
}

자바

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    …
    splitController.registerRule(new SplitPairRule(newFilters));
    startActivity(new Intent(this, DetailActivity.class));
}

딥 링크 대상은 뒤로 탐색 스택에서 사용자가 사용할 수 있는 유일한 활동일 수도 있으므로 세부정보 활동을 닫고 기본 활동만 남기는 것을 방지하는 것이 좋습니다.

목록 활동과 세부정보 활동이 나란히 표시된 대형 디스플레이.
          뒤로 탐색이 세부정보 활동을 닫고 화면에 목록 활동을 남길 수 없음.

세부정보 활동만 있는 작은 디스플레이. 뒤로 탐색이 세부정보 활동을 닫고 목록 활동을 표시할 수 없음

대신 finishPrimaryWithSecondary 속성을 사용하여 두 활동을 동시에 종료할 수 있습니다.

<SplitPairRule
    window:finishPrimaryWithSecondary="true">
    <SplitPairFilter
        window:primaryActivityName=".List"
        window:secondaryActivityName=".Detail"/>
</SplitPairRule>

분할 컨테이너의 여러 활동

분할 컨테이너에 여러 활동을 스택하면 사용자가 딥 콘텐츠에 액세스할 수 있습니다. 예를 들어 목록-세부정보 분할에서 사용자가 하위 세부정보 섹션으로 이동하되 기본 활동은 제자리에 유지해야 할 수도 있습니다.

그림 14. 작업 창의 보조 창에서 제자리에 열려 있는 활동

Kotlin

class DetailActivity {
    …
    fun onOpenSubDetail() {
      startActivity(Intent(this, SubDetailActivity::class.java))
    }
}

자바

public class DetailActivity {
    …
    void onOpenSubDetail() {
        startActivity(new Intent(this, SubDetailActivity.class));
    }
}

하위 세부정보 활동이 세부정보 활동 위에 놓여 세부정보 활동이 숨겨집니다.

그러면 사용자는 스택에서 뒤로 이동하여 이전 세부정보 수준으로 돌아갈 수 있습니다.

그림 15. 스택 맨 위에서 삭제된 활동

여러 활동을 서로 위에 스택하는 것은 이러한 활동이 동일한 보조 컨테이너의 한 활동에서 실행되는 경우 기본 동작입니다. 활성 분할 내의 기본 컨테이너에서 실행된 활동도 활동 스택 맨 위에 있는 보조 컨테이너에 배치됩니다.

새 작업의 활동

분할 작업 창의 활동이 새 작업에서 활동을 시작하는 경우 이 새 작업은 분할을 포함하는 작업과 별개이며 전체 크기 창으로 표시됩니다. 최근 항목 화면에는 분할에서의 작업과 새 작업 두 가지 작업이 표시됩니다.

그림 16. 활동 B에서 활동 C를 새 작업으로 시작

활동 대체

활동이 보조 컨테이너 스택에서 대체될 수 있습니다. 예를 들어 기본 활동이 최상위 탐색에 사용되고 보조 활동이 선택된 대상인 경우가 있습니다. 최상위 탐색에서 선택한 각 항목은 보조 컨테이너에서 새 활동을 시작하고 이전에 보조 컨테이너에 존재하던 활동을 삭제해야 합니다.

그림 17. 기본 창의 최상위 탐색 활동이 보조 창의 대상 활동을 대체함

탐색 선택이 변경될 때 앱이 보조 컨테이너의 활동을 종료하지 않으면 분할이 접힐 때(기기가 접힐 때) 뒤로 탐색이 혼란스러울 수도 있습니다. 예를 들어 기본 창에 메뉴가 있고 보조 창에 화면 A와 B가 스택되어 있는 경우 사용자가 휴대전화를 접으면 B는 A 위에, A는 메뉴 위에 배치됩니다. 사용자가 B에서 뒤로 이동하면 메뉴 대신 화면 A가 표시됩니다.

이 경우 화면 A를 백 스택에서 제거해야 합니다.

기존 분할에서 측면으로 새 컨테이너에 실행할 때의 기본 동작은 새 보조 컨테이너를 맨 위에 두고 이전 컨테이너를 백 스택에 유지하는 것입니다. clearTop을 사용하여 이전 보조 컨테이너를 지우고 정상적으로 새 활동을 실행하도록 분할을 구성할 수 있습니다.

<SplitPairRule
    window:clearTop="true">
    <SplitPairFilter
        window:primaryActivityName=".Menu"
        window:secondaryActivityName=".ScreenA"/>
    <SplitPairFilter
        window:primaryActivityName=".Menu"
        window:secondaryActivityName=".ScreenB"/>
</SplitPairRule>

Kotlin

class MenuActivity {
    …
    fun onMenuItemSelected(selectedMenuItem: Int) {
        startActivity(Intent(this, classForItem(selectedMenuItem)))
    }
}

자바

public class MenuActivity {
    …
    void onMenuItemSelected(int selectedMenuItem) {
        startActivity(new Intent(this, classForItem(selectedMenuItem)));
    }
}

또는 동일한 보조 활동을 사용하고 기본(메뉴) 활동에서는 동일한 인스턴스로 확인되지만 상태 또는 UI 업데이트를 보조 컨테이너에 트리거하는 새 인텐트를 전송합니다.

여러 분할

앱은 추가 활동을 측면에 실행하여 여러 수준의 딥 탐색을 제공할 수 있습니다.

보조 컨테이너의 활동이 새 활동을 측면에 실행하면 기존 분할 위에 새 분할이 생성됩니다.

그림 18. 활동 B가 측면에 활동 C를 시작함

백 스택에는 이전에 열었던 모든 활동이 포함되어 있으므로 사용자는 C를 종료한 후 A/B 분할로 이동할 수 있습니다.

스택 하나에 있는 활동 A, B, C. 활동은 위에서 아래로 C, B, A의 순서로 스택됨

새 분할을 만들려면 기존의 보조 컨테이너에서 측면으로 새 활동을 실행합니다. A/B 분할과 B/C 분할에 모두 구성을 선언하고 정상적으로 B에서 활동 C를 실행합니다.

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
    <SplitPairFilter
        window:primaryActivityName=".B"
        window:secondaryActivityName=".C"/>
</SplitPairRule>

Kotlin

class B {
    fun onOpenC() {
        startActivity(Intent(this, C::class.java))
    }
}

자바

public class B {
    …
    void onOpenC() {
        startActivity(new Intent(this, C.class));
    }
}

분할 상태 변경에 관한 반응

앱의 다양한 활동에는 동일한 기능을 실행하는 UI 요소가 있을 수 있습니다. 예를 들어 계정 설정이 포함된 창을 여는 컨트롤이 있습니다.

그림 19. 기능적으로 동일한 UI 요소를 가진 여러 활동

공통된 UI 요소를 가진 두 활동이 분할되는 경우 중복되어 이 요소를 두 활동 모두에서 표시하면 혼란스러울 수 있습니다.

그림 20. 활동 분할에 중복된 UI 요소

활동이 분할에 배치되는 시점을 확인하려면 SplitController를 사용하여 분할 등록 변경 리스너를 등록합니다. 그런 다음 UI를 다음과 같이 조정합니다.

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    splitController
        .addSplitListener(this, mainThreadExecutor, SplitInfoChangeCallback())
}

inner class SplitInfoChangeCallback : Consumer<List<SplitInfo>> {
    override fun accept(splitInfoList: List<SplitInfo>) {
        findViewById<View>(R.id.infoButton).visibility =
            if (!splitInfoList.isEmpty()) View.GONE else View.VISIBLE
    }
}

자바

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    splitController
        .addSplitListener(this, mainThreadExecutor, SplitInfoChangeCallback());
}

class SplitInfoChangeCallback extends Consumer<List<SplitInfo>> {
    public void accept(List<SplitInfo> splitInfoList) {
        findViewById<View>(R.id.infoButton).visibility =
            !splitInfoList.isEmpty()) ? View.GONE : View.VISIBLE;
    }
}

활동이 중지되는 시점을 포함하여 모든 수명 주기 상태에서 콜백할 수 있습니다. 리스너는 일반적으로 onStart()에 등록되고 onStop()에 등록 취소되어야 합니다.

전체 크기 창 모달

일부 활동은 지정된 작업이 실행될 때까지 사용자가 애플리케이션과 상호작용하지 못하도록 차단합니다. 예를 들어 로그인 화면 활동, 정책 확인 화면, 오류 메시지 등이 있습니다. 모달 활동은 분할에 표시되지 않아야 합니다.

확장 구성을 사용하여 활동이 항상 작업 창을 채우도록 강제할 수 있습니다.

<ActivityRule
    window:alwaysExpand="true">
    <ActivityFilter
        window:activityName=".FullWidthActivity"/>
</ActivityRule>

활동 종료

사용자는 디스플레이 가장자리에서 스와이프하여 분할의 한쪽 측면의 활동을 종료할 수 있습니다.

그림 21. 스와이프 동작으로 활동 B 종료
그림 22. 스와이프 동작으로 활동 A 종료

기기가 동작 탐색 대신 뒤로 버튼을 사용하도록 설정된 경우 포커스가 맞춰진 활동(마지막으로 터치 또는 실행된 활동)으로 입력이 전송됩니다.

분할에 있는 활동 중 하나를 완료한 결과는 분할 구성에 따라 다릅니다.

기본 구성

분할의 활동 하나가 종료되면 나머지 활동이 전체 창을 차지합니다.

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

활동 A와 활동 B가 포함된 분할. A가 종료되어 B가 전체 창을 차지하게 됨.

활동 A와 활동 B가 포함된 분할. B가 종료되어 A가 전체 창을 차지하게 됨

활동 함께 종료

보조 활동이 종료되면 기본 활동을 자동으로 종료합니다.

<SplitPairRule
    window:finishPrimaryWithSecondary="true">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

활동 A와 활동 B가 포함된 분할. B의 종료에 따라 A도 종료되어 작업 창이 비게 됨.

활동 A와 활동 B가 포함된 분할. A가 종료되어 B가 작업 창에 단독으로 남음

기본 활동이 종료되면 보조 활동을 자동으로 종료합니다.

<SplitPairRule
    window:finishSecondaryWithPrimary="true">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

활동 A와 활동 B가 포함된 분할. A의 종료에 따라 B도 종료되어 작업 창이 비게 됨.

활동 A와 활동 B가 포함된 분할. B가 종료되어 A가 작업 창에 단독으로 남음

기본 또는 보조가 종료되면 활동을 함께 종료합니다.

<SplitPairRule
    window:finishPrimaryWithSecondary="true"
    window:finishSecondaryWithPrimary="true">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

활동 A와 활동 B가 포함된 분할. A의 종료에 따라 B도 종료되어 작업 창이 비게 됨.

활동 A와 활동 B가 포함된 분할. B의 종료에 따라 A도 종료되어 작업 창이 비게 됨.

컨테이너의 여러 활동 종료

분할 컨테이너에 여러 활동이 스택된 경우 스택 맨 아래에 있는 활동을 종료해도 맨 위에 있는 활동이 자동으로 종료되지는 않습니다.

예를 들어 보조 컨테이너에 두 활동, 즉 활동 B 위에 활동 C가 있으며

B 위에 스택된 활동 C를 포함하는 보조 액티비티 스택은 활동 A를 포함하는 기본 액티비티 스택 위에 스택되어 있습니다.

분할 구성이 활동 A와 활동의 B의 구성에 의해 정의되는 경우

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

맨 위 활동을 종료하면 분할이 유지됩니다.

기본 컨테이너의 활동 A와 보조 컨테이너의 활동 B, C로 분할되며 C는 B 위에 스택됨. C가 종료되고 A와 B가 활동 분할에 남음

보조 컨테이너의 맨 아래(루트) 활동을 종료해도 그 위에 있는 활동은 삭제되지 않으며 분할도 유지됩니다.

기본 컨테이너의 활동 A와 보조 컨테이너의 활동 B, C로 분할되며 C는 B 위에 스택됨. B가 종료되고 A와 C가 활동 분할에 남음

기본 활동과 함께 보조 활동을 종료하는 경우와 같이 활동을 함께 종료하기 위한 추가 규칙도 실행됩니다.

<SplitPairRule
    window:finishSecondaryWithPrimary="true">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

기본 컨테이너의 활동 A와 보조 컨테이너의 활동 B, C로 분할되며 C는 B 위에 스택됨. A가 종료되고 B와 C도 종료됨

기본과 보조를 함께 종료하도록 분할이 구성된 경우 다음과 같습니다.

<SplitPairRule
    window:finishPrimaryWithSecondary="true"
    window:finishSecondaryWithPrimary="true">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

기본 컨테이너의 활동 A와 보조 컨테이너의 활동 B, C로 분할되며 C는 B 위에 스택됨. C가 종료되고 A와 B가 활동 분할에 남음

기본 컨테이너의 활동 A와 보조 컨테이너의 활동 B, C로 분할되며 C는 B 위에 스택됨. B가 종료되고 A와 C가 활동 분할에 남음

기본 컨테이너의 활동 A와 보조 컨테이너의 활동 B, C로 분할되며 C는 B 위에 스택됨. A가 종료되고 B와 C도 종료됨

런타임 시 분할 속성 변경

현재 활성 상태이며 표시되는 분할의 속성은 변경할 수 없습니다. 분할 규칙을 변경하면 추가 활동 실행 및 새 컨테이너에 영향을 주지만 기존의 활성 분할에는 영향을 주지 않습니다.

활성 분할의 속성을 변경하려면 분할의 측면 활동을 종료한 후 새 구성으로 다시 측면으로 실행합니다.

분할에서 전체 크기 창으로 활동 추출

측면 활동 전체 크기 창을 표시하는 새 구성을 만든 다음 동일한 인스턴스로 확인되는 인텐트로 활동을 다시 실행합니다.

런타임 시 분할 지원 확인

활동 삽입은 12L(API 수준 32)의 기능이지만 이전 플랫폼 버전을 사용하는 일부 기기에서 사용할 수 있습니다. 이 기능의 사용 가능성을 런타임 시 확인하려면 SplitController.isSplitSupported() 메서드를 사용합니다.

Kotlin

val splitController = SplitController.Companion.getInstance()
if (splitController.isSplitSupported()) {
    // Device supports split activity features.
}

자바

SplitController splitController = SplitController.Companion.getInstance();
if (splitController.isSplitSupported()) {
  // Device supports split activity features.
}

분할이 지원되지 않으면 활동이 일반 모델에 따라 맨 위에서 실행됩니다.

제한사항, 제약, 주의사항

  • 작업의 루트 활동의 소유자로 식별되는 작업의 호스트 앱만 다른 활동을 구성하고 작업에 삽입할 수 있습니다. 삽입 및 분할을 지원하는 활동이 다른 애플리케이션에 속한 작업에서 실행되는 경우 이러한 활동에는 삽입 및 분할이 작동하지 않습니다.
  • 단일 작업 내에서만 활동을 구성할 수 있습니다. 새 작업에서 활동을 실행하면 항상 기존 분할 외부의 새로운 확장 창에 활동이 배치됩니다.
  • 동일한 프로세스의 활동만 구성하고 분할에 배치할 수 있습니다. SplitInfo 콜백은 동일한 프로세스에 속한 활동만 보고합니다. 다른 프로세스의 활동에 관해 알 수 있는 방법이 없기 때문입니다.
  • 각 쌍 또는 단일 활동 규칙은 규칙 등록 후 발생하는 활동 실행에만 적용됩니다. 기존 분할이나 시각적 속성을 업데이트할 수 있는 방법은 현재 없습니다.
  • 분할 쌍 필터 구성은 활동을 완전히 실행할 때 사용된 인텐트와 일치해야 합니다. 이러한 일치는 애플리케이션 프로세스에서 새로운 활동이 시작될 때 발생합니다. 따라서 암시적 인텐트를 사용할 때 시스템 프로세스의 후반에 확인되는 구성요소 이름을 모를 수도 있습니다. 실행 시점에 구성요소 이름을 알 수 없는 경우 와일드 카드('*/*')를 대신 사용할 수 있으며 인텐트 작업을 기반으로 필터링할 수 있습니다.
  • 분할이 생성된 후 컨테이너 간에 또는 분할 내부나 외부로 활동을 이동할 수 있는 방법은 현재 없습니다. 일치하는 규칙이 있는 새 활동이 실행될 때만 WindowManager 라이브러리에 의해 분할이 생성되며 분할 컨테이너의 마지막 활동이 종료될 때 분할이 소멸됩니다.
  • 구성이 변경될 때 활동을 다시 실행할 수 있으므로 분할을 만들거나 삭제하고 활동 경계가 변경되면 활동은 이전 인스턴스가 완전히 소멸되고 새 인스턴스가 생성되는 과정을 거칠 수 있습니다. 따라서 앱 개발자는 수명 주기 콜백에서 새 활동을 실행하는 등의 작업에 주의해야 합니다.

추가 리소스