הטמעת פעילויות

הטמעת פעילויות מאפשרת לכם לשפר את הביצועים של אפליקציות במכשירים עם מסך גדול על ידי חלוקת חלון המשימות של האפליקציה בין שתי פעילויות או בין שני מופעים של אותה פעילות.

איור 1. אפליקציית ההגדרות עם פעילויות לצד אחת.

אם האפליקציה שלכם מורכבת מכמה פעילויות, הטמעת הפעילויות מאפשרת לכם לספק חוויית משתמש משופרת בטאבלטים, במכשירים מתקפלים ובמכשירי ChromeOS.

אין צורך לבצע רפאקציה של הקוד כדי להטמיע פעילויות. אתם קובעים איך האפליקציה תציג את הפעילויות שלה – זה לצד זה או מוערמות – על ידי יצירת קובץ תצורה מסוג XML או על ידי שליחת קריאות ל-API של Jetpack WindowManager.

התמיכה במסכים קטנים מתבצעת באופן אוטומטי. כשהאפליקציה מותקנת במכשיר עם מסך קטן, הפעילויות נערמות אחת על גבי השנייה. במסכים גדולים, הפעילויות מוצגות זו לצד זו. המערכת קובעת את הצגת הנתונים על סמך ההגדרה שיצרתם – לא נדרשת לוגיקה של הסתעפות.

הטמעת הפעילות מתאימה לשינויים בכיוון המכשיר ופועלת בצורה חלקה במכשירים מתקפלים, כך שאפשר לערום פעילויות ולפרוס אותן כשהמכשיר מתקפל ונפתח.

אפשר להטמיע פעילויות ברוב המכשירים עם מסך גדול שפועלים עם Android 12L‏ (רמת API 32) ואילך.

פיצול חלון המשימה

הטמעת הפעילות מפצלת את חלון המשימות של האפליקציה לשני קונטיינרים: ראשי ומשני. הקונטיינרים מכילים פעילויות שהופעלו מהפעילות הראשית או מפעילויות אחרות שכבר נמצאות בקונטיינרים.

הפעילויות נערמות בקונטיינר המשני כשהן מופעלות, והקונטיינר המשני נערם מעל הקונטיינר הראשי במסכים קטנים, כך שהערמה של פעילויות וניווט חזרה תואמים לסדר הפעילויות שכבר מובנות באפליקציה.

הטמעת פעילויות מאפשרת לכם להציג פעילויות במגוון דרכים. האפליקציה יכולה לפצל את חלון המשימות על ידי הפעלה של שתי פעילויות בו-זמנית:

איור 2. שתי פעילויות זו לצד זו.

לחלופין, אפשר ליצור חלוקה של פעילות שממלאת את כל חלון המשימות על ידי הפעלת פעילות חדשה לצד:

איור 3. פעילות א' מתחילה את פעילות ב' בצד.

פעילויות שכבר מחולקות ומשתתפות בחלון משימות משותף יכולות להפעיל פעילויות אחרות בדרכים הבאות:

  • בצד, מעל פעילות אחרת:

    איור 4. פעילות א' מתחילה את פעילות ג' בצד של פעילות ב'.
  • לצד, ולהזיז את החלוקה לצדדים כדי להסתיר את הפעילות הראשית הקודמת:

    איור 5. פעילות ב' מתחילה את פעילות ג' בצד ומזיזה את החלוקה לצד.
  • להפעיל פעילות במקום העליון, כלומר באותה רשימת פעילויות (activity stack):

    איור 6. פעילות ב' מפעילה את פעילות ג' ללא דגלים נוספים של כוונה.
  • הפעלת חלון מלא של פעילות באותה משימה:

    איור 7. פעילות א' או פעילות ב' מתחילות את פעילות ג', שממלאת את חלון המשימות.

ניווט חזרה

לסוגים שונים של אפליקציות יכולים להיות כללי ניווט אחורה שונים במצב של חלון משימות מפוצל, בהתאם ליחסי התלות בין הפעילויות או לאופן שבו המשתמשים מפעילים את אירוע החזרה אחורה. לדוגמה:

  • פעילות משותפת: אם שתי פעילויות קשורות זו לזו ואחת לא אמורה להופיע בלי השנייה, אפשר להגדיר את הניווט לאחור כך שיסיים את שתיהן.
  • פעילות עצמאית: אם הפעילויות עצמאיות לחלוטין, ניווט חזרה בפעילות לא משפיע על המצב של פעילות אחרת בחלון המשימות.

האירוע back נשלח לפעילות האחרונה שבה המשתמש התמקד כשמשתמשים בניווט באמצעות לחצנים.

לניווט מבוסס-תנועות:

  • Android 14 (רמת API ‏34) וגרסאות ישנות יותר – אירוע החזרה לאחור נשלח לפעילות שבה התרחשה התנועה. כשמשתמשים מחליקים מצד ימין של המסך, אירוע החזרה לאחור נשלח לפעילות בחלונית הימנית של החלון המפוצל. כשמשתמשים מחליקים מהצד הימני של המסך, אירוע החזרה נשלח לפעילות שבחלונית השמאלית.

  • Android מגרסה 15 (רמת API ‏35) ואילך

    • כשיש כמה פעילויות מאותה אפליקציה, התנועה מסתיימת בפעילות העליונה ללא קשר לכיוון ההחלקה, וכך מספקת חוויה אחידה יותר.

    • בתרחישים שכוללים שתי פעילויות מאפליקציות שונות (שכבת-על), אירוע החזרה לאחור מופנה לפעילות האחרונה שבה התמקדו, בהתאם להתנהגות של ניווט באמצעות לחצנים.

פריסת חלוניות מרובות

Jetpack WindowManager מאפשר ליצור פעילות שמוטמעת בתצוגה עם כמה חלונות במכשירים עם מסך גדול עם Android 12L‏ (API ברמה 32) ואילך, ובמכשירים מסוימים עם גרסאות קודמות של הפלטפורמה. אפליקציות קיימות שמבוססות על כמה פעילויות במקום על קטעים או על פריסות שמבוססות על תצוגות, כמו SlidingPaneLayout, יכולות לספק חוויית משתמש משופרת במסך גדול בלי לבצע רפאקציה של קוד המקור.

