การฝังกิจกรรม

การฝังกิจกรรมจะเพิ่มประสิทธิภาพแอปในอุปกรณ์หน้าจอขนาดใหญ่โดยการแบ่งหน้าต่างงานของแอปพลิเคชัน ระหว่าง 2 กิจกรรมหรือ 2 อินสแตนซ์ของกิจกรรมเดียวกัน

รูปที่ 1 แอปการตั้งค่าที่มีกิจกรรมอยู่ข้างๆ

หากแอปประกอบด้วยกิจกรรมหลายอย่าง การฝังกิจกรรมจะช่วยให้คุณ มอบประสบการณ์การใช้งานที่ดียิ่งขึ้นบนแท็บเล็ต อุปกรณ์พับ และอุปกรณ์ ChromeOS

การฝังกิจกรรมไม่จำเป็นต้องปรับโครงสร้างโค้ด คุณกำหนดวิธีที่แอปแสดงกิจกรรม ไม่ว่าจะแสดงแบบเคียงข้างกันหรือซ้อนกันได้โดยการสร้างไฟล์การกำหนดค่า XML หรือโดยการเรียก API ของ Jetpack WindowManager

ระบบจะดูแลการรองรับหน้าจอขนาดเล็กโดยอัตโนมัติ เมื่อแอปของคุณอยู่ใน อุปกรณ์ที่มีหน้าจอขนาดเล็ก กิจกรรมจะซ้อนกัน ในหน้าจอขนาดใหญ่ กิจกรรมจะแสดงแบบเคียงข้างกัน ระบบจะกำหนดการนำเสนอตามการกำหนดค่าที่คุณสร้างขึ้น โดยไม่จำเป็นต้องมีตรรกะการแยกสาขา

การฝังกิจกรรมจะรองรับการเปลี่ยนแปลงการวางแนวอุปกรณ์และทำงานได้อย่างราบรื่น ในอุปกรณ์แบบพับได้ โดยจะซ้อนและยกเลิกการซ้อนกิจกรรมเมื่ออุปกรณ์พับและ กางออก

การฝังกิจกรรมใช้ได้ในอุปกรณ์หน้าจอขนาดใหญ่ส่วนใหญ่ที่ใช้ Android 12L (API ระดับ 32) ขึ้นไป

แยกหน้าต่างงาน

การฝังกิจกรรมจะแบ่งหน้าต่างงานของแอปออกเป็น 2 คอนเทนเนอร์ ได้แก่ คอนเทนเนอร์หลักและ คอนเทนเนอร์รอง คอนเทนเนอร์จะเก็บกิจกรรมที่เปิดจากกิจกรรมหลักหรือจากกิจกรรมอื่นๆ ที่อยู่ในคอนเทนเนอร์อยู่แล้ว

กิจกรรมจะซ้อนกันในคอนเทนเนอร์รองเมื่อเปิดใช้งาน และ คอนเทนเนอร์รองจะซ้อนอยู่เหนือคอนเทนเนอร์หลักในหน้าจอขนาดเล็ก ดังนั้นการซ้อนกิจกรรมและการไปยังส่วนต่างๆ ด้วยปุ่มย้อนกลับจึงสอดคล้องกับการจัดลำดับ กิจกรรมที่สร้างไว้ในแอปอยู่แล้ว

การฝังกิจกรรมช่วยให้คุณแสดงกิจกรรมได้หลากหลายวิธี แอปของคุณสามารถแยกหน้าต่างงานได้โดยการเปิด 2 กิจกรรมพร้อมกันแบบเคียงข้างกันหรือเปิดกิจกรรมหนึ่งเหนืออีกกิจกรรมหนึ่ง ดังนี้

รูปที่ 2 กิจกรรม 2 รายการอยู่ข้างกันและอีก 1 รายการอยู่ด้านบน

กิจกรรมที่ครอบครองทั้งหน้าต่างงานจะสร้างการแยกหน้าจอได้โดย เปิดกิจกรรมใหม่ควบคู่กัน ดังนี้

รูปที่ 3 กิจกรรม A เริ่มกิจกรรม B ที่ด้านข้าง

กิจกรรมที่อยู่ในโหมดแยกหน้าจอและแชร์หน้าต่างงานจะเปิดกิจกรรมอื่นๆ ได้ด้วยวิธีต่อไปนี้

  • หากต้องการวางซ้อนกิจกรรมอื่น ให้ทำดังนี้

    รูปที่ 4 กิจกรรม A เริ่มกิจกรรม C ไปด้านข้างกิจกรรม B
  • ไปด้านข้างและเลื่อนการแยกหน้าจอไปด้านข้างเพื่อซ่อนกิจกรรมหลักก่อนหน้า

    รูปที่ 5 กิจกรรม B เริ่มกิจกรรม C ไปด้านข้างและเลื่อน การแยกไปด้านข้าง
  • เปิดกิจกรรมในตำแหน่งเดิมที่ด้านบน ซึ่งก็คือในสแต็กกิจกรรมเดียวกัน

    รูปที่ 6 กิจกรรม B เริ่มกิจกรรม C โดยไม่มีค่าสถานะ Intent เพิ่มเติม
  • เปิดกิจกรรมแบบเต็มหน้าต่างในงานเดียวกัน

    รูปที่ 7 กิจกรรม A หรือกิจกรรม B จะเริ่มกิจกรรม C ซึ่งจะเติม หน้าต่างงาน

การนำทางย้อนกลับ

แอปพลิเคชันประเภทต่างๆ อาจมีกฎการนำทางย้อนกลับที่แตกต่างกันในสถานะหน้าต่างงานที่แยกกัน โดยขึ้นอยู่กับความสัมพันธ์ระหว่างกิจกรรมหรือวิธีที่ผู้ใช้ทริกเกอร์เหตุการณ์ย้อนกลับ เช่น

  • การดำเนินการร่วมกัน: หากกิจกรรมมีความเกี่ยวข้องและไม่ควรแสดงกิจกรรมหนึ่งโดยไม่มีอีกกิจกรรมหนึ่ง คุณจะกำหนดค่าการนำทางด้วยท่าทางสัมผัสย้อนกลับให้เสร็จสิ้นทั้ง 2 กิจกรรมได้
  • การทำงานแบบแยกกัน: หากกิจกรรมเป็นอิสระอย่างสมบูรณ์ การนำทางย้อนกลับในกิจกรรมจะไม่ส่งผลต่อสถานะของกิจกรรมอื่นในหน้าต่างงาน

ระบบจะส่งเหตุการณ์ย้อนกลับไปยังกิจกรรมที่โฟกัสล่าสุดเมื่อใช้การนำทางด้วยปุ่ม

สำหรับการไปยังส่วนต่างๆ ด้วยท่าทางสัมผัส ให้ทำดังนี้

  • Android 14 (API ระดับ 34) และต่ำกว่า - ระบบจะส่งเหตุการณ์ย้อนกลับไปยัง กิจกรรมที่เกิดท่าทางสัมผัส เมื่อผู้ใช้ปัดจากด้านซ้ายของหน้าจอ ระบบจะส่งเหตุการณ์ย้อนกลับไปยังกิจกรรมในแผงด้านซ้ายของหน้าต่างที่แยก เมื่อผู้ใช้ปัดจากด้านขวาของหน้าจอ ระบบจะส่งเหตุการณ์ย้อนกลับไปยังกิจกรรมในแผงด้านขวา

  • Android 15 (API ระดับ 35) ขึ้นไป

    • เมื่อจัดการกับกิจกรรมหลายอย่างจากแอปเดียวกัน ท่าทางสัมผัสจะ สิ้นสุดกิจกรรมที่ด้านบนสุดโดยไม่คำนึงถึงทิศทางการปัด ซึ่งจะมอบประสบการณ์การใช้งานที่ เป็นหนึ่งเดียวกันมากขึ้น

    • ในสถานการณ์ที่มีกิจกรรม 2 รายการจากแอปต่างๆ (การซ้อนทับ) ระบบจะส่ง เหตุการณ์ย้อนกลับไปยังกิจกรรมล่าสุดที่โฟกัส ซึ่งสอดคล้องกับ ลักษณะการทำงานของการนำทางด้วยปุ่ม

เลย์เอาต์แบบหลายบานหน้าต่าง

Jetpack WindowManager ช่วยให้คุณสร้างเลย์เอาต์แบบหลายบานหน้าต่างที่ฝังกิจกรรมในอุปกรณ์หน้าจอขนาดใหญ่ที่ใช้ Android 12L (API ระดับ 32) ขึ้นไป และในอุปกรณ์บางรุ่นที่ใช้แพลตฟอร์มเวอร์ชันก่อนหน้า แอปที่มีอยู่ซึ่งอิงตาม กิจกรรมหลายรายการแทนที่จะเป็น Fragment หรือเลย์เอาต์ที่อิงตามมุมมอง เช่น SlidingPaneLayout สามารถมอบประสบการณ์การใช้งานหน้าจอขนาดใหญ่ที่ดีขึ้น โดยไม่ต้องปรับโครงสร้างซอร์สโค้ด

ตัวอย่างที่พบบ่อยคือการแยกรายการ-รายละเอียด เพื่อให้การนำเสนอมีคุณภาพสูง ระบบจะเริ่มกิจกรรมรายการ จากนั้นแอปพลิเคชัน จะเริ่มกิจกรรมรายละเอียดทันที ระบบการเปลี่ยนภาพจะรอจนกว่าจะวาดทั้ง 2 กิจกรรม แล้วจึงแสดงพร้อมกัน สำหรับผู้ใช้ กิจกรรมทั้ง 2 รายการจะเปิดขึ้นพร้อมกัน

รูปที่ 8 กิจกรรม 2 อย่างที่เริ่มพร้อมกันในเลย์เอาต์แบบหลายบานหน้าต่าง

แอตทริบิวต์ที่แยก

คุณระบุสัดส่วนของหน้าต่างงานระหว่างคอนเทนเนอร์ที่แยกกัน และวิธีจัดวางคอนเทนเนอร์ที่สัมพันธ์กันได้

สำหรับกฎที่กำหนดไว้ในไฟล์การกำหนดค่า XML ให้ตั้งค่าแอตทริบิวต์ต่อไปนี้

  • splitRatio: กำหนดสัดส่วนของคอนเทนเนอร์ ค่าคือตัวเลขทศนิยม ในช่วงเปิด (0.0, 1.0)
  • splitLayoutDirection: ระบุวิธีจัดวางคอนเทนเนอร์ที่แยก เทียบกับคอนเทนเนอร์อื่นๆ ค่าต่างๆ มีดังนี้
    • ltr: จากซ้ายไปขวา
    • rtl: ขวาไปซ้าย
    • locale: ระบบจะกำหนด ltr หรือ rtl จากการตั้งค่าภาษา

ดูตัวอย่างได้ที่ส่วนการกำหนดค่า XML

สำหรับกฎที่สร้างโดยใช้ WindowManager API ให้สร้างออบเจ็กต์ SplitAttributes ด้วย SplitAttributes.Builder และเรียกใช้เมธอด Builder ต่อไปนี้

  • setSplitType(): กำหนดสัดส่วนของคอนเทนเนอร์ที่แยก ดูSplitAttributes.SplitTypeสำหรับอาร์กิวเมนต์ที่ถูกต้อง รวมถึงเมธอด SplitAttributes.SplitType.ratio()
  • setLayoutDirection(): กำหนดเลย์เอาต์ของคอนเทนเนอร์ ดูค่าที่เป็นไปได้ใน SplitAttributes.LayoutDirection

    ไม่รองรับแอตทริบิวต์ XML ที่เทียบเท่า

ดูตัวอย่างได้ที่ส่วน WindowManager API