דוגמה נפוצה היא פיצול של רשימה לפרטים. כדי להבטיח איכות גבוהה של הצגה, המערכת מפעילה את הפעילות של הרשימה, ואז האפליקציה מפעילה מיד את הפעילות של הפרטים. מערכת המעבר ממתינה עד ששתי הפעילויות יוצגו, ואז מציגה אותן יחד. מבחינת המשתמש, שתי הפעילויות מופעלות כפעילות אחת.

איור 8. שתי פעילויות שהתחילו בו-זמנית בפריסה עם כמה חלונות.

פיצול מאפיינים

אתם יכולים לציין את היחס בין החלונות של המשימות לבין הקונטיינרים המפוצלים, ואת אופן הפריסה של הקונטיינרים ביחס זה לזה.

לכללים שמוגדרים בקובץ תצורה מסוג XML, מגדירים את המאפיינים הבאים:

  • splitRatio: הגדרת הפרופורציות של הקונטיינר. הערך הוא מספר נקודה צפה (floating-point) בטווח הפתוח (0.0, 1.0).
  • splitLayoutDirection: מציין את אופן הפריסה של הקונטיינרים המפוצלים ביחס זה לזה. הערכים כוללים:
    • ltr: משמאל לימין
    • rtl: מימין לשמאל
    • locale: הערך ltr או rtl נקבע לפי הגדרת השפה והאזור

דוגמאות מפורטות מופיעות בקטע הגדרת XML.

לכללים שנוצרו באמצעות ממשקי ה-API של WindowManager, יוצרים אובייקט SplitAttributes עם SplitAttributes.Builder ומפעילים את שיטות ה-builder הבאות:

  • setSplitType(): הגדרת הפרופורציות של הקונטיינרים המפוצלים. אפשר לעיין במאמר SplitAttributes.SplitType כדי לראות אילו ארגומנטים חוקיים, כולל השיטה SplitAttributes.SplitType.ratio().
  • setLayoutDirection(): הגדרת הפריסה של הקונטיינרים. הערכים האפשריים מפורטים במאמר SplitAttributes.LayoutDirection.

דוגמאות מפורטות בקטע WindowManager API.

איור 9. שתי חלוקות של פעילות שממוקמות מימין לשמאל, אבל עם יחסי חלוקה שונים.

placeholders

פעילויות placeholder הן פעילויות משניות ריקות שממלאות אזור של חלוקת פעילות. בסופו של דבר, הן אמורות להחליף בפעילות אחרת שמכילה תוכן. לדוגמה, פעילות placeholder יכולה לתפוס את הצד המשני של פעילות שמחולקת בתצוגת רשימה עם פרטים, עד שבוחרים פריט מהרשימה. בשלב הזה, פעילות שמכילה את פרטי הפריט שנבחר ברשימה מחליפה את ה-placeholder.

כברירת מחדל, המערכת מציגה placeholders רק כשיש מספיק מקום לחלוקת פעילות. ה-placeholders מסתיימים באופן אוטומטי כשגודל התצוגה משתנה לרוחב או לגובה קטנים מדי כדי להציג חלוקה. אם יש מספיק מקום, המערכת מפעילה מחדש את ה-placeholder במצב שמוגדר מחדש.

איור 10. מכשיר מתקפל שנפתח ונסגר. הפעילות של placeholder מסתיימת ונוצרת מחדש כשגודל התצוגה משתנה.

עם זאת, המאפיין stickyPlaceholder של שיטת SplitPlaceholderRule או setSticky() של SplitPlaceholder.Builder יכול לבטל את התנהגות ברירת המחדל. כשהערך של המאפיין או השיטה הוא true, המערכת מציגה את placeholder כפעילות העליונה בחלון המשימות כשהגודל של המסך משתנה מחלון עם שתי חלוניות לחלון עם חלונית אחת (לדוגמה, ראו הגדרת פיצול).

איור 11. מכשיר מתקפל שנפתח ונסגר. הפעילות של placeholder היא דביקה.

שינויים בגודל החלון

כששינויים בהגדרות המכשיר מצמצמים את רוחב חלון המשימות כך שהוא לא גדול מספיק לפריסת חלונות מרובים (לדוגמה, כשמכשיר מתקפל עם מסך גדול מתקפל מטאבלט לגודל טלפון או כשמשנים את הגודל של חלון האפליקציה במצב חלונות מרובים), הפעילויות שאינן placeholders בחלונית המשנית של חלון המשימות נערמות מעל הפעילויות בחלונית הראשית.

פעילויות של placeholders מוצגות רק כשיש מספיק רוחב תצוגה לחלוקה. במסכים קטנים יותר, Placeholder נסגר באופן אוטומטי. כשאזור התצוגה שוב יהיה גדול מספיק, ה-placeholder ייווצר מחדש. (מידע נוסף זמין בקטע Placeholders).

אפשר להצמיד פעילויות כי WindowManager מסדר את הפעילויות בחלונית המשנית לפי סדר 'z' מעל הפעילויות בחלונית הראשית.

כמה פעילויות בחלונית המשנית

פעילות ב' מפעילה את פעילות ג' במקום ללא דגלים נוספים של כוונת השימוש:

חלוקת פעילות שמכילה את הפעילויות א', ב' ו-ג', כאשר הפעילות ג' מוערמת על גבי הפעילות ב'.

וכתוצאה מכך, סדר הפעילויות באותה משימה יהיה:

רשימת פעילויות משנית שמכילה את הפעילות C שממובצת מעל הפעילות B.
          הסטאק המשני נמצא מעל הסטאק הראשי של הפעילויות, שמכיל את הפעילות א'.

לכן, בחלון משימות קטן יותר, האפליקציה מצטמצמת לפעילות אחת עם C בחלק העליון של הסטאק:

חלון קטן שבו מוצגת רק הפעילות ג'.

ניווט חזרה בחלון הקטן יותר מאפשר לנווט בין הפעילויות שממוזערות זו על גבי זו.

אם תגדירו מחדש את חלון המשימות לגודל גדול יותר שיכול להכיל כמה חלוניות, הפעילויות יוצגו שוב זו לצד זו.

פלחים מוערמים

פעילות ב' מתחילה את פעילות ג' בצד ומזיזה את החלוקה לצד:

חלון משימות שבו מוצגות הפעילויות א' ו-ב', ואז הפעילויות ב' ו-ג'.

התוצאה היא סדר z הבא של הפעילויות באותה משימה:

פעילויות א', ב' ו-ג' ב-stack אחד. הפעילויות נערמות לפי הסדר הבא, מלמעלה למטה: C,‏ B,‏ A.

בחלון משימות קטן יותר, האפליקציה מצטמצמת לפעילות אחת עם C בחלק העליון:

חלון קטן שבו מוצגת רק הפעילות ג'.

כיוון לאורך קבוע

הגדרת המניפסט android:screenOrientation מאפשרת לאפליקציות להגביל את הפעילויות לכיוון לאורך או לרוחב. כדי לשפר את חוויית השימוש במכשירים עם מסך גדול, כמו טאבלטים ומכשירים מתקפלים, יצרני המכשירים (OEM) יכולים להתעלם מהבקשות לגבי כיוון המסך ולהציג את האפליקציה בפורמט letterbox בכיוון לאורך במסכים בכיוון לרוחב, או בכיוון לרוחב במסכים בכיוון לאורך.

איור 12. פעילויות בפורמט letterbox: פורמט לאורך קבוע במכשיר בפריסה לרוחב (שמאל), פורמט לרוחב קבוע במכשיר בפריסה לאורך (ימין).

באופן דומה, כשהתכונה 'הטמעת פעילות' מופעלת, יצרני ציוד מקורי יכולים להתאים אישית מכשירים כך שיציגו פעילויות בפורמט letterbox לאורך בפריסה לרוחב במסכים גדולים (רוחב של 600dp ומעלה). כשפעילות בפורמט ניצב קבוע מפעילה פעילות שנייה, המכשיר יכול להציג את שתי הפעילויות זו לצד זו במסך עם שני חלונות.

איור 13. פעילות א' בפורמט לאורך קבוע מתחילה את פעילות ב' בצד.

תמיד מוסיפים את המאפיין android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED לקובץ המניפסט של האפליקציה כדי להודיע למכשירים שהאפליקציה תומכת בהטמעת פעילויות (ראו הקטע הגדרת חלוקה). לאחר מכן, במכשירים בהתאמה אישית של יצרן ציוד מקורי אפשר יהיה לקבוע אם להציג בפורמט letterbox פעילויות בפורמט לאורך קבוע.

הגדרת פיצול

כללי פיצול מגדירים את הפיצולים של הפעילות. מגדירים את כללי הפיצול בקובץ תצורה של XML או באמצעות קריאות ל-API של WindowManager ב-Jetpack.

בכל מקרה, האפליקציה צריכה לגשת לספרייה WindowManager ולהודיע למערכת שהאפליקציה הטמיעה הטמעת פעילות.

מבצעים את הפעולות הבאות:

  1. מוסיפים את תלות הספרייה העדכנית ביותר של WindowManager לקובץ build.gradle ברמת המודול של האפליקציה, לדוגמה:

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

    ספריית WindowManager מספקת את כל הרכיבים הנדרשים להטמעת פעילות.

  2. מודיעים למערכת שהטמעתם באפליקציה הטמעת פעילות.

    מוסיפים את המאפיין android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED לאלמנט <application> בקובץ המניפסט של האפליקציה ומגדירים את הערך כ-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>
    

    במהדורה 1.1.0-alpha06 ואילך של WindowManager, חלוקות של הטמעת פעילויות מושבתות, אלא אם המאפיין נוסף למניפסט והוגדר כ-true.

    בנוסף, יצרני המכשירים משתמשים בהגדרה הזו כדי להפעיל יכולות בהתאמה אישית לאפליקציות שתומכות בהטמעת פעילות. לדוגמה, במכשירים עם מסך אופקי, פעילות בפורמט לאורך בלבד מוצגת בפורמט letterbox כדי להתאים את הפעילות למעבר לפריסה של שני חלונות כשפעילות שנייה מתחילה (ראו כיוון קבוע לרוחב).

הגדרות XML