รูปที่ 9 กิจกรรม 2 รายการที่แบ่งออกเป็นซ้ายและขวา แต่มีอัตราส่วนการแบ่งที่แตกต่างกัน

การวางแนวแยก

ขนาดและสัดส่วนภาพของจอแสดงผลจะเป็นตัวกำหนดตำแหน่งของ กิจกรรมในการแยกการฝังกิจกรรม ในจอแสดงผลแนวนอนขนาดใหญ่ กิจกรรมจะแสดงเคียงข้างกัน ส่วนในจอแสดงผลแนวตั้งสูงหรือการวางบนโต๊ะในอุปกรณ์พับได้ กิจกรรมจะแสดงซ้อนกัน

คุณระบุการวางแนวการแยกได้ด้วยเครื่องคำนวณ SplitController SplitAttributes เครื่องคำนวณจะคำนวณ SplitAttributes สำหรับ SplitRule ที่ใช้งานอยู่

ใช้เครื่องคิดเลขเพื่อแยกคอนเทนเนอร์หลักในทิศทางต่างๆ สำหรับ สถานะอุปกรณ์ที่แตกต่างกัน เช่น

Kotlin

if (WindowSdkExtensions.getInstance().extensionVersion >= 2) {
    SplitController.getInstance(this).setSplitAttributesCalculator { params ->
        val parentConfiguration = params.parentConfiguration
        val builder = SplitAttributes.Builder()
        return@setSplitAttributesCalculator if (parentConfiguration.screenWidthDp >= 840) {
            // Side-by-side dual-pane layout for wide displays.
            builder
                .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
                .build()
        } else if (parentConfiguration.screenHeightDp >= 600) {
            // Horizontal split for tall displays.
            builder
                .setLayoutDirection(SplitAttributes.LayoutDirection.BOTTOM_TO_TOP)
                .build()
        } else {
            // Fallback to expand the secondary container.
            builder
                .setSplitType(SPLIT_TYPE_EXPAND)
                .build()
        }
    }
}

Java

if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 2) {
    SplitController.getInstance(this).setSplitAttributesCalculator(params -> {
        Configuration parentConfiguration = params.getParentConfiguration();
        SplitAttributes.Builder builder = new SplitAttributes.Builder();
        if (parentConfiguration.screenWidthDp >= 840) {
            // Side-by-side dual-pane layout for wide displays.
            return builder
                .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
                .build();
        } else if (parentConfiguration.screenHeightDp >= 600) {
            // Horizontal split for tall displays.
            return builder
                .setLayoutDirection(SplitAttributes.LayoutDirection.BOTTOM_TO_TOP)
                .build();
        } else {
            // Fallback to expand the secondary container.
            return builder
                .setSplitType(SplitType.SPLIT_TYPE_EXPAND)
                .build();
        }
    });
}

ในอุปกรณ์แบบพับได้ คุณจะแยกหน้าจอในแนวตั้งได้หากอุปกรณ์อยู่ในแนวนอน แสดงกิจกรรมเดียวได้หากอุปกรณ์อยู่ในแนวตั้ง และแยกหน้าจอในแนวนอนได้หากอุปกรณ์อยู่ในท่าตั้งบนโต๊ะ

Kotlin

if (WindowSdkExtensions.getInstance().extensionVersion >= 2) {
    SplitController.getInstance(this).setSplitAttributesCalculator { params ->
        val tag = params.splitRuleTag
        val parentWindowMetrics = params.parentWindowMetrics
        val parentConfiguration = params.parentConfiguration
        val foldingFeatures =
            params.parentWindowLayoutInfo.displayFeatures.filterIsInstance<FoldingFeature>()
        val feature = if (foldingFeatures.size == 1) foldingFeatures[0] else null
        val builder = SplitAttributes.Builder()
        builder.setSplitType(SPLIT_TYPE_HINGE)
        return@setSplitAttributesCalculator if (feature?.isSeparating == true) {
            // Horizontal split for tabletop posture.
            builder
                .setSplitType(SPLIT_TYPE_HINGE)
                .setLayoutDirection(
                    if (feature.orientation == FoldingFeature.Orientation.HORIZONTAL) {
                        SplitAttributes.LayoutDirection.BOTTOM_TO_TOP
                    } else {
                        SplitAttributes.LayoutDirection.LOCALE
                    }
                )
                .build()
        } else if (parentConfiguration.screenWidthDp >= 840) {
            // Side-by-side dual-pane layout for wide displays.
            builder
                .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
                .build()
        } else {
            // No split for tall displays.
            builder
                .setSplitType(SPLIT_TYPE_EXPAND)
                .build()
        }
    }
}

Java

if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 2) {
    SplitController.getInstance(this).setSplitAttributesCalculator(params -> {
        String tag = params.getSplitRuleTag();
        WindowMetrics parentWindowMetrics = params.getParentWindowMetrics();
        Configuration parentConfiguration = params.getParentConfiguration();
        List<FoldingFeature> foldingFeatures =
            params.getParentWindowLayoutInfo().getDisplayFeatures().stream().filter(
                    item -> item instanceof FoldingFeature)
                .map(item -> (FoldingFeature) item)
                .collect(Collectors.toList());
        FoldingFeature feature = foldingFeatures.size() == 1 ? foldingFeatures.get(0) : null;
        SplitAttributes.Builder builder = new SplitAttributes.Builder();
        builder.setSplitType(SplitType.SPLIT_TYPE_HINGE);
        if (feature != null && feature.isSeparating()) {
            // Horizontal slit for tabletop posture.
            return builder
                .setSplitType(SplitType.SPLIT_TYPE_HINGE)
                .setLayoutDirection(
                    feature.getOrientation() == FoldingFeature.Orientation.HORIZONTAL
                        ? SplitAttributes.LayoutDirection.BOTTOM_TO_TOP
                        : SplitAttributes.LayoutDirection.LOCALE)
                .build();
        }
        else if (parentConfiguration.screenWidthDp >= 840) {
            // Side-by-side dual-pane layout for wide displays.
            return builder
                .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
                .build();
        } else {
            // No split for tall displays.
            return builder
                .setSplitType(SplitType.SPLIT_TYPE_EXPAND)
                .build();
        }
    });
}

ไม่รองรับแอตทริบิวต์ XML ที่เทียบเท่า

ตัวยึดตำแหน่ง

กิจกรรมตัวยึดตำแหน่งคือกิจกรรมรองที่ว่างเปล่าซึ่งใช้พื้นที่ใน การแยกกิจกรรม ในท้ายที่สุดแล้ว กิจกรรมเหล่านี้จะถูกแทนที่ด้วยกิจกรรมอื่น ที่มีเนื้อหา ตัวอย่างเช่น กิจกรรมตัวยึดตำแหน่งอาจอยู่ใน ด้านรองของการแยกกิจกรรมในเลย์เอาต์รายการ-รายละเอียดจนกว่าจะมีการเลือกรายการจาก รายการ เมื่อถึงจุดนั้น กิจกรรมที่มีข้อมูลรายละเอียดสำหรับรายการที่เลือกจะ แทนที่ตัวยึดตำแหน่ง

โดยค่าเริ่มต้น ระบบจะแสดงตัวยึดตำแหน่งเมื่อมีพื้นที่เพียงพอสำหรับ การแยกกิจกรรมเท่านั้น ตัวยึดตำแหน่งจะสิ้นสุดโดยอัตโนมัติเมื่อขนาดการแสดงผลเปลี่ยนเป็นความกว้างหรือความสูงที่เล็กเกินกว่าจะแสดงการแยก เมื่อมีพื้นที่ว่าง ระบบจะเปิดตัวตัวยึดตำแหน่งอีกครั้งโดยมีสถานะเริ่มต้นใหม่

รูปที่ 10 อุปกรณ์ที่พับได้กำลังพับและกางออก ระบบจะสิ้นสุดกิจกรรม Placeholder และสร้างกิจกรรมขึ้นใหม่เมื่อขนาดการแสดงผลเปลี่ยนแปลง

อย่างไรก็ตาม แอตทริบิวต์ stickyPlaceholder ของเมธอด SplitPlaceholderRule หรือ setSticky() ของ SplitPlaceholder.Builder สามารถลบล้างลักษณะการทำงานเริ่มต้นได้ เมื่อแอตทริบิวต์หรือเมธอดระบุค่า true ระบบจะแสดงตัวยึดตำแหน่งเป็นกิจกรรมบนสุดในหน้าต่างงานเมื่อมีการปรับขนาดจอแสดงผลจากจอแสดงผลแบบ 2 บานหน้าต่างเป็นจอแสดงผลแบบบานหน้าต่างเดียว (ดูตัวอย่างได้ที่การกำหนดค่าการแยก)

รูปที่ 11 อุปกรณ์ที่พับได้กำลังพับและกางออก กิจกรรมตัวยึดตำแหน่ง จะคงอยู่

การเปลี่ยนแปลงขนาดหน้าต่าง

เมื่อการเปลี่ยนแปลงการกำหนดค่าอุปกรณ์ลดความกว้างของหน้าต่างงานจนไม่ ใหญ่พอสำหรับการจัดวางแบบหลายบานหน้าต่าง (เช่น เมื่ออุปกรณ์พับได้หน้าจอขนาดใหญ่ พับจากขนาดแท็บเล็ตเป็นขนาดโทรศัพท์ หรือเมื่อมีการปรับขนาดหน้าต่างแอปใน โหมดหลายหน้าต่าง) ระบบจะซ้อนกิจกรรมที่ไม่ใช่ตัวยึดตำแหน่งในบานหน้าต่างรองของ หน้าต่างงานไว้ด้านบนของกิจกรรมในบานหน้าต่างหลัก

กิจกรรมตัวยึดตำแหน่งจะแสดงเมื่อมีความกว้างของจอแสดงผลเพียงพอสำหรับการ แยกเท่านั้น ในหน้าจอขนาดเล็ก ระบบจะปิดพื้นที่ที่ใช้แทนโดยอัตโนมัติ เมื่อ พื้นที่แสดงผลมีขนาดใหญ่พออีกครั้ง ระบบจะสร้างตัวยึดตำแหน่งขึ้นมาใหม่ (ดูส่วนตัวยึดตำแหน่ง)

การซ้อนกิจกรรมเป็นไปได้เนื่องจาก WindowManager จะจัดลำดับ Z ของกิจกรรม ในแผงรองเหนือกิจกรรมในแผงหลัก

กิจกรรมหลายรายการในแผงรอง

กิจกรรม B เริ่มกิจกรรม C ในที่เดิมโดยไม่มีค่าสถานะ Intent เพิ่มเติม

การแยกกิจกรรมที่มีกิจกรรม ก ข และ ค โดยมี ค ซ้อนอยู่
          ด้านบนของ ข

ซึ่งส่งผลให้กิจกรรมในงานเดียวกันมีลำดับ Z ดังนี้

สแต็กกิจกรรมรองที่มีกิจกรรม C ซ้อนอยู่บน B
          สแต็กรองจะซ้อนอยู่เหนือสแต็กกิจกรรมหลัก
          ซึ่งมีกิจกรรม ก

ดังนั้นในหน้าต่างงานที่เล็กลง แอปพลิเคชันจะหดเหลือเพียงกิจกรรมเดียวโดยมี C อยู่ที่ด้านบนของสแต็ก

หน้าต่างเล็กๆ ที่แสดงเฉพาะกิจกรรม C

การไปยังส่วนต่างๆ กลับในหน้าต่างขนาดเล็กจะนำคุณไปยังกิจกรรมที่ซ้อนกัน

หากมีการคืนค่าการกำหนดค่าหน้าต่างงานเป็นขนาดที่ใหญ่ขึ้นซึ่งรองรับหลายแผงได้ กิจกรรมจะแสดงแบบคู่ขนานอีกครั้ง