כדי ליצור הטמעה של הטמעת פעילויות שמבוססת על XML:

  1. יוצרים קובץ משאב XML שמבצע את הפעולות הבאות:

    • הגדרת פעילויות שיש להן חלוקה משותפת
    • הגדרת אפשרויות הפיצול
    • יוצר placeholder למאגר המשני של הפיצול כשהתוכן לא זמין
    • מציין פעילויות שאף פעם לא צריכות להיות חלק מחלוק

    לדוגמה:

    <!-- 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. מוסיפים את יחסי התלות העדכניים ביותר של ספריית Jetpack Startup לקובץ build.gradle ברמת המודול, לדוגמה:

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

    2. יוצרים מחלקה שמטמיעה את הממשק Initializer.

      ה-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 לקובץ המניפסט של האפליקציה בתור <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

      final 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) שצריך להיות למאפיין הקטן מבין שני מאפייני התצוגה כדי לאפשר פיצול, ללא קשר לכיוון המכשיר.
      • setMaxAspectRatioInPortrait(): הגדרת יחס הגובה-רוחב המקסימלי של התצוגה (גובה:רוחב) בכיוון לאורך שבו מוצגים חלוקות הפעילות. אם יחס הגובה-רוחב של תצוגה בפורמט לאורך חורג מיחס הגובה-רוחב המקסימלי, החלוקות מושבתות ללא קשר לרוחב התצוגה. הערה: ערך ברירת המחדל הוא 1.4, וכתוצאה מכך הפעילויות תופסות את כל חלון המשימות ברוב הטאבלטים כשהמסך בכיוון לאורך. למידע נוסף, ראו גם SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT ו-setMaxAspectRatioInLandscape(). ערך ברירת המחדל לפורמט 'לאורך' הוא ALWAYS_ALLOW.
      • setFinishPrimaryWithSecondary(): מגדיר את ההשפעה של סיום כל הפעילויות במאגר המשני על הפעילויות במאגר הראשי. הערך NEVER מציין שהמערכת לא צריכה לסיים את הפעילויות הראשיות כשכל הפעילויות בקונטיינר המשני מסתיימות (ראו סיום פעילויות).
      • setFinishSecondaryWithPrimary(): ההגדרה קובעת איך סיום כל הפעילויות בקונטיינר הראשי ישפיע על הפעילויות בקונטיינר המשני. הערך ALWAYS מציין שהמערכת תמיד צריכה לסיים את הפעילויות בקונטיינר המשני כשכל הפעילויות בקונטיינר הראשי מסתיימות (ראו סיום פעילויות).
      • setClearTop(): קובע אם כל הפעילויות בקונטיינר המשני מסתיימות כשפעילות חדשה מופעלת בקונטיינר. הערך false מציין שהפעילויות החדשות נערמות מעל הפעילויות שכבר נמצאות במיכל המשני.
    5. מקבלים את מופע היחיד של WindowManager‏ RuleController ומוסיפים את הכלל:

      Kotlin

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

      Java

        RuleController ruleController = RuleController.getInstance(this);
        ruleController.addRule(splitPairRule);
        
  2. יוצרים placeholder לקונטיינר המשני כשהתוכן לא זמין:

    1. יוצרים את השדה ActivityFilter שמזהה את הפעילות שאיתה המקום הזמין לשמירת תוכן משתף את החלוקה של חלון המשימות:

      Kotlin

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

      Java

      ActivityFilter placeholderActivityFilter = new ActivityFilter(
          new ComponentName(this, ListActivity.class),
          null
      );
    2. מוסיפים את המסנן לקבוצת מסננים:

      Kotlin

      val placeholderActivityFilterSet = setOf(placeholderActivityFilter)

      Java

      Set<ActivityFilter> placeholderActivityFilterSet = new HashSet<>();
      placeholderActivityFilterSet.add(placeholderActivityFilter);
    3. יוצרים 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(context, PlaceholderActivity.class)
          ).setDefaultSplitAttributes(splitAttributes)
           .setMinWidthDp(840)
           .setMinSmallestWidthDp(600)
           .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
           .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS)
           .setSticky(false)
           .build();

      SplitPlaceholderRule.Builder יוצרת את הכלל ומגדירה אותו:

      • placeholderActivityFilterSet: מכיל מסנני פעילות שמאפשרים לקבוע מתי להחיל את הכלל על ידי זיהוי הפעילויות שפעילות placeholder משויכת אליהן.
      • Intent: מציין את ההפעלה של פעילות placeholder.
      • setDefaultSplitAttributes(): החלת מאפייני פריסה על הכלל.
      • setMinWidthDp(): הגדרת רוחב התצוגה המינימלי (בפיקסלים שלא תלויים בדחיסות, dp) שמאפשר פיצול.
      • setMinSmallestWidthDp(): הגדרת הערך המינימלי (ב-dp) שצריך להיות למאפיין הקטן מבין שני מאפייני התצוגה כדי לאפשר פיצול, ללא קשר לכיוון המכשיר.
      • setMaxAspectRatioInPortrait(): הגדרת היחס הגובה-רוחב המקסימלי של התצוגה (גובה:רוחב) בכיוון לאורך שבו מוצגים חלוקות הפעילות. הערה: ערך ברירת המחדל הוא 1.4, וכתוצאה מכך הפעילויות ממלאות את חלון המשימות ברוב הטאבלטים בכיוון לאורך. מידע נוסף זמין במאמרים SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT ו-setMaxAspectRatioInLandscape(). ערך ברירת המחדל לפריסה לרוחב הוא ALWAYS_ALLOW.
      • setFinishPrimaryWithPlaceholder(): מגדיר את ההשפעה של סיום הפעילות של placeholder על הפעילויות במאגר הראשי. הערך ALWAYS מציין שהמערכת תמיד צריכה לסיים את הפעילויות במאגר הראשי כשה-placeholder מסתיים (ראו סיום פעילויות).
      • setSticky(): קובע אם פעילות placeholder מופיעה מעל מקבץ הפעילויות במסכים קטנים, אחרי שהיא מופיעה בפעם הראשונה במסך מפוצל עם רוחב מינימלי מספיק.
    4. מוסיפים את הכלל ל-WindowManager‏ RuleController:

      Kotlin

      ruleController.addRule(splitPlaceholderRule)

      Java

      ruleController.addRule(splitPlaceholderRule);
  3. מציינים פעילויות שאף פעם לא צריכות להיות חלק מחלוק:

    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. אפליקציית ההגדרות (תפריט בצד ימין) עם בורר הטפט כפעילות מוטמעת (בצד ימין).

מודל אמון

תהליכי המארח שמטמיעים פעילויות מאפליקציות אחרות יכולים להגדיר מחדש את הצגת הפעילויות המוטמעות, כולל הגודל, המיקום, החיתוך והשקיפות. מארחים זדוניים יכולים להשתמש ביכולת הזו כדי להטעות משתמשים וליצור הונאת clickjacking או התקפות אחרות שמתמקדות בממשק המשתמש.

כדי למנוע שימוש לרעה בהטמעת פעילויות בין אפליקציות, נדרש מפתחי אפליקציות ב-Android להביע הסכמה להטמעת הפעילויות שלהם. אפליקציות יכולות להגדיר מארחים בתור מהימנים או לא מהימנים.

מארחים מהימנים

כדי לאפשר לאפליקציות אחרות להטמיע את הפעילות מהאפליקציה שלכם ולשלוט באופן מלא בהצגתה, צריך לציין את אישור ה-SHA-256 של אפליקציית המארח במאפיין android:knownActivityEmbeddingCerts של האלמנטים <activity> או <application> בקובץ המניפסט של האפליקציה.

מגדירים את הערך של 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 על ידי הרצת המשימה signingReport ב-Gradle. תקציר האישור הוא טביעת האצבע מסוג SHA-256 ללא הנקודתיים המפרדות. מידע נוסף זמין במאמרים הרצת דוח חתימה ואימות הלקוח.

מארחים לא מהימנים

כדי לאפשר לכל אפליקציה להטמיע את הפעילויות של האפליקציה שלכם ולשלוט בהצגתן, צריך לציין את המאפיין android:allowUntrustedActivityEmbedding ברכיבים <activity> או <application> במניפסט של האפליקציה, לדוגמה:

<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(this).isActivityEmbedded(activity);
}

הגבלת גודל מינימלי

מערכת Android מחילה על פעילויות מוטמעות את הגובה והרוחב המינימליים שצוינו באלמנט <layout> במניפסט של האפליקציה. אם לא צוינו גובה ורוחב מינימלי באפליקציה, יילקחו ערכי ברירת המחדל של המערכת (sw220dp).

אם המארח ינסה לשנות את הגודל של הקונטיינר המוטמע לגודל קטן מהגודל המינימלי, הקונטיינר המוטמע יתרחב כדי לתפוס את כל גבולות המשימה.

<activity-alias>

כדי שאפשר יהיה להטמיע פעילות מהימנה או לא מהימנה עם האלמנט <activity-alias>, צריך להחיל את android:knownActivityEmbeddingCerts או את android:allowUntrustedActivityEmbedding על פעילות היעד ולא על הכינוי. המדיניות שמאמתת את האבטחה בשרת המערכת מבוססת על הדגלים שהוגדרו ביעד, ולא על הכינוי החלופי.

אפליקציית המארח

אפליקציות מארחות מטמיעות הטמעת פעילות בכמה אפליקציות באותו אופן שבו הן מטמיעות הטמעת פעילות באפליקציה אחת. אובייקטים מסוג SplitPairRule ו-SplitPairFilter או אובייקטים מסוג ActivityRule ו-ActivityFilter מציינים פעילויות מוטמעות ופיצולים של חלון המשימות. כללי הפיצול מוגדרים סטטית ב-XML או בזמן הריצה באמצעות קריאות ל-Jetpack WindowManager API.

אם אפליקציית מארח מנסה להטמיע פעילות שלא הביעה הסכמה להטמעה בין אפליקציות, הפעילות תופסת את כל גבולות המשימה. לכן, אפליקציות המארח צריכות לדעת אם פעילויות היעד מאפשרות הטמעה בכמה אפליקציות.

אם פעילות מוטמעת מתחילה פעילות חדשה באותה משימה, והפעילות החדשה לא הביעה הסכמה להטמעה בין אפליקציות, הפעילות תופסת את כל גבולות המשימה במקום להופיע כשכבת-על לפעילות במאגר המוטמע.

אפליקציית מארח יכולה להטמיע פעילויות משלה ללא הגבלה, כל עוד הפעילויות מופעלות באותה משימה.

דוגמאות לפיצול

פיצול ממסך מלא

איור 15. פעילות א' מתחילה את פעילות ב' בצד.

אין צורך לבצע רפאקציה. אפשר להגדיר את ההגדרות של הפיצול באופן סטטי או בזמן הריצה, ואז להפעיל את Context#startActivity() בלי פרמטרים נוספים.

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

פיצול כברירת מחדל

כשדף הנחיתה של אפליקציה מחולק לשני מאגרים במסכים גדולים, חוויית המשתמש הטובה ביותר מתקבלת כאשר שתי הפעילויות נוצרות ומוצגות בו-זמנית. עם זאת, יכול להיות שהתוכן לא יהיה זמין לקונטיינר המשני של הפיצול עד שהמשתמש יתקשר עם הפעילות בקונטיינר הראשי (לדוגמה, המשתמש בוחר פריט מתפריט ניווט). פעילות placeholder יכולה למלא את החלל הריק עד שתוכלו להציג תוכן במאגר המשני של הפיצול (ראו הקטע Placeholders).

איור 16. פיצול שנוצר על ידי פתיחת שתי פעילויות בו-זמנית. פעילות אחת היא placeholder.

כדי ליצור חלוקה עם placeholder, יוצרים placeholder ומשייכים אותו לפעילות הראשית:

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

כשאפליקציה מקבלת כוונה, הפעילות היעד יכולה להופיע כחלק משני של חלוקת פעילות. לדוגמה, בקשה להצגת מסך פרטים עם מידע על פריט מרשימת פריטים. במסכים קטנים, הפרטים מוצגים בחלון המשימות המלא. במכשירים גדולים יותר, הם מוצגים לצד הרשימה.

איור 17. פעילות פרטים של קישור עומק מוצגת לבד במסך קטן, אבל יחד עם פעילות רשימה במסך גדול.

צריך לנתב את בקשת ההפעלה לפעילות הראשית, ולהפעיל את פעילות פרטי היעד באמצעות חלוקה. המערכת בוחרת באופן אוטומטי את אופן ההצגה הנכון – בערימה או זה לצד זה – על סמך רוחב המסך הזמין.

Kotlin

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

Java

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

יכול להיות שיעדים של קישורי עומק הם הפעילויות היחידות שצריכות להיות זמינות למשתמש בסטאק הניווט לאחור, ורצוי להימנע מסגירה של פעילות הפרטים ולהשאיר רק את הפעילות הראשית:

תצוגה גדולה עם רשימת הפעילויות והפעילות המפורטת זו לצד זו.
          לא ניתן להשתמש בניווט חזרה כדי לסגור את הפעילות המפורטת ולהשאיר את הפעילות ברשימת הפעילויות במסך.

תצוגה קטנה עם פעילות מפורטת בלבד. לא ניתן להשתמש בניווט לאחור כדי לסגור את פעילות הפרטים ולהציג את פעילות הרשימה.

במקום זאת, אפשר לסיים את שתי הפעילויות בו-זמנית באמצעות המאפיין finishPrimaryWithSecondary:

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

ראו את הקטע מאפייני תצורה.

פעילויות מרובות במאגרים מפוצלים

עריכת כמה פעילויות בקונטיינר מפוצל מאפשרת למשתמשים לגשת לתוכן מעמיק. לדוגמה, כשמחלקים את המסך לרשימה ולפרטים, יכול להיות שהמשתמש יצטרך לעבור לקטע פרטים משני בלי שהפעילות הראשית תיעלם:

איור 18. הפעילות נפתחת במקום בחלונית המשנית של חלון המשימות.

Kotlin

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

Java

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

הפעילות ברמת הפירוט המשני ממוקמת מעל הפעילות ברמת הפירוט, ומסתירה אותה:

לאחר מכן, המשתמש יכול לחזור לרמת הפירוט הקודמת על ידי ניווט חזרה בסטאק:

איור 19. הפעילות הוסרה מהחלק העליון של המקבץ.

יצירת סטאק של פעילויות זוהי התנהגות ברירת המחדל כשפעילויות מופעלות מפעילות באותו מאגר משני. פעילויות שמתחילות בקונטיינר הראשי במסגרת חלוקה פעילה מגיעות גם הן לקונטיינר המשני בחלק העליון של רשימת הפעילויות.

פעילויות במשימה חדשה