การแยกแบบซ้อน

กิจกรรม B จะเริ่มกิจกรรม C ไปด้านข้างและเลื่อนการแยกหน้าจอไปด้านข้าง

หน้าต่างงานแสดงกิจกรรม ก และ ข จากนั้นแสดงกิจกรรม ข และ ค

ผลลัพธ์คือลำดับ Z ของกิจกรรมต่อไปนี้ในงานเดียวกัน

กิจกรรม ก, ข และ ค ในสแต็กเดียว กิจกรรมจะซ้อนกัน
          ตามลําดับต่อไปนี้จากบนลงล่าง: ค, ข, ก

ในหน้าต่างงานขนาดเล็ก แอปพลิเคชันจะย่อเหลือกิจกรรมเดียวโดยมี C อยู่ด้านบน

หน้าต่างเล็กๆ ที่แสดงเฉพาะกิจกรรม C

การวางแนวตั้งแบบคงที่

การตั้งค่าไฟล์ Manifest android:screenOrientation ช่วยให้แอปจำกัด กิจกรรมให้เป็นการวางแนวตั้งหรือแนวนอนได้ เพื่อปรับปรุงประสบการณ์ของผู้ใช้ ในอุปกรณ์หน้าจอขนาดใหญ่ เช่น แท็บเล็ตและอุปกรณ์แบบพับได้ ผู้ผลิตอุปกรณ์ (OEM) สามารถไม่สนใจคำขอการวางแนวหน้าจอและแสดงแอปในรูปแบบจดหมาย ในแนวนอนบนจอแสดงผลแนวนอน หรือในแนวนอนบนจอแสดงผลแนวตั้ง

รูปที่ 12 กิจกรรมแบบ Letterbox: แนวตั้งคงที่บนอุปกรณ์แนวนอน (ซ้าย), แนวนอนคงที่บนอุปกรณ์แนวตั้ง (ขวา)

ในทำนองเดียวกัน เมื่อเปิดใช้การฝังกิจกรรมแล้ว OEM จะปรับแต่งอุปกรณ์เพื่อ แสดงกิจกรรมแนวตั้งแบบคงที่ในแนวนอนบนหน้าจอขนาดใหญ่ (ความกว้าง ≥ 600dp) ได้ เมื่อกิจกรรมแนวตั้งคงที่เปิดใช้กิจกรรมที่ 2 อุปกรณ์จะแสดงกิจกรรมทั้ง 2 อย่างแบบเคียงข้างกันในจอแสดงผลแบบ 2 บานหน้าต่าง

รูปที่ 13 กิจกรรม A ที่เป็นแนวตั้งแบบคงที่เริ่มกิจกรรม B ไปด้านข้าง

เพิ่มพร็อพเพอร์ตี้ android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED ลงในไฟล์ Manifest ของแอปเสมอเพื่อแจ้งให้อุปกรณ์ทราบว่าแอปของคุณรองรับ การฝังกิจกรรม (ดูส่วนการกำหนดค่าการแยก ) จากนั้นอุปกรณ์ที่ OEM ปรับแต่งจะพิจารณาได้ว่าจะใส่ขอบดำ กิจกรรมแนวตั้งแบบคงที่หรือไม่

การกำหนดค่าการทดสอบ A/B

กฎการแยกจะกำหนดค่าการแยกกิจกรรม คุณกำหนดกฎการแยกในไฟล์การกำหนดค่า XML หรือโดยการเรียกใช้ API ของ WindowManager ของ Jetpack

ไม่ว่าจะในกรณีใดก็ตาม แอปต้องเข้าถึงไลบรารี WindowManager และต้องแจ้งให้ระบบทราบว่าแอปได้ใช้การฝังกิจกรรมแล้ว

โดยทำดังนี้

  1. เพิ่มทรัพยากร Dependency ของไลบรารี WindowManager ล่าสุดลงในไฟล์ build.gradle ระดับโมดูลของแอป เช่น

    implementation 'androidx.window:window:1.1.0-beta02'

    ไลบรารี WindowManager มีคอมโพเนนต์ทั้งหมดที่จำเป็นสำหรับการฝังกิจกรรม

  2. แจ้งให้ระบบทราบว่าแอปของคุณได้ฝังกิจกรรมแล้ว

    เพิ่มพร็อพเพอร์ตี้ android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED ไปยังองค์ประกอบ <application> ของไฟล์ Manifest ของแอป และตั้งค่า เป็น true เช่น

    <manifest xmlns:android="http://schemas.android.com/apk/res/android">
        <application>
            <property
                android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED"
                android:value="true" />
        </application>
    </manifest>
    

    ใน WindowManager เวอร์ชัน 1.1.0-alpha06 ขึ้นไป ระบบจะปิดใช้การแยกการฝังกิจกรรม เว้นแต่จะเพิ่มพร็อพเพอร์ตี้ลงในไฟล์ Manifest และตั้งค่าเป็น true

    นอกจากนี้ ผู้ผลิตอุปกรณ์ยังใช้การตั้งค่านี้เพื่อเปิดใช้ความสามารถที่กำหนดเองสำหรับ แอปที่รองรับการฝังกิจกรรมด้วย เช่น อุปกรณ์สามารถแสดงกิจกรรมที่ใช้ได้เฉพาะในแนวตั้งบนจอแสดงผลแนวนอนโดยมีแถบดำด้านบนและด้านล่างเพื่อปรับทิศทางกิจกรรมสำหรับการเปลี่ยนไปใช้เลย์เอาต์แบบ 2 บานหน้าต่างเมื่อกิจกรรมที่ 2 เริ่มต้น (ดูการวางแนวแบบคงที่ในแนวตั้ง)

การกำหนดค่า XML

หากต้องการสร้างการติดตั้งใช้งานการฝังกิจกรรมที่อิงตาม XML ให้ทำตาม ขั้นตอนต่อไปนี้

  1. สร้างไฟล์ทรัพยากร XML ที่ทำสิ่งต่อไปนี้

    • กำหนดกิจกรรมที่แชร์การแยก
    • กำหนดค่าตัวเลือกการแยก
    • สร้างตัวยึดตำแหน่งสำหรับคอนเทนเนอร์รองของ การแยกเมื่อไม่มีเนื้อหา
    • ระบุกิจกรรมที่ไม่ควรเป็นส่วนหนึ่งของการแยก

    เช่น

    <!-- main_split_config.xml -->
    
    <resources
        xmlns:window="http://schemas.android.com/apk/res-auto">
    
        <!-- Define a split for the named activities. -->
        <SplitPairRule
            window:splitRatio="0.33"
            window:splitLayoutDirection="locale"
            window:splitMinWidthDp="840"
            window:splitMaxAspectRatioInPortrait="alwaysAllow"
            window:finishPrimaryWithSecondary="never"
            window:finishSecondaryWithPrimary="always"
            window:clearTop="false">
            <SplitPairFilter
                window:primaryActivityName=".ListActivity"
                window:secondaryActivityName=".DetailActivity"/>
        </SplitPairRule>
    
        <!-- Specify a placeholder for the secondary container when content is
             not available. -->
        <SplitPlaceholderRule
            window:placeholderActivityName=".PlaceholderActivity"
            window:splitRatio="0.33"
            window:splitLayoutDirection="locale"
            window:splitMinWidthDp="840"
            window:splitMaxAspectRatioInPortrait="alwaysAllow"
            window:stickyPlaceholder="false">
            <ActivityFilter
                window:activityName=".ListActivity"/>
        </SplitPlaceholderRule>
    
        <!-- Define activities that should never be part of a split. Note: Takes
             precedence over other split rules for the activity named in the
             rule. -->
        <ActivityRule
            window:alwaysExpand="true">
            <ActivityFilter
                window:activityName=".ExpandedActivity"/>
        </ActivityRule>
    
    </resources>
    
  2. สร้างตัวเริ่มต้น

    คอมโพเนนต์ WindowManager RuleController จะแยกวิเคราะห์ไฟล์การกำหนดค่า XML และทำให้กฎพร้อมใช้งานในระบบ ไลบรารี Startup ของ Jetpack Initializer ทำให้ไฟล์ XML พร้อมใช้งานกับ RuleController เมื่อแอปเริ่มต้นทำงาน เพื่อให้กฎมีผลเมื่อกิจกรรมใดก็ตามเริ่มขึ้น

    หากต้องการสร้างตัวเริ่มต้น ให้ทำดังนี้

    1. เพิ่มทรัพยากร Dependency ของไลบรารี Jetpack Startup ล่าสุดลงในไฟล์ build.gradle ระดับโมดูล เช่น

      implementation 'androidx.startup:startup-runtime:1.1.1'

    2. สร้างคลาสที่ใช้Initializerอินเทอร์เฟซ

      ตัวเริ่มต้นจะทำให้กฎการแยกพร้อมใช้งานสำหรับ RuleController โดย ส่งรหัสของไฟล์การกำหนดค่า XML (main_split_config.xml) ไปยังเมธอด RuleController.parseRules()

      Kotlin

      class SplitInitializer : Initializer<RuleController> {
      
          override fun create(context: Context): RuleController {
              return RuleController.getInstance(context).apply {
                  setRules(RuleController.parseRules(context, R.xml.main_split_config))
              }
          }
      
          override fun dependencies(): List<Class<out Initializer<*>>> {
              return emptyList()
          }
      }

      Java

      public class SplitInitializer implements Initializer<RuleController> {
      
          @NonNull
          @Override
          public RuleController create(@NonNull Context context) {
              RuleController ruleController = RuleController.getInstance(context);
              ruleController.setRules(
                  RuleController.parseRules(context, R.xml.main_split_config)
              );
               return ruleController;
           }
      
           @NonNull
           @Override
           public List<Class<? extends Initializer<?>>> dependencies() {
               return Collections.emptyList();
           }
      }

  3. สร้างผู้ให้บริการเนื้อหาสำหรับคำจำกัดความของกฎ

    เพิ่ม androidx.startup.InitializationProvider ลงในไฟล์ Manifest ของแอป เป็น <provider> ใส่การอ้างอิงถึงการติดตั้งใช้งาน RuleController ตัวเริ่มต้น SplitInitializer ดังนี้

    <!-- AndroidManifest.xml -->
    
    <provider android:name="androidx.startup.InitializationProvider"
        android:authorities="${applicationId}.androidx-startup"
        android:exported="false"
        tools:node="merge">
        <!-- Make SplitInitializer discoverable by InitializationProvider. -->
        <meta-data android:name="${applicationId}.SplitInitializer"
            android:value="androidx.startup" />
    </provider>
    

    InitializationProvider จะค้นหาและเริ่มต้น SplitInitializer ก่อนที่จะเรียกใช้เมธอด onCreate() ของแอป ด้วยเหตุนี้ กฎการแยกจึงมีผลเมื่อกิจกรรมหลักของแอปเริ่มต้น

WindowManager API

คุณสามารถฝังกิจกรรมโดยใช้โปรแกรมด้วยการเรียก API เพียงไม่กี่ครั้ง เรียกใช้ในเมธอด onCreate() ของคลาสย่อยของ Application เพื่อให้แน่ใจว่ากฎมีผลก่อนที่กิจกรรมใดๆ จะเปิดตัว