כשפעילויות בחלון של משימה מפוצלת מתחילות פעילויות במשימה חדשה, המשימה החדשה נפרדת מהמשימה שכוללת את הפיצול ומוצגת בחלון מלא. במסך 'מהזמן האחרון' יוצגו שתי משימות: המשימה בחלוקה והמשימה החדשה.

איור 20. מתחילים את הפעילות ג' במשימה חדשה מתוך הפעילות ב'.

החלפת פעילות

אפשר להחליף פעילויות בסטאק הקונטיינר המשני. לדוגמה, כשהפעילות הראשית משמשת לניווט ברמה העליונה והפעילות המשנית היא יעד שנבחר. כל בחירה מהניווט ברמה העליונה אמורה להתחיל פעילות חדשה בקונטיינר המשני ולהסיר את הפעילות או הפעילויות שהיו שם קודם.

איור 21. פעילות הניווט ברמה העליונה בחלונית הראשית מחליפה את פעילויות היעד בחלונית המשנית.

אם האפליקציה לא מסיימת את הפעילות במאגר המשני כשבחירת הניווט משתנה, יכול להיות שהניווט לאחור יהיה מבולגן כשהחלוקה תיסגר (כשהמכשיר מקופל). לדוגמה, אם יש תפריט בחלונית הראשית ומסכים א' ו-ב' מוערמים בחלונית המשנית, כשהמשתמש מקפל את הטלפון, המסך ב' מופיע מעל המסך א', והמסך א' מופיע מעל התפריט. כשהמשתמש חוזר מ-B, 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)))
    }
}

Java

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

לחלופין, אפשר להשתמש באותה פעילות משנית, ומפעילות הראשית (התפריט) לשלוח כוונות חדשות שמתקבלות באותה מכונה אבל מפעילות עדכון של מצב או ממשק משתמש בקונטיינר המשני.

מספר פילוחים

אפליקציות יכולות לספק ניווט מעמיק ברמות מרובות על ידי הפעלת פעילויות נוספות בצד.

כשפעילות בקונטיינר משני מפעילה פעילות חדשה בצד, נוצר פיצול חדש מעל הפיצול הקיים.

איור 22. פעילות ב' מתחילה את פעילות ג' בצד.

סטאק החזרה מכיל את כל הפעילויות שנפתחו בעבר, כך שמשתמשים יכולים לנווט אל חלוקת ה-A/B אחרי שהם מסיימים את הפעילות C.

פעילויות א&#39;, ב&#39; ו-ג&#39; ב-stack. הפעילויות נערמות לפי הסדר הבא, מלמעלה למטה: C,‏ B,‏ A.

כדי ליצור חלוקה חדשה, מריצים את הפעילות החדשה לצד המאגר המשני הקיים. מגדירים את ההגדרות של חלוקות 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 {
    . . .
    fun onOpenC() {
        startActivity(Intent(this, C::class.java))
    }
}

Java

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

תגובה לשינויים במצב המפוצל

לפעילויות שונות באפליקציה יכולים להיות רכיבי ממשק משתמש שמבצעים את אותה הפונקציה. לדוגמה, רכיב בקרה שפותח חלון שמכיל את הגדרות החשבון.

איור 23. פעילויות שונות עם רכיבי ממשק משתמש זהים מבחינה פונקציונלית.

אם יש שתי פעילויות עם רכיב משותף בממשק המשתמש, והן מחולקות, הצגת הרכיב בשתי הפעילויות היא מיותרת ואולי מבלבלת.

איור 24. אלמנטים כפולים של ממשק המשתמש בחלוקת הפעילות.

כדי לדעת מתי פעילויות נמצאות במצב חלוקה, בודקים את התהליך SplitController.splitInfoList או רושמים מאזין עם SplitControllerCallbackAdapter לשינויים במצב החלוקה. לאחר מכן, משנים את ממשק המשתמש בהתאם:

Kotlin

val layout = layoutInflater.inflate(R.layout.activity_main, null)
val view = layout.findViewById<View>(R.id.infoButton)
lifecycleScope.launch {
    lifecycle.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) {
    . . .
    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>

סיום פעילויות

המשתמשים יכולים לסיים את הפעילויות בכל אחד מהצדדים של המסך המפוצל על ידי החלקה מהקצה של המסך:

איור 25. תנועת החלקה לסיום פעילות ב'.
איור 26. תנועת החלקה לסיום פעילות א'.

אם המכשיר מוגדר לשימוש בלחצן החזרה אחורה במקום בתנועות ניווט, הקלט נשלח לפעילות שבה מתמקד המשתמש – הפעילות שנגעו בה או שהפעילו אותה לאחרונה.

ההשפעה של סיום כל הפעילויות בקונטיינר על הקונטיינר הנגדי תלויה בהגדרת הפיצול.

מאפייני תצורה

אפשר לציין מאפיינים של כללי חלוקה לשניים כדי לקבוע איך השלמת כל הפעילויות בצד אחד של החלוקה משפיעה על הפעילויות בצד השני של החלוקה. המאפיינים הם:

  • window:finishPrimaryWithSecondary — איך סיום כל הפעילויות במאגר המשני משפיע על הפעילויות במאגר הראשי
  • window:finishSecondaryWithPrimary — איך סיום כל הפעילויות במאגר הראשי משפיע על הפעילויות במאגר המשני

הערכים האפשריים של המאפיינים כוללים:

  • always – תמיד צריך לסיים את הפעילויות בקונטיינר המשויך
  • never — אף פעם לא לסיים את הפעילויות בקונטיינר המשויך
  • adjacent — לסיים את הפעילויות במאגר המשויך כששני המאגרים מוצגים זה לצד זה, אבל לא כששני המאגרים מוערמים

לדוגמה:

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

הגדרות ברירת המחדל

כשכל הפעילויות בקונטיינר אחד של חלוקה מסתיימות, הקונטיינר הנותר תופס את כל החלון:

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

חלוקה שמכילה את הפעילויות א&#39; ו-ב&#39;. הפעילות A מסתיימת, ו-B תופסת את כל החלון.

חלוקה שמכילה את הפעילויות א&#39; ו-ב&#39;. הפעילות ב-B מסתיימת, ו-A תופסת את כל החלון.

לסיים פעילויות ביחד

לסיים את הפעילויות בקונטיינר הראשי באופן אוטומטי כשכל הפעילויות בקונטיינר המשני מסתיימות:

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

חלוקה שמכילה את הפעילויות א&#39; ו-ב&#39;. המשימה B מסתיימת, וכך גם המשימה A, וחלון המשימות נשאר ריק.

חלוקה שמכילה את הפעילויות א&#39; ו-ב&#39;. משתמש א&#39; סיים, ומשתמש ב&#39; נשאר לבדו בחלון המשימות.

לסיים את הפעילויות בקונטיינר המשני באופן אוטומטי כשכל הפעילויות בקונטיינר הראשי מסתיימות:

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

חלוקה שמכילה את הפעילויות א&#39; ו-ב&#39;. המשימה א&#39; מסתיימת, וכך גם המשימה ב&#39;, וחלון המשימות נשאר ריק.

חלוקה שמכילה את הפעילויות א&#39; ו-ב&#39;. משתמש ב&#39;ב&#39; סיים, והחלון של המשימה נשאר רק עם משתמש ב&#39;א&#39;.

לסיים פעילויות ביחד כשכל הפעילויות בקונטיינר הראשי או המשני מסתיימות:

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

חלוקה שמכילה את הפעילויות א&#39; ו-ב&#39;. המשימה א&#39; מסתיימת, וכך גם המשימה ב&#39;, וחלון המשימות נשאר ריק.

חלוקה שמכילה את הפעילויות א&#39; ו-ב&#39;. המשימה B מסתיימת, וכך גם המשימה A, וחלון המשימות נשאר ריק.

השלמת כמה פעילויות בקונטיינרים

אם יש כמה פעילויות בקונטיינר מפוצל, סיום פעילות בתחתית הערימה לא מסיים באופן אוטומטי את הפעילויות שבחלק העליון.

לדוגמה, אם יש שתי פעילויות בקונטיינר המשני, C מעל B:

רשימת הפעילויות המשנית שמכילה את הפעילות C, שממוקמת מעל הפעילות B, ממוקמת מעל רשימת הפעילויות הראשית שמכילה את הפעילות A.

וההגדרה של הפיצול מוגדרת לפי ההגדרה של הפעילויות א' ו-ב':

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

סיום הפעילות העליונה שומר על הפיצול.

חלוקה שבה הפעילות א&#39; נמצאת במיכל הראשי והפעילויות ב&#39; ו-ג&#39; נמצאות במיכל המשני, כאשר הפעילות ג&#39; מוערמת מעל הפעילות ב&#39;. משתמש C מסיים, והפעילות מחולקת בין משתמשים A ו-B.

סיום הפעילות התחתונה (שורש) של המאגר המשני לא מסיר את הפעילויות שמעליו, ולכן גם מאפשר לשמור על הפיצול.

חלוקה שבה הפעילות א&#39; נמצאת במיכל הראשי והפעילויות ב&#39; ו-ג&#39; נמצאות במיכל המשני, כאשר הפעילות ג&#39; מוערמת מעל הפעילות ב&#39;. משתמש B מסיים, והפעילות מחולקת בין משתמשים A ו-C.

כללים נוספים לסיום פעילויות ביחד, כמו סיום הפעילות המשנית עם הפעילות הראשית, מתבצעים גם הם:

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

חלוקה שבה הפעילות א&#39; נמצאת במיכל הראשי והפעילויות ב&#39; ו-ג&#39; נמצאות במיכל המשני, כאשר הפעילות ג&#39; מוערמת מעל הפעילות ב&#39;. משימה A מסתיימת, וגם משימה B ו-C.

כשהחלוקה מוגדרת לסיום של המודעה הראשית והמשנית יחד:

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

חלוקה שבה הפעילות א&#39; נמצאת במיכל הראשי והפעילויות ב&#39; ו-ג&#39; נמצאות במיכל המשני, כאשר הפעילות ג&#39; מוערמת מעל הפעילות ב&#39;. משתמש C מסיים, והפעילות מחולקת בין משתמשים A ו-B.

חלוקה שבה הפעילות א&#39; נמצאת במיכל הראשי והפעילויות ב&#39; ו-ג&#39; נמצאות במיכל המשני, כאשר הפעילות ג&#39; מוערמת מעל הפעילות ב&#39;. משתמש B מסיים, והפעילות מחולקת בין משתמשים A ו-C.

חלוקה שבה הפעילות א&#39; נמצאת במיכל הראשי והפעילויות ב&#39; ו-ג&#39; נמצאות במיכל המשני, כאשר הפעילות ג&#39; מוערמת מעל הפעילות ב&#39;. משימה A מסתיימת, ובכך מסתיימות גם המשימות B ו-C.

שינוי מאפייני הפיצול בזמן הריצה

אי אפשר לשנות את המאפיינים של חלוקה פעילה וגלומה. שינוי כללי הפיצול משפיע על השקות נוספות של פעילויות ועל מאגרים חדשים, אבל לא על חלוקות קיימות ופעילות.

כדי לשנות את המאפיינים של חלוקות פעילות, צריך לסיים את הפעילות או הפעילויות בחלוקה ולהפעיל אותה שוב עם הגדרות חדשות.

מאפייני חלוקה דינמיים

ב-Android 15 (רמת API 35) ואילך, עם תמיכה ב-Jetpack WindowManager 1.4 ואילך, יש תכונות דינמיות שמאפשרות לקבוע את החלוקות של הטמעת הפעילות, כולל:

  • הרחבת חלוניות: מחלק אינטראקטיבי שניתן לגרירה מאפשר למשתמשים לשנות את הגודל של החלונות במצגת מחולקת.
  • הצמדה של סטאק פעילויות: משתמשים יכולים להצמיד את התוכן בקונטיינר אחד ולבודד את הניווט בקונטיינר הזה מהניווט בקונטיינר השני.
  • הכהיית תיבת דו-שיח במסך מלא: כשמוצגת תיבת דו-שיח, האפליקציות יכולות לציין אם להכהות את כל חלון המשימה או רק את המארז שבו נפתחה תיבת הדו-שיח.

הרחבת חלונית

הרחבת החלון מאפשרת למשתמשים לשנות את שטח המסך שהוקצה לשתי הפעילויות בפריסה של שתי חלוניות.

כדי להתאים אישית את המראה של מחלק החלונות ולהגדיר את טווח הגרירת המחלק:

  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(context, 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(context, 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() של הפעילות, מגדירים למעקב אחר לחיצות על הלחצן:

    Kotlin

    pinButton = 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);
    });