หากต้องการสร้างการแยกกิจกรรมโดยอัตโนมัติ ให้ทำดังนี้

  1. สร้างกฎการแยก

    1. สร้างSplitPairFilter ที่ระบุกิจกรรมที่แชร์การแยก

      Kotlin

      val splitPairFilter = SplitPairFilter(
          ComponentName(this, ListActivity::class.java),
          ComponentName(this, DetailActivity::class.java),
          null
      )

      Java

      SplitPairFilter splitPairFilter = new SplitPairFilter(
         new ComponentName(this, ListActivity.class),
         new ComponentName(this, DetailActivity.class),
         null
      );

    2. เพิ่มตัวกรองลงในชุดตัวกรองโดยทำดังนี้

      Kotlin

      val filterSet = setOf(splitPairFilter)

      Java

      Set<SplitPairFilter> filterSet = new HashSet<>();
      filterSet.add(splitPairFilter);
      ```

    3. สร้างแอตทริบิวต์เลย์เอาต์สำหรับการแยก ดังนี้

      Kotlin

      val splitAttributes: SplitAttributes = SplitAttributes.Builder()
          .setSplitType(SplitAttributes.SplitType.ratio(0.33f))
          .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
          .build()

      Java

      SplitAttributes splitAttributes = new SplitAttributes.Builder()
            .setSplitType(SplitAttributes.SplitType.ratio(0.33f))
            .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
            .build();

      SplitAttributes.Builder จะสร้างออบเจ็กต์ที่มีแอตทริบิวต์เลย์เอาต์

      • setSplitType(): กำหนดวิธีจัดสรรพื้นที่แสดงผลที่มีอยู่ให้กับคอนเทนเนอร์กิจกรรมแต่ละรายการ ประเภทการแยกสัดส่วนจะระบุ สัดส่วนของพื้นที่แสดงผลที่ใช้ได้ซึ่งจัดสรรให้กับ คอนเทนเนอร์หลัก ส่วนคอนเทนเนอร์รองจะใช้พื้นที่แสดงผลที่เหลือ ที่ใช้ได้
      • setLayoutDirection(): ระบุวิธีจัดวางคอนเทนเนอร์กิจกรรม เมื่อเทียบกับคอนเทนเนอร์อื่นๆ โดยให้คอนเทนเนอร์หลักอยู่ก่อน
    4. สร้าง SplitPairRule

      Kotlin

      val splitPairRule = SplitPairRule.Builder(filterSet)
          .setDefaultSplitAttributes(splitAttributes)
          .setMinWidthDp(840)
          .setMinSmallestWidthDp(600)
          .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
          .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER)
          .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS)
          .setClearTop(false)
          .build()

      Java

      SplitPairRule splitPairRule = new SplitPairRule.Builder(filterSet)
          .setDefaultSplitAttributes(splitAttributes)
          .setMinWidthDp(840)
          .setMinSmallestWidthDp(600)
          .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
          .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER)
          .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS)
          .setClearTop(false)
          .build();

      SplitPairRule.Builder สร้างและกำหนดค่ากฎ

      • filterSet: มีตัวกรองคู่การแยกที่กำหนดเวลาที่จะใช้กฎโดยการระบุกิจกรรมที่แชร์การแยก
      • setDefaultSplitAttributes(): ใช้แอตทริบิวต์เลย์เอาต์กับกฎ
      • setMinWidthDp(): ตั้งค่าความกว้างการแสดงผลขั้นต่ำ (ใน พิกเซลอิสระความหนาแน่น dp) ที่เปิดใช้การแยก
      • setMinSmallestWidthDp(): กำหนดค่าขั้นต่ำ (เป็น dp) ที่ขนาดการแสดงผลที่เล็กกว่าใน 2 ขนาดต้องมีเพื่อเปิดใช้การแยกหน้าจอโดยไม่คำนึงถึงการวางแนวของอุปกรณ์
      • setMaxAspectRatioInPortrait(): ตั้งค่าอัตราส่วนภาพสูงสุดที่แสดง (สูง:กว้าง) ในการวางแนวตั้งซึ่งจะแสดงการแยกกิจกรรม หากสัดส่วนภาพของจอแสดงผลแนวตั้ง เกินสัดส่วนภาพสูงสุด ระบบจะปิดใช้การแยกจอแสดงผลไม่ว่าจอแสดงผลจะมีความกว้างเท่าใดก็ตาม หมายเหตุ: ค่าเริ่มต้นคือ 1.4 ซึ่ง จะทําให้กิจกรรมครอบครองทั้งหน้าต่างงานในแนวตั้ง บนแท็บเล็ตส่วนใหญ่ ดูเพิ่มเติม SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT และ setMaxAspectRatioInLandscape() ค่าเริ่มต้นสำหรับ แนวนอนคือ ALWAYS_ALLOW
      • setFinishPrimaryWithSecondary(): กำหนดว่าการทำกิจกรรมทั้งหมดในคอนเทนเนอร์รองจะส่งผลต่อกิจกรรมในคอนเทนเนอร์หลักอย่างไร NEVER หมายความว่าระบบไม่ควรทำกิจกรรมหลักให้เสร็จ เมื่อกิจกรรมทั้งหมดในคอนเทนเนอร์รองเสร็จสิ้น (ดูทำกิจกรรมให้เสร็จ)
      • setFinishSecondaryWithPrimary(): กำหนดวิธีที่การทำกิจกรรมทั้งหมดในคอนเทนเนอร์หลักให้เสร็จสมบูรณ์จะส่งผลต่อกิจกรรมในคอนเทนเนอร์รอง ALWAYS ระบุว่าระบบควรทำกิจกรรมในคอนเทนเนอร์รองให้เสร็จเสมอเมื่อกิจกรรมทั้งหมดในคอนเทนเนอร์หลักเสร็จสิ้น (ดูทำกิจกรรมให้เสร็จ)
      • setClearTop(): ระบุว่ากิจกรรมทั้งหมดใน คอนเทนเนอร์รองเสร็จสิ้นเมื่อมีการเปิดใช้กิจกรรมใหม่ใน คอนเทนเนอร์หรือไม่ ค่า false ระบุว่ากิจกรรมใหม่จะ ซ้อนอยู่ด้านบนของกิจกรรมที่อยู่ในคอนเทนเนอร์รองอยู่แล้ว
    5. รับอินสแตนซ์ Singleton ของ WindowManager RuleController และเพิ่มกฎต่อไปนี้

      Kotlin

      val ruleController = RuleController.getInstance(this)
      ruleController.addRule(splitPairRule)

      Java

      RuleController ruleController = RuleController.getInstance(this);
      ruleController.addRule(splitPairRule);

    6. สร้างตัวยึดตำแหน่งสำหรับคอนเทนเนอร์รองเมื่อ ไม่มีเนื้อหา

    7. สร้าง ActivityFilter ที่ระบุกิจกรรมซึ่ง ตัวยึดตำแหน่งใช้การแยกหน้าต่างงานร่วมกัน

      Kotlin

      val placeholderActivityFilter = ActivityFilter(
          ComponentName(this, ListActivity::class.java),
          null
      )

      Java

      ActivityFilter placeholderActivityFilter = new ActivityFilter(
          new ComponentName(this, ListActivity.class),
          null
      );

    8. เพิ่มตัวกรองลงในชุดตัวกรองโดยทำดังนี้

      Kotlin

      val placeholderActivityFilterSet = setOf(placeholderActivityFilter)

      Java

      Set<ActivityFilter> placeholderActivityFilterSet = new HashSet<>();
      placeholderActivityFilterSet.add(placeholderActivityFilter);

    9. สร้างSplitPlaceholderRule

      Kotlin

      val splitPlaceholderRule = SplitPlaceholderRule.Builder(
          placeholderActivityFilterSet,
          Intent(context, PlaceholderActivity::class.java)
      ).setDefaultSplitAttributes(splitAttributes)
          .setMinWidthDp(840)
          .setMinSmallestWidthDp(600)
          .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
          .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS)
          .setSticky(false)
          .build()

      Java

      SplitPlaceholderRule splitPlaceholderRule = new SplitPlaceholderRule.Builder(
            placeholderActivityFilterSet,
            new Intent(this, PlaceholderActivity.class)
          ).setDefaultSplitAttributes(splitAttributes)
           .setMinWidthDp(840)
           .setMinSmallestWidthDp(600)
           .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
           .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS)
           .setSticky(false)
           .build();

      SplitPlaceholderRule.Builder สร้างและกำหนดค่ากฎ

      • placeholderActivityFilterSet: มีตัวกรองกิจกรรมที่ กำหนดเวลาที่จะใช้กฎโดยการระบุกิจกรรมที่ เชื่อมโยงกับกิจกรรมตัวยึดตำแหน่ง
      • Intent: ระบุการเปิดตัวกิจกรรมตัวยึดตำแหน่ง
      • setDefaultSplitAttributes(): ใช้แอตทริบิวต์เลย์เอาต์กับกฎ
      • setMinWidthDp(): กำหนดความกว้างขั้นต่ำของจอแสดงผล (เป็นความหนาแน่นของพิกเซลอิสระ, dp) ที่อนุญาตให้แยก
      • setMinSmallestWidthDp(): กำหนดค่าต่ำสุด (เป็น dp) ที่ขนาดการแสดงผลที่เล็กกว่าใน 2 ขนาด ต้องมีเพื่อให้แยกได้โดยไม่คำนึงถึงการวางแนวของอุปกรณ์
      • setMaxAspectRatioInPortrait(): กำหนดอัตราส่วนภาพสูงสุด (ความสูง:ความกว้าง) ในการวางแนวตั้ง ซึ่งจะแสดงการแยกกิจกรรม หมายเหตุ: ค่าเริ่มต้นคือ 1.4 ซึ่งจะทําให้กิจกรรมเติมหน้าต่างงานในแนวนอนบนแท็บเล็ตส่วนใหญ่ ดูเพิ่มเติม SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT และ setMaxAspectRatioInLandscape() ค่าเริ่มต้นสําหรับแนวนอนคือ ALWAYS_ALLOW
      • setFinishPrimaryWithPlaceholder(): กำหนดวิธีที่การสิ้นสุดกิจกรรมตัวยึดตำแหน่งส่งผลต่อกิจกรรม ในคอนเทนเนอร์หลัก ALWAYS ระบุว่าระบบควรทำกิจกรรมในคอนเทนเนอร์หลักให้เสร็จเสมอเมื่อตัวยึดตำแหน่งเสร็จสิ้น (ดูทำกิจกรรมให้เสร็จ)
      • setSticky(): กำหนดว่ากิจกรรมตัวยึดตำแหน่ง จะปรากฏที่ด้านบนของสแต็กกิจกรรมในจอแสดงผลขนาดเล็กหรือไม่ เมื่อตัวยึดตำแหน่ง ปรากฏในโหมดแยกเป็นครั้งแรกโดยมี ความกว้างขั้นต่ำเพียงพอ
    10. เพิ่มกฎไปยัง WindowManager RuleController:

      Kotlin

      ruleController.addRule(splitPlaceholderRule)

      Java

      ruleController.addRule(splitPlaceholderRule);

  2. ระบุกิจกรรมที่ไม่ควรเป็นส่วนหนึ่งของการแยก

    1. สร้าง ActivityFilter ที่ระบุกิจกรรมที่ควร ครอบครองพื้นที่แสดงงานทั้งหมดเสมอ

      Kotlin

      val expandedActivityFilter = ActivityFilter(
          ComponentName(this, ExpandedActivity::class.java),
          null
      )

      Java

      ActivityFilter expandedActivityFilter = new ActivityFilter(
          new ComponentName(this, ExpandedActivity.class),
          null
      );

    2. เพิ่มตัวกรองลงในชุดตัวกรองโดยทำดังนี้

      Kotlin

      val expandedActivityFilterSet = setOf(expandedActivityFilter)

      Java

      Set<ActivityFilter> expandedActivityFilterSet = new HashSet<>();
      expandedActivityFilterSet.add(expandedActivityFilter);

    3. สร้าง ActivityRule โดยทำดังนี้

      Kotlin

      val activityRule = ActivityRule.Builder(expandedActivityFilterSet)
          .setAlwaysExpand(true)
          .build()

      Java

      ActivityRule activityRule = new ActivityRule.Builder(
          expandedActivityFilterSet
      ).setAlwaysExpand(true)
       .build();

      ActivityRule.Builder สร้างและกำหนดค่ากฎ

      • expandedActivityFilterSet: มีตัวกรองกิจกรรมที่ กำหนดเวลาที่จะใช้กฎโดยการระบุกิจกรรมที่คุณ ต้องการยกเว้นจากการแยก
      • setAlwaysExpand(): ระบุว่ากิจกรรมควรแสดงเต็มหน้าต่างงานหรือไม่
    4. เพิ่มกฎไปยัง WindowManager RuleController:

      Kotlin

      ruleController.addRule(activityRule)

      Java

      ruleController.addRule(activityRule);

การฝังข้ามแอปพลิเคชัน

ใน Android 13 (API ระดับ 33) ขึ้นไป แอปจะฝังกิจกรรมจากแอปอื่นๆ ได้ การฝังกิจกรรมข้ามแอปพลิเคชันหรือข้าม UID ช่วยให้ผสานรวมกิจกรรมจากแอปพลิเคชัน Android หลายรายการได้ ระบบจะแสดงกิจกรรมของแอปโฮสต์และกิจกรรมที่ฝังจาก แอปอื่นบนหน้าจอแบบเคียงข้างกันหรือบนและล่างเช่นเดียวกับการฝังกิจกรรม แบบแอปเดียว

ตัวอย่างเช่น แอปการตั้งค่าอาจฝังกิจกรรมตัวเลือกวอลเปเปอร์จาก แอป WallpaperPicker ดังนี้

รูปที่ 14 แอปการตั้งค่า (เมนูด้านซ้าย) ที่มีตัวเลือกวอลเปเปอร์เป็นกิจกรรมที่ฝัง (ขวา)

โมเดลความน่าเชื่อถือ

กระบวนการโฮสต์ที่ฝังกิจกรรมจากแอปอื่นๆ จะสามารถกำหนดการนำเสนอของกิจกรรมที่ฝังใหม่ได้ ซึ่งรวมถึงขนาด ตำแหน่ง การครอบตัด และความโปร่งใส โฮสต์ที่เป็นอันตรายอาจใช้ความสามารถนี้เพื่อหลอกลวงผู้ใช้และ สร้างการคลิกแจ็กกิ้งหรือการโจมตีอื่นๆ ที่แก้ไข UI

Android กำหนดให้แอปเลือกใช้เพื่ออนุญาตการฝังกิจกรรมของตนเองเพื่อป้องกันการใช้การฝังกิจกรรมข้ามแอปในทางที่ผิด แอปสามารถกำหนดให้โฮสต์เป็นเชื่อถือได้ หรือไม่เชื่อถือได้

โฮสต์ที่เชื่อถือได้

หากต้องการอนุญาตให้แอปพลิเคชันอื่นๆ ฝังและควบคุมการนำเสนอกิจกรรมจากแอปของคุณได้อย่างเต็มที่ ให้ระบุใบรับรอง SHA-256 ของแอปพลิเคชันโฮสต์ในแอตทริบิวต์ android:knownActivityEmbeddingCerts ขององค์ประกอบ <activity> หรือ <application> ในไฟล์ Manifest ของแอป

ตั้งค่า android:knownActivityEmbeddingCerts เป็นสตริง

<activity
    android:name=".MyEmbeddableActivity"
    android:knownActivityEmbeddingCerts="@string/known_host_certificate_digest"
    ... />

หรือหากต้องการระบุใบรับรองหลายรายการ ให้ใช้อาร์เรย์ของสตริง

<activity
    android:name=".MyEmbeddableActivity"
    android:knownActivityEmbeddingCerts="@array/known_host_certificate_digests"
    ... />

ซึ่งอ้างอิงถึงทรัพยากร เช่น

<resources>
    <string-array name="known_host_certificate_digests">
      <item>cert1</item>
      <item>cert2</item>
      ...
    </string-array>
</resources>

เจ้าของแอปจะรับค่าแฮชของใบรับรอง SHA ได้โดยเรียกใช้ทาสก์ Gradle signingReport ไดเจสต์ของใบรับรองคือลายนิ้วมือ SHA-256 ที่ไม่มี เครื่องหมายโคลอนคั่น ดูข้อมูลเพิ่มเติมได้ที่หัวข้อเรียกใช้รายงานการลงนามและ การตรวจสอบสิทธิ์ไคลเอ็นต์

โฮสต์ที่ไม่น่าเชื่อถือ

หากต้องการอนุญาตให้แอปใดก็ตามฝังกิจกรรมของแอปและควบคุมการนำเสนอ ให้ระบุแอตทริบิวต์ android:allowUntrustedActivityEmbedding ในองค์ประกอบ <activity> หรือ <application> ในไฟล์ Manifest ของแอป เช่น

<activity
    android:name=".MyEmbeddableActivity"
    android:allowUntrustedActivityEmbedding="true"
    ... />

ค่าเริ่มต้นของแอตทริบิวต์คือ false ซึ่งจะป้องกันการฝังกิจกรรมข้ามแอป

การตรวจสอบสิทธิ์ที่กำหนดเอง

หากต้องการลดความเสี่ยงของการฝังกิจกรรมที่ไม่น่าเชื่อถือ ให้สร้างกลไกการตรวจสอบสิทธิ์ที่กำหนดเองซึ่งจะยืนยันตัวตนของโฮสต์ หากคุณทราบใบรับรองโฮสต์ ให้ใช้ไลบรารี androidx.security.app.authenticator เพื่อตรวจสอบสิทธิ์ หากผู้จัดทำการตรวจสอบสิทธิ์หลังจากฝังกิจกรรม คุณจะ แสดงเนื้อหาจริงได้ หากไม่เป็นไปตามนโยบาย คุณสามารถแจ้งให้ผู้ใช้ทราบว่าระบบ ไม่อนุญาตให้ดำเนินการดังกล่าวและบล็อกเนื้อหาได้

ใช้เมธอด ActivityEmbeddingController#isActivityEmbedded() จากไลบรารี Jetpack WindowManager เพื่อตรวจสอบว่าโฮสต์ฝังกิจกรรมของคุณหรือไม่ เช่น

Kotlin

fun isActivityEmbedded(activity: Activity): Boolean {
    return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity)
}

Java

boolean isActivityEmbedded(Activity activity) {
    return ActivityEmbeddingController.getInstance(context).isActivityEmbedded(activity);
}

ข้อจำกัดเกี่ยวกับขนาดขั้นต่ำ

ระบบ Android จะใช้ความสูงและความกว้างขั้นต่ำที่ระบุไว้ในองค์ประกอบ <layout> ของไฟล์ Manifest ของแอปกับกิจกรรมที่ฝังไว้ หากแอปพลิเคชันไม่ได้ระบุความสูงและความกว้างขั้นต่ำ ระบบจะใช้ค่าเริ่มต้น (sw220dp)

หากโฮสต์พยายามปรับขนาดคอนเทนเนอร์ที่ฝังให้มีขนาดเล็กกว่าขนาดขั้นต่ำ คอนเทนเนอร์ที่ฝังจะขยายเพื่อครอบคลุมขอบเขตของงานทั้งหมด

<activity-alias>

หากต้องการให้การฝังกิจกรรมที่เชื่อถือได้หรือไม่น่าเชื่อถือทํางานร่วมกับองค์ประกอบ <activity-alias> คุณต้องใช้ android:knownActivityEmbeddingCerts หรือ android:allowUntrustedActivityEmbedding กับกิจกรรมเป้าหมาย แทนที่จะใช้นามแฝง นโยบายที่ยืนยันความปลอดภัยในเซิร์ฟเวอร์ของระบบจะ อิงตามค่าสถานะที่ตั้งค่าไว้ในเป้าหมาย ไม่ใช่นามแฝง

แอปพลิเคชันโฮสต์

แอปพลิเคชันโฮสต์จะใช้การฝังกิจกรรมข้ามแอปในลักษณะเดียวกับที่ใช้การฝังกิจกรรมแอปเดียว ออบเจ็กต์ SplitPairRule และ SplitPairFilter หรือ ActivityRule และ ActivityFilter ระบุกิจกรรมที่ฝังและการแยกหน้าต่างงาน คุณกำหนดกฎการแยกได้แบบคงที่ใน XML หรือที่รันไทม์โดยใช้การเรียก API ของ Jetpack WindowManager

หากแอปพลิเคชันโฮสต์พยายามฝังกิจกรรมที่ไม่ได้เลือกใช้การฝังข้ามแอป กิจกรรมจะครอบครองขอบเขตของงานทั้งหมด ด้วยเหตุนี้ แอปพลิเคชันโฮสต์จึงต้องทราบว่ากิจกรรมเป้าหมายอนุญาตการฝังข้ามแอปหรือไม่

หากกิจกรรมที่ฝังเริ่มกิจกรรมใหม่ในงานเดียวกันและกิจกรรมใหม่ไม่ได้เลือกใช้การฝังข้ามแอป กิจกรรมจะครอบครองขอบเขตของงานทั้งหมดแทนที่จะซ้อนทับกิจกรรมในคอนเทนเนอร์ที่ฝัง

แอปพลิเคชันโฮสต์สามารถฝังกิจกรรมของตัวเองได้โดยไม่มีข้อจำกัด ตราบใดที่ กิจกรรมเปิดตัวในงานเดียวกัน

ตัวอย่างการแยก

แยกจากหน้าต่างแบบเต็ม

รูปที่ 15 กิจกรรม A เริ่มกิจกรรม B ที่ด้านข้าง

ไม่ต้องทำการปรับโครงสร้างโค้ด คุณกำหนดค่าสำหรับการแยกได้แบบคงที่หรือที่รันไทม์ แล้วเรียกใช้ Context#startActivity() โดยไม่ต้องมีพารามิเตอร์เพิ่มเติม

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

แยกโดยค่าเริ่มต้น

เมื่อออกแบบหน้า Landing Page ของแอปพลิเคชันให้แบ่งออกเป็น 2 คอนเทนเนอร์ในหน้าจอขนาดใหญ่ ประสบการณ์ของผู้ใช้จะดีที่สุดเมื่อสร้างและนำเสนอกิจกรรมทั้ง 2 อย่างพร้อมกัน อย่างไรก็ตาม เนื้อหาอาจไม่พร้อมใช้งานสำหรับคอนเทนเนอร์รองของการแยกจนกว่าผู้ใช้จะโต้ตอบกับกิจกรรมในคอนเทนเนอร์หลัก (เช่น ผู้ใช้เลือกรายการจากเมนูการนำทาง) กิจกรรมตัวยึดตำแหน่งจะช่วยเติมเต็มช่องว่างจนกว่าจะแสดงเนื้อหาในคอนเทนเนอร์รองของการแยกหน้าจอได้ (ดูส่วนตัวยึดตำแหน่ง)

รูปที่ 16 การแยกหน้าจอที่สร้างขึ้นโดยการเปิด 2 กิจกรรมพร้อมกัน กิจกรรมหนึ่งเป็นตัวยึดตำแหน่ง

หากต้องการสร้างการแยกที่มีตัวยึดตำแหน่ง ให้สร้างตัวยึดตำแหน่งและเชื่อมโยงกับ กิจกรรมหลักโดยทำดังนี้

<SplitPlaceholderRule
    window:placeholderActivityName=".PlaceholderActivity">
    <ActivityFilter
        window:activityName=".MainActivity"/>
</SplitPlaceholderRule>

เมื่อแอปได้รับ Intent ระบบจะแสดงกิจกรรมเป้าหมายเป็น ส่วนรองของการแยกกิจกรรมได้ เช่น คำขอให้แสดงหน้าจอรายละเอียดที่มีข้อมูลเกี่ยวกับรายการจากรายการ ในจอแสดงผลขนาดเล็ก รายละเอียด จะแสดงในหน้าต่างงานแบบเต็ม ส่วนในอุปกรณ์ขนาดใหญ่ รายละเอียดจะแสดงข้างรายการ

รูปที่ 17 กิจกรรมรายละเอียด Deep Link จะแสดงบนหน้าจอขนาดเล็ก แต่จะแสดงพร้อมกับกิจกรรมรายการบนหน้าจอขนาดใหญ่

คำขอเปิดตัวควรส่งไปยังกิจกรรมหลัก และควรเปิดตัวกิจกรรมรายละเอียดเป้าหมาย ในโหมดแยก ระบบจะเลือกการนำเสนอที่ถูกต้องโดยอัตโนมัติ ไม่ว่าจะเป็นแบบซ้อนกันหรือแบบเคียงข้างกัน โดยอิงตามความกว้างของจอแสดงผลที่มี

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    RuleController.getInstance(this)
        .addRule(SplitPairRule.Builder(filterSet).build())
    startActivity(Intent(this, DetailActivity::class.java))
}

Java

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    RuleController.getInstance(this)
        .addRule(new SplitPairRule.Builder(filterSet).build());
    startActivity(new Intent(this, DetailActivity.class));
}

ปลายทางของ Deep Link อาจเป็นกิจกรรมเดียวที่ควรพร้อมใช้งานสำหรับ ผู้ใช้ในสแต็กการนำทางย้อนกลับ และคุณอาจต้องการหลีกเลี่ยงการปิด กิจกรรมรายละเอียดและเหลือเพียงกิจกรรมหลัก

จอแสดงผลขนาดใหญ่ที่มีกิจกรรมรายการและกิจกรรมรายละเอียดอยู่ข้างกัน
          การนำทางย้อนกลับไม่สามารถปิดกิจกรรมรายละเอียดและออกจากรายการ
          กิจกรรมบนหน้าจอ

การแสดงผลขนาดเล็กที่มีกิจกรรมแบบละเอียดเท่านั้น การนำทางย้อนกลับไม่สามารถ
          ปิดกิจกรรมรายละเอียดและแสดงกิจกรรมรายการ

แต่คุณสามารถทำกิจกรรมทั้ง 2 อย่างให้เสร็จพร้อมกันได้โดยใช้แอตทริบิวต์ finishPrimaryWithSecondary

<SplitPairRule
    window:finishPrimaryWithSecondary="always">
    <SplitPairFilter
        window:primaryActivityName=".ListActivity"
        window:secondaryActivityName=".DetailActivity"/>
</SplitPairRule>

ดูส่วนแอตทริบิวต์การกำหนดค่า

กิจกรรมหลายรายการในคอนเทนเนอร์ที่แยก

การซ้อนกิจกรรมหลายอย่างในคอนเทนเนอร์แบบแยกช่วยให้ผู้ใช้เข้าถึงเนื้อหาเชิงลึกได้ ตัวอย่างเช่น เมื่อใช้การแยกรายการ-รายละเอียด ผู้ใช้อาจต้องไปที่ส่วนรายละเอียดย่อย แต่ยังคงกิจกรรมหลักไว้

รูปที่ 18 กิจกรรมเปิดขึ้นในแผงรองของหน้าต่างงาน

Kotlin

class DetailActivity : AppCompatActivity() {
    fun onOpenSubdetail() {
        startActivity(Intent(this, SubdetailActivity::class.java))
    }
}

Java

public class DetailActivity  extends AppCompatActivity {
    void onOpenSubdetail() {
        startActivity(new Intent(this, SubdetailActivity.class));
    }
}

กิจกรรมย่อยจะอยู่เหนือกิจกรรมหลักและซ่อนกิจกรรมหลักไว้

จากนั้นผู้ใช้จะกลับไปยังระดับรายละเอียดก่อนหน้าได้โดยการย้อนกลับ ผ่านสแต็ก

รูปที่ 19 นำกิจกรรมออกจากด้านบนสุดของกองซ้อน

การซ้อนกิจกรรมทับกันเป็นลักษณะการทำงานเริ่มต้นเมื่อเปิดกิจกรรมจากกิจกรรมในคอนเทนเนอร์รองเดียวกัน กิจกรรม ที่เปิดจากคอนเทนเนอร์หลักภายในสปลิตที่ใช้งานอยู่จะไปอยู่ใน คอนเทนเนอร์รองที่ด้านบนของสแต็กกิจกรรมด้วย

กิจกรรมในงานใหม่

เมื่อกิจกรรมในหน้าต่างงานที่แยกเริ่มต้นกิจกรรมในงานใหม่ งานใหม่จะแยกจากงานที่มีการแยกและแสดงแบบเต็มหน้าต่าง หน้าจอ "ล่าสุด" จะแสดง 2 งาน ได้แก่ งานในโหมดแยกหน้าจอและงานใหม่

รูปที่ 20 เริ่มกิจกรรม C ในงานใหม่จากกิจกรรม B

การแทนที่กิจกรรม

สามารถแทนที่กิจกรรมในสแต็กคอนเทนเนอร์รองได้ เช่น เมื่อใช้กิจกรรมหลักสำหรับการนำทางระดับบนสุดและกิจกรรมรองเป็นปลายทางที่เลือก แต่ละรายการที่เลือกจากการนำทางระดับบนสุดควร เริ่มกิจกรรมใหม่ในคอนเทนเนอร์รอง และนำกิจกรรมหรือ กิจกรรมที่อยู่ในนั้นก่อนหน้านี้ออก

รูปที่ 21 กิจกรรมการนำทางระดับบนสุดในแผงหลัก จะแทนที่กิจกรรมปลายทางในแผงรอง

หากแอปทำกิจกรรมในคอนเทนเนอร์รองไม่เสร็จเมื่อการเลือกการนำทางเปลี่ยนแปลง การนำทางย้อนกลับอาจทำให้สับสนเมื่อมีการยุบการแยก (เมื่อพับอุปกรณ์) เช่น หากคุณมีเมนูใน แผงหลัก และหน้าจอ A กับ B ซ้อนกันในแผงรอง เมื่อผู้ใช้ พับโทรศัพท์ B จะอยู่เหนือ A และ A จะอยู่เหนือเมนู เมื่อผู้ใช้ ย้อนกลับจาก B หน้า A จะปรากฏแทนเมนู

ในกรณีดังกล่าว ต้องนำหน้าจอ ก. ออกจาก Back Stack

ลักษณะการทำงานเริ่มต้นเมื่อเปิดใช้ไปด้านข้างในคอนเทนเนอร์ใหม่เหนือการแยกที่มีอยู่คือการวางคอนเทนเนอร์รองใหม่ไว้ด้านบนและเก็บคอนเทนเนอร์เก่าไว้ในสแต็กย้อนกลับ คุณสามารถกำหนดค่าการแยกเพื่อล้างคอนเทนเนอร์รองก่อนหน้าด้วย clearTop และเปิดกิจกรรมใหม่ตามปกติ

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

Kotlin

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

Java

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

หรือจะใช้กิจกรรมรองเดียวกันก็ได้ และจากกิจกรรมหลัก (เมนู) ให้ส่ง Intent ใหม่ที่ไปยังอินสแตนซ์เดียวกัน แต่ทริกเกอร์การอัปเดตสถานะหรือ UI ในคอนเทนเนอร์รอง

การแยกหลายครั้ง

แอปสามารถให้การนำทางแบบหลายระดับได้โดยการเปิดใช้งานกิจกรรมเพิ่มเติม ที่ด้านข้าง

เมื่อกิจกรรมในคอนเทนเนอร์รองเปิดกิจกรรมใหม่ไปด้านข้าง ระบบจะ สร้างการแยกหน้าจอใหม่ทับการแยกหน้าจอที่มีอยู่

รูปที่ 22 กิจกรรม B เริ่มกิจกรรม C ไปด้านข้าง

Back Stack จะมีกิจกรรมทั้งหมดที่เปิดก่อนหน้านี้ เพื่อให้ผู้ใช้ไปยังการแยก A/B ได้หลังจากทำ C เสร็จแล้ว

กิจกรรม ก, ข และ ค ในสแต็ก กิจกรรมจะซ้อนกันตามลำดับต่อไปนี้จากบนลงล่าง: ค, ข, ก

หากต้องการสร้างการแยกหน้าจอใหม่ ให้เปิดกิจกรรมใหม่ไปด้านข้างจากคอนเทนเนอร์รองที่มีอยู่ ประกาศการกำหนดค่าสำหรับการแยก A/B และ B/C และเปิดใช้กิจกรรม C จาก B ตามปกติ

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

Kotlin

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

Java

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

ตอบสนองต่อการเปลี่ยนแปลงสถานะการแยก

กิจกรรมต่างๆ ในแอปอาจมีองค์ประกอบ UI ที่ทําหน้าที่เดียวกัน เช่น ตัวควบคุมที่เปิดหน้าต่างที่มีการตั้งค่าบัญชี

รูปที่ 23 กิจกรรมต่างๆ ที่มีองค์ประกอบ UI ที่ทำงานเหมือนกัน

หากกิจกรรม 2 รายการที่มีองค์ประกอบ UI ร่วมกันอยู่ในโหมดแยกหน้าจอ การแสดงองค์ประกอบในทั้ง 2 กิจกรรมจะซ้ำซ้อนและอาจทำให้เกิดความสับสน

รูปที่ 24 องค์ประกอบ UI ที่ซ้ำกันในการแยกกิจกรรม

หากต้องการทราบว่ากิจกรรมใดอยู่ในสปลิต ให้ตรวจสอบโฟลว์ SplitController.splitInfoList หรือลงทะเบียน Listener ด้วย SplitControllerCallbackAdapter เพื่อดูการเปลี่ยนแปลงสถานะสปลิต จากนั้น ปรับ UI ตามนั้น

Kotlin

val layout = layoutInflater.inflate(R.layout.activity_main, null)
val view = layout.findViewById<View>(R.id.infoButton)
lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        splitController.splitInfoList(this@SplitDeviceActivity) // The activity instance.
            .collect { list ->
                view.visibility = if (list.isEmpty()) View.VISIBLE else View.GONE
            }
    }
}

Java

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    new SplitControllerCallbackAdapter(SplitController.getInstance(this))
        .addSplitListener(
            this,
            Runnable::run,
            splitInfoList -> {
                View layout = getLayoutInflater().inflate(R.layout.activity_main, null);
                layout.findViewById(R.id.infoButton).setVisibility(
                    splitInfoList.isEmpty() ? View.VISIBLE : View.GONE);
            });
}

คุณเรียกใช้โครูทีนได้ในสถานะวงจรใดก็ได้ แต่โดยปกติแล้วจะเรียกใช้ในสถานะ STARTED เพื่อประหยัดทรัพยากร (ดูข้อมูลเพิ่มเติมได้ที่ใช้โครูทีน Kotlin กับคอมโพเนนต์ที่รับรู้ถึงวงจร)

การเรียกกลับสามารถทำได้ในทุกสถานะวงจร รวมถึงเมื่อมีการหยุดกิจกรรม โดยปกติแล้ว ผู้ฟังควรลงทะเบียนใน onStart() และยกเลิกการลงทะเบียนใน onStop()

โมดัลแบบเต็มหน้าต่าง

กิจกรรมบางอย่างจะบล็อกไม่ให้ผู้ใช้โต้ตอบกับแอปพลิเคชันจนกว่าจะมีการดำเนินการที่ ระบุ เช่น กิจกรรมหน้าจอเข้าสู่ระบบ หน้าจอรับทราบนโยบาย หรือข้อความแสดงข้อผิดพลาด ควรป้องกันไม่ให้กิจกรรมในโมดัลปรากฏในหน้าจอแยก

คุณบังคับให้กิจกรรมเติมหน้าต่างงานได้เสมอโดยใช้การกำหนดค่าขยาย

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

ทำกิจกรรมให้เสร็จ

ผู้ใช้สามารถทำกิจกรรมให้เสร็จได้ทั้ง 2 ด้านของการแยกหน้าจอโดยปัดจากขอบ ของจอแสดงผล

รูปที่ 25 ท่าทางสัมผัสด้วยการปัดเพื่อสิ้นสุดกิจกรรม B
รูปที่ 26 ท่าทางสัมผัสการปัดเพื่อสิ้นสุดกิจกรรม ก.

หากตั้งค่าอุปกรณ์ให้ใช้ปุ่มย้อนกลับแทนการนำทางด้วยท่าทางสัมผัส ระบบจะส่งข้อมูลไปยังกิจกรรมที่โฟกัส ซึ่งเป็นกิจกรรมที่แตะหรือ เปิดใช้ล่าสุด

ผลกระทบที่การทำกิจกรรมทั้งหมดในคอนเทนเนอร์มีต่อคอนเทนเนอร์ตรงข้าม จะขึ้นอยู่กับการกำหนดค่าการแยก

แอตทริบิวต์การกำหนดค่า

คุณสามารถระบุแอตทริบิวต์ของกฎคู่ที่แยกเพื่อกำหนดค่าวิธีที่การทำกิจกรรมทั้งหมดในด้านหนึ่งของการแยกส่งผลต่อกิจกรรมในอีกด้านหนึ่งของการแยก แอตทริบิวต์ดังกล่าวมีดังนี้

  • window:finishPrimaryWithSecondary — วิธีที่การทำกิจกรรมทั้งหมดในคอนเทนเนอร์รองให้เสร็จสมบูรณ์ส่งผลต่อกิจกรรมในคอนเทนเนอร์หลัก
  • window:finishSecondaryWithPrimary — วิธีที่การทำกิจกรรมทั้งหมดในคอนเทนเนอร์หลักให้เสร็จสมบูรณ์ส่งผลต่อกิจกรรมในคอนเทนเนอร์รอง

ค่าที่เป็นไปได้ของแอตทริบิวต์ ได้แก่

  • always - ทำกิจกรรมในคอนเทนเนอร์ที่เชื่อมโยงให้เสร็จเสมอ
  • never — ไม่เคยทำกิจกรรมในคอนเทนเนอร์ที่เชื่อมโยงให้เสร็จสมบูรณ์
  • adjacent — ทำกิจกรรมในคอนเทนเนอร์ที่เชื่อมโยงให้เสร็จเมื่อคอนเทนเนอร์ 2 รายการแสดงอยู่ติดกัน แต่จะไม่ทำเมื่อคอนเทนเนอร์ 2 รายการซ้อนกัน

เช่น

<SplitPairRule
    <!-- Do not finish primary container activities when all secondary container activities finish. -->
    window:finishPrimaryWithSecondary="never"
    <!-- Finish secondary container activities when all primary container activities finish. -->
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

การกำหนดค่าเริ่มต้น

เมื่อกิจกรรมทั้งหมดในคอนเทนเนอร์หนึ่งของหน้าจอแยกเสร็จสิ้น คอนเทนเนอร์ที่เหลือจะ ครอบครองทั้งหน้าต่าง

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

ส่วนที่มีกิจกรรม ก และ ข A เสร็จแล้ว B จึง
          ครอบครองทั้งหน้าต่าง

ส่วนที่มีกิจกรรม ก และ ข B เสร็จสิ้นแล้ว เหลือเพียง A ที่
          ครอบครองทั้งหน้าต่าง

ทำกิจกรรมให้เสร็จพร้อมกัน

ทำกิจกรรมในคอนเทนเนอร์หลักให้เสร็จโดยอัตโนมัติเมื่อกิจกรรมทั้งหมด ในคอนเทนเนอร์รองเสร็จสิ้น

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

ส่วนที่มีกิจกรรม ก และ ข B เสร็จสิ้น ซึ่งจะทำให้ A เสร็จสิ้นด้วยเช่นกัน
          ทำให้หน้าต่างงานว่างเปล่า

ส่วนที่มีกิจกรรม ก และ ข A เสร็จแล้ว เหลือเพียง B
          ในหน้าต่างงาน

ทำกิจกรรมในคอนเทนเนอร์รองให้เสร็จโดยอัตโนมัติเมื่อกิจกรรมทั้งหมดในคอนเทนเนอร์หลักเสร็จสิ้น

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

ส่วนที่มีกิจกรรม ก และ ข A เสร็จสิ้น ซึ่งจะทำให้ B เสร็จสิ้นด้วย และทำให้หน้าต่างงานว่างเปล่า

ส่วนที่มีกิจกรรม ก และ ข B เสร็จสิ้นแล้ว เหลือเพียง A
          ในหน้าต่างงาน

ทำกิจกรรมให้เสร็จพร้อมกันเมื่อกิจกรรมทั้งหมดในคอนเทนเนอร์หลักหรือ คอนเทนเนอร์รองเสร็จสิ้น

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

ส่วนที่มีกิจกรรม ก และ ข A เสร็จสิ้น ซึ่งจะทำให้ B เสร็จสิ้นด้วย และทำให้หน้าต่างงานว่างเปล่า

ส่วนที่มีกิจกรรม ก และ ข B เสร็จสิ้น ซึ่งจะทำให้ A เสร็จสิ้นด้วยเช่นกัน
          ทำให้หน้าต่างงานว่างเปล่า

ทำกิจกรรมหลายอย่างในคอนเทนเนอร์ให้เสร็จ

หากมีกิจกรรมหลายรายการซ้อนกันในคอนเทนเนอร์แบบแยก เมื่อสิ้นสุดกิจกรรมที่อยู่ด้านล่างของสแต็ก กิจกรรมที่อยู่ด้านบนจะไม่สิ้นสุดโดยอัตโนมัติ

เช่น หากกิจกรรม 2 รายการอยู่ในคอนเทนเนอร์รอง โดยมี C อยู่เหนือ B

สแต็กกิจกรรมรองที่มีกิจกรรม C ซ้อนอยู่เหนือ B
          จะซ้อนอยู่เหนือสแต็กกิจกรรมหลักที่มีกิจกรรม
          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="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

แยกด้วยกิจกรรม A ในคอนเทนเนอร์หลัก และกิจกรรม B และ C ใน
          คอนเทนเนอร์รอง โดย C ซ้อนอยู่เหนือ B A เสร็จสิ้นแล้ว
          รวมถึง B และ C ด้วย

และเมื่อกำหนดค่าการแยกเพื่อสิ้นสุดทั้งหลักและรองพร้อมกัน

<SplitPairRule
    window:finishPrimaryWithSecondary="always"
    window:finishSecondaryWithPrimary="always">
    <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 ก็จะเสร็จสิ้นด้วย

เปลี่ยนพร็อพเพอร์ตี้การแยกที่รันไทม์

คุณจะเปลี่ยนพร็อพเพอร์ตี้ของการแยกที่ใช้งานอยู่และมองเห็นไม่ได้ การเปลี่ยน กฎการแยกจะส่งผลต่อการเปิดตัวกิจกรรมเพิ่มเติมและคอนเทนเนอร์ใหม่ แต่จะไม่ส่งผลต่อ การแยกที่มีอยู่และใช้งานอยู่

หากต้องการเปลี่ยนพร็อพเพอร์ตี้ของการแยกที่ใช้งานอยู่ ให้ทำกิจกรรมด้านข้างหรือ กิจกรรมในการแยกให้เสร็จ แล้วเปิดใช้ด้านข้างอีกครั้งด้วยการกำหนดค่าใหม่

คุณสมบัติการแยกแบบไดนามิก

Android 15 (API ระดับ 35) ขึ้นไปที่รองรับโดย Jetpack WindowManager 1.4 ขึ้นไปมีฟีเจอร์แบบไดนามิกที่ช่วยให้กำหนดค่าการแยกการฝังกิจกรรมได้ ซึ่งรวมถึง

  • การขยายแผง: เส้นแบ่งแบบอินเทอร์แอกทีฟที่ลากได้ช่วยให้ผู้ใช้ ปรับขนาดแผงในการนำเสนอแบบแยก
  • การปักหมุดสแต็กกิจกรรม: ผู้ใช้สามารถปักหมุดเนื้อหาในคอนเทนเนอร์หนึ่งและ แยกการนำทางในคอนเทนเนอร์จากการนำทางในคอนเทนเนอร์อื่น
  • การหรี่แสงแบบเต็มหน้าจอของกล่องโต้ตอบ: เมื่อแสดงกล่องโต้ตอบ แอปจะระบุได้ ว่าจะหรี่แสงทั้งหน้าต่างงานหรือเฉพาะคอนเทนเนอร์ที่เปิด กล่องโต้ตอบ

การขยายแผง

การขยายแผงช่วยให้ผู้ใช้ปรับขนาดพื้นที่หน้าจอที่จัดสรรให้กับ กิจกรรมทั้ง 2 รายการในเลย์เอาต์แบบ 2 แผงได้

หากต้องการปรับแต่งลักษณะที่ปรากฏของตัวแบ่งหน้าต่างและตั้งค่าช่วงที่ลากได้ของตัวแบ่ง ให้ทำดังนี้

  1. สร้างอินสแตนซ์ของ DividerAttributes

  2. ปรับแต่งแอตทริบิวต์ตัวคั่น

    • color: สีของตัวคั่นบานหน้าต่างที่ลากได้

    • widthDp: ความกว้างของตัวคั่นบานหน้าต่างที่ลากได้ ตั้งค่าเป็น WIDTH_SYSTEM_DEFAULT เพื่อให้ระบบกำหนดความกว้างของตัวคั่น

    • ช่วงการลาก: เปอร์เซ็นต์ขั้นต่ำของหน้าจอที่บานหน้าต่างใดบานหน้าต่างหนึ่งสามารถ ใช้ได้ อยู่ในช่วง 0.33 ถึง 0.66 ตั้งค่าเป็น DRAG_RANGE_SYSTEM_DEFAULT เพื่อให้ระบบกำหนดช่วงการลาก

    Kotlin

    val splitAttributesBuilder: SplitAttributes.Builder = SplitAttributes.Builder()
        .setSplitType(SplitAttributes.SplitType.ratio(0.33f))
        .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
    
    if (WindowSdkExtensions.getInstance().extensionVersion >= 6) {
        splitAttributesBuilder.setDividerAttributes(
            DividerAttributes.DraggableDividerAttributes.Builder()
                .setColor(getColor(R.color.divider_color))
                .setWidthDp(4)
                .setDragRange(DividerAttributes.DragRange.DRAG_RANGE_SYSTEM_DEFAULT)
                .build()
        )
    }
    val splitAttributes: SplitAttributes = splitAttributesBuilder.build()

    Java

    SplitAttributes.Builder splitAttributesBuilder = new SplitAttributes.Builder()
        .setSplitType(SplitAttributes.SplitType.ratio(0.33f))
        .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT);
    
    if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 6) {
        splitAttributesBuilder.setDividerAttributes(
          new DividerAttributes.DraggableDividerAttributes.Builder()
            .setColor(ContextCompat.getColor(this, R.color.divider_color))
            .setWidthDp(4)
            .setDragRange(DividerAttributes.DragRange.DRAG_RANGE_SYSTEM_DEFAULT)
            .build()
        );
    }
    SplitAttributes _splitAttributes = splitAttributesBuilder.build();

การปักหมุดสแต็กกิจกรรม

การปักหมุดสแต็กกิจกรรมช่วยให้ผู้ใช้ปักหมุดหน้าต่างที่แยกไว้หน้าต่างหนึ่งได้ เพื่อให้กิจกรรมยังคงอยู่เช่นเดิมขณะที่ผู้ใช้ไปยังส่วนต่างๆ ในหน้าต่างอื่น การปักหมุดสแต็กกิจกรรม ช่วยให้ประสบการณ์การทำงานแบบมัลติทาสก์ดียิ่งขึ้น

หากต้องการเปิดใช้การปักหมุดสแต็กกิจกรรมในแอป ให้ทำดังนี้

  1. เพิ่มปุ่มลงในไฟล์เลย์เอาต์ของกิจกรรมที่ต้องการปักหมุด เช่น กิจกรรมรายละเอียดของเลย์เอาต์รายการ-รายละเอียด

    <androidx.constraintlayout.widget.ConstraintLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/detailActivity"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="@color/white"
     tools:context=".DetailActivity">
    
    <TextView
       android:id="@+id/textViewItemDetail"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:textSize="36sp"
       android:textColor="@color/obsidian"
       app:layout_constraintBottom_toTopOf="@id/pinButton"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toTopOf="parent" />
    
    <androidx.appcompat.widget.AppCompatButton
       android:id="@+id/pinButton"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/pin_this_activity"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toBottomOf="@id/textViewItemDetail"/>
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    
  2. ในonCreate()เมธอดของกิจกรรม ให้ตั้งค่าเครื่องมือฟัง onclick บนปุ่ม

    Kotlin

    val pinButton: Button = findViewById(R.id.pinButton)
    pinButton.setOnClickListener {
        val splitAttributes: SplitAttributes = SplitAttributes.Builder()
            .setSplitType(SplitAttributes.SplitType.ratio(0.66f))
            .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
            .build()
    
        val pinSplitRule = SplitPinRule.Builder()
            .setSticky(true)
            .setDefaultSplitAttributes(splitAttributes)
            .build()
    
        SplitController.getInstance(applicationContext)
            .pinTopActivityStack(taskId, pinSplitRule)
    }

    Java

    Button pinButton = findViewById(R.id.pinButton);
    pinButton.setOnClickListener( (view) -> {
        SplitAttributes splitAttributes = new SplitAttributes.Builder()
            .setSplitType(SplitAttributes.SplitType.ratio(0.66f))
            .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
            .build();
    
        SplitPinRule pinSplitRule = new SplitPinRule.Builder()
            .setSticky(true)
            .setDefaultSplitAttributes(splitAttributes)
            .build();
    
        SplitController.getInstance(getApplicationContext())
            .pinTopActivityStack(getTaskId(), pinSplitRule);
    });

กล่องโต้ตอบแบบเต็มหน้าจอ

โดยปกติแล้ว กิจกรรมจะหรี่จอแสดงผลเพื่อดึงดูดความสนใจไปยังกล่องโต้ตอบ ใน การฝังกิจกรรม บานหน้าต่างทั้ง 2 ของจอแสดงผลแบบ 2 บานหน้าต่างควรหรี่แสง ไม่ใช่ เฉพาะบานหน้าต่างที่มีกิจกรรมที่เปิดกล่องโต้ตอบ เพื่อให้ได้ประสบการณ์ UI ที่เป็นหนึ่งเดียว

เมื่อใช้ WindowManager 1.4 ขึ้นไป หน้าต่างแอปทั้งหมดจะหรี่ลงโดยค่าเริ่มต้นเมื่อ เปิดกล่องโต้ตอบ (ดู EmbeddingConfiguration.DimAreaBehavior.ON_TASK)

หากต้องการหรี่เฉพาะคอนเทนเนอร์ของกิจกรรมที่เปิดกล่องโต้ตอบ ให้ใช้ EmbeddingConfiguration.DimAreaBehavior.ON_ACTIVITY_STACK

แยกกิจกรรมจากหน้าต่างที่แยกเป็นหน้าต่างเต็ม

สร้างการกำหนดค่าใหม่ที่แสดงกิจกรรมด้านข้างแบบเต็มหน้าต่าง แล้ว เปิดกิจกรรมอีกครั้งด้วย Intent ที่จะแสดงผลเป็นอินสแตนซ์เดียวกัน

ตรวจสอบการรองรับการแยกที่รันไทม์

การฝังกิจกรรมใช้ได้ใน Android 12L (API ระดับ 32) ขึ้นไป แต่ก็ใช้ได้ในอุปกรณ์บางรุ่นที่ใช้แพลตฟอร์มเวอร์ชันก่อนหน้าด้วย หากต้องการตรวจสอบความพร้อมใช้งานของฟีเจอร์ขณะรันไทม์ ให้ใช้พร็อพเพอร์ตี้ SplitController.splitSupportStatus หรือเมธอด SplitController.getSplitSupportStatus()

Kotlin

if (SplitController.getInstance(this).splitSupportStatus ==
    SplitController.SplitSupportStatus.SPLIT_AVAILABLE
) {
    // Device supports split activity features.
}

Java

if (SplitController.getInstance(this).getSplitSupportStatus() ==
    SplitController.SplitSupportStatus.SPLIT_AVAILABLE) {
    // Device supports split activity features.
}

หากไม่รองรับการแยกหน้าจอ ระบบจะเปิดใช้กิจกรรมที่ด้านบนของสแต็กกิจกรรม (ตามรูปแบบการฝังที่ไม่ใช่กิจกรรม)

ป้องกันการลบล้างระบบ

ผู้ผลิตอุปกรณ์ Android (ผู้ผลิตอุปกรณ์ดั้งเดิมหรือ OEM) สามารถฝังกิจกรรมเป็นฟังก์ชันของระบบอุปกรณ์ได้ ระบบจะระบุกฎการแยกสำหรับแอปแบบหลายกิจกรรม ซึ่งจะลบล้างลักษณะการทำงานของ การจัดหน้าต่างของแอป การลบล้างของระบบจะบังคับให้แอปแบบหลายกิจกรรมเข้าสู่โหมดการฝังกิจกรรมที่ระบบกำหนด

การฝังกิจกรรมของระบบช่วยปรับปรุงการนำเสนอแอปผ่านเลย์เอาต์แบบหลายบานหน้าต่าง เช่น รายการ-รายละเอียด โดยไม่ต้องเปลี่ยนแปลงแอป อย่างไรก็ตาม การฝังกิจกรรมของระบบ อาจทำให้เลย์เอาต์ของแอปไม่ถูกต้อง มีข้อบกพร่อง หรือ ขัดแย้งกับการฝังกิจกรรมที่แอปใช้

แอปของคุณสามารถป้องกันหรืออนุญาตการฝังกิจกรรมของระบบได้โดยการตั้งค่า PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE ในไฟล์ Manifest ของแอป เช่น

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application>
        <property
            android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE"
            android:value="true|false" />
    </application>
</manifest>

ชื่อพร็อพเพอร์ตี้กำหนดไว้ในออบเจ็กต์ WindowProperties ของ Jetpack WindowManager ตั้งค่าเป็น false หากแอปของคุณใช้การฝังกิจกรรม หรือหากคุณต้องการป้องกันไม่ให้ระบบใช้กฎการฝังกิจกรรมกับแอปของคุณ ตั้งค่าเป็น true เพื่ออนุญาตให้ระบบใช้การฝังกิจกรรมที่ระบบกำหนดกับแอปของคุณ

ข้อจำกัดและคำเตือน

  • เฉพาะแอปโฮสต์ของงานเท่านั้น ซึ่งระบุว่าเป็นเจ้าของกิจกรรมรูท ในงาน จึงจะจัดระเบียบและฝังกิจกรรมอื่นๆ ในงานได้ หากกิจกรรมที่รองรับการฝังและการแยกทำงานในงานที่เป็นของแอปพลิเคชันอื่น การฝังและการแยกจะไม่ทำงานสำหรับกิจกรรมเหล่านั้น
  • คุณจัดระเบียบกิจกรรมได้ภายในงานเดียวเท่านั้น การเปิดกิจกรรม ในงานใหม่จะทำให้กิจกรรมนั้นอยู่ในหน้าต่างใหม่ที่ขยายออกนอกการแยกหน้าจอที่มีอยู่เสมอ
  • จัดระเบียบและวางกิจกรรมในโหมดแยกได้เฉพาะกิจกรรมในกระบวนการเดียวกัน SplitInfo จะรายงานเฉพาะกิจกรรมที่อยู่ใน กระบวนการเดียวกันเท่านั้น เนื่องจากไม่มีวิธีทราบกิจกรรมใน กระบวนการอื่น
  • กฎกิจกรรมแต่ละคู่หรือกฎกิจกรรมเดียวจะมีผลกับการเปิดตัวกิจกรรมที่เกิดขึ้นหลังจากลงทะเบียนกฎแล้วเท่านั้น ขณะนี้ยังไม่มีวิธี อัปเดตการแยกเพลงที่มีอยู่หรือพร็อพเพอร์ตี้ภาพของเพลง
  • การกำหนดค่าตัวกรองคู่ที่แยกต้องตรงกับ Intent ที่ใช้เมื่อ เปิดกิจกรรมจนเสร็จสมบูรณ์ การจับคู่จะเกิดขึ้นเมื่อมีการเริ่มกิจกรรมใหม่จากกระบวนการของแอปพลิเคชัน ดังนั้นจึงอาจไม่ทราบชื่อคอมโพเนนต์ที่ได้รับการแก้ไขในภายหลังในกระบวนการของระบบเมื่อใช้ Intent โดยนัย หากไม่ทราบชื่อคอมโพเนนต์ในเวลาที่เปิดตัว คุณสามารถใช้ อักขระไวด์การ์ดแทนได้ ("*/*") และทำการกรองตาม การดำเนินการของ Intent ได้
  • ปัจจุบันยังไม่มีวิธีในการย้ายกิจกรรมระหว่างคอนเทนเนอร์หรือเข้าและออกจากสปลิตหลังจากที่สร้างแล้ว ไลบรารี WindowManager จะสร้างการแยกเมื่อมีการเปิดใช้งานกิจกรรมใหม่ที่มีกฎที่ตรงกันเท่านั้น และจะทำลายการแยกเมื่อกิจกรรมสุดท้ายในคอนเทนเนอร์การแยกเสร็จสิ้น
  • กิจกรรมจะเปิดตัวอีกครั้งได้เมื่อมีการเปลี่ยนแปลงการกำหนดค่า ดังนั้นเมื่อมีการสร้างหรือนำการแยกออก และขอบเขตของกิจกรรมเปลี่ยนแปลง กิจกรรมจะถูกทำลายอินสแตนซ์ก่อนหน้าอย่างสมบูรณ์และสร้างอินสแตนซ์ใหม่ ด้วยเหตุนี้ นักพัฒนาแอปจึงควรระมัดระวังเรื่องต่างๆ เช่น การเปิดตัวกิจกรรมใหม่จากโค้ดเรียกกลับของวงจร
  • อุปกรณ์ต้องมีอินเทอร์เฟซส่วนขยายหน้าต่างเพื่อรองรับการฝังกิจกรรม อุปกรณ์หน้าจอขนาดใหญ่เกือบทั้งหมดที่ใช้ Android 12L (API ระดับ 32) ขึ้นไปจะมีอินเทอร์เฟซนี้ อย่างไรก็ตาม อุปกรณ์หน้าจอขนาดใหญ่บางรุ่นที่ ไม่สามารถเรียกใช้กิจกรรมหลายอย่างพร้อมกันได้จะไม่มีอินเทอร์เฟซส่วนขยายหน้าต่าง หากอุปกรณ์หน้าจอขนาดใหญ่ไม่รองรับโหมดหลายหน้าต่าง ก็อาจไม่รองรับการฝังกิจกรรม

แหล่งข้อมูลเพิ่มเติม