עמעום המסך במהלך תיבת דו-שיח במסך מלא

בדרך כלל, התצוגות של הפעילויות כהות כדי למשוך את תשומת הלב לתיבת הדו-שיח. כשממקמים פעילות בתוך פעילות אחרת, שתי החלונות של המסך הכפול צריכים להתעמעם, ולא רק החלון שמכיל את הפעילות שפתחה את תיבת הדו-שיח, כדי ליצור חוויית משתמש אחידה.

ב-WindowManager מגרסה 1.4 ואילך, כל חלון האפליקציה מתעמעם כברירת מחדל כשתיבת דו-שיח נפתחת (ראו EmbeddingConfiguration.DimAreaBehavior.ON_TASK).

כדי להכהות רק את המאגר של הפעילות שפתחה את תיבת הדו-שיח, משתמשים ב-EmbeddingConfiguration.DimAreaBehavior.ON_ACTIVITY_STACK.

איך מעבירים פעילות מחלון מפוצל לחלון מלא

יוצרים הגדרה חדשה שבה מוצג חלון מלא של הפעילות הצדדית, ואז מפעילים מחדש את הפעילות עם כוונה שמפנה לאותה מכונה.

בדיקת התמיכה בחלוקה בזמן הריצה

יש תמיכה בהטמעת פעילות ב-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.
}

אם אין תמיכה בחלוקות, הפעילויות מופעלות מעל ל-activity stack (לפי מודל הטמעה ללא פעילות).

מניעת שינוי מברירת המחדל של המערכת

יצרני מכשירי Android (יצרני ציוד מקורי, או OEM) יכולים להטמיע הטמעת פעילות כפונקציה של מערכת המכשיר. המערכת מציינת כללי פיצול לאפליקציות עם כמה פעילויות, ומבטלת את התנהגות החלונות של האפליקציות. שינוי ברירת המחדל של המערכת מאלץ אפליקציות עם כמה פעילויות לעבור למצב הטמעת פעילות שהוגדר על ידי המערכת.

הטמעת הפעילות של המערכת יכולה לשפר את הצגת האפליקציה באמצעות פריסות עם כמה חלונות, כמו list-detail, בלי לבצע שינויים באפליקציה. עם זאת, הטמעת הפעילות של המערכת עשויה גם לגרום לפריסות שגויות של האפליקציה, לבאגים או להתנגשויות עם הטמעת הפעילות שהאפליקציה מטמיעה.

האפליקציה יכולה למנוע הטמעה של פעילות מערכת או לאפשר אותה על ידי הגדרת מאפיין בקובץ המניפסט של האפליקציה. לדוגמה:

<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 של WindowManager ב-Jetpack. מגדירים את הערך כ-false אם האפליקציה מטמיעה הטמעת פעילות, או אם רוצים למנוע בדרך אחרת את המערכת משימוש בכללי הטמעת הפעילות שלה באפליקציה. מגדירים את הערך כ-true כדי לאפשר למערכת להחיל על האפליקציה הטמעת פעילות שהוגדרה על ידי המערכת.

מגבלות, הגבלות וסייגים

  • רק אפליקציית המארח של המשימה, שזוהתה בתור הבעלים של פעילות הבסיס במשימה, יכולה לארגן ולטמיע פעילויות אחרות במשימה. אם פעילויות שתומכות בהטמעה ובחלוקות פועלות במשימה ששייכת לאפליקציה אחרת, ההטמעה והחלוקות לא יפעלו בפעילויות האלה.
  • אפשר לארגן פעילויות רק בתוך משימה אחת. הפעלת פעילות במשימה חדשה תמיד תפתח אותה בחלון חדש מורחב, מחוץ לכל פיצולים קיימים.
  • אפשר לארגן ולחלק רק פעילויות באותו תהליך. פונקציית ה-callback‏ SplitInfo מדווחת רק על פעילויות ששייכות לאותו תהליך, כי אין דרך לדעת על פעילויות בתהליכים שונים.
  • כל כלל פעילות יחיד או כלל פעילות משולש חל רק על השקות של פעילויות שמתרחשות אחרי שהכלל נרשם. בשלב זה אין אפשרות לעדכן חלוקות קיימות או את המאפיינים החזותיים שלהן.
  • הגדרת המסנן של הצמד המפוצל חייבת להתאים לכוונות שבהן השתמשתם כשהפעילתם את הפעילויות באופן מלא. ההתאמה מתרחשת בנקודה שבה מתחילה פעילות חדשה מתהליך האפליקציה, כך שיכול להיות שהיא לא תדע על שמות הרכיבים שמתקבלים מאוחר יותר בתהליך המערכת כשמשתמשים בכוונות משתמשים מרומזות. אם שם הרכיב לא ידוע בזמן ההשקה, אפשר להשתמש בתו אסימון ("*/*") ולבצע סינון על סמך פעולת הכוונה.
  • בשלב זה אין אפשרות להעביר פעילויות בין קונטיינרים או להעביר אותן לחלוקות או מהן אחרי שהן נוצרו. ספריית WindowManager יוצרת חלוקות רק כשמפעילים פעילויות חדשות עם כללים תואמים, והן נמחקות כשהפעילות האחרונה בקונטיינר המפוצל מסתיימת.
  • אפשר להפעיל מחדש פעילויות כשהתצורה משתנה. לכן, כשיוצרים או מסירים פיצול וערכי הגבול של הפעילות משתנים, הפעילות יכולה לעבור תהליך של השמדה מוחלטת של המכונה הקודמת ויצירה של מכונה חדשה. כתוצאה מכך, מפתחי אפליקציות צריכים להיזהר בדברים כמו הפעלת פעילויות חדשות מקריאות חזרה (callbacks) במחזור החיים.
  • כדי לתמוך בהטמעת פעילות, המכשירים חייבים לכלול את ממשק התוספים של החלון. ממשק המשתמש נכלל כמעט בכל המכשירים עם מסך גדול שפועלים עם Android 12L (רמת API 32) ואילך. עם זאת, במכשירים מסוימים עם מסך גדול שלא יכולים להריץ כמה פעילויות, ממשק התוספים של החלונות לא מופיע. אם מכשיר עם מסך גדול לא תומך במצב של חלונות מרובים, יכול להיות שהוא לא תומך בהטמעת פעילויות.

מקורות מידע נוספים