הגדרת הצגה על פי דרישה

מודולים של תכונות מאפשרים להפריד בין תכונות ומשאבים מסוימים מהמודול הבסיסי של האפליקציה ולכלול אותם ב-App Bundle. עד באמצעות Play Feature Delivery, המשתמשים יכולים, לדוגמה, להוריד ולהתקין את האפליקציות האלה מאוחר יותר על פי דרישה אחרי שהם כבר התקינו את ה-APK הבסיסי של האפליקציה.

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

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

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

הדף הזה עוזר לכם להוסיף מודול של פיצ'ר לפרויקט האפליקציה שלכם. להגדיר אותו למסירה על פי דרישה. לפני שתתחילו, ודאו שאתם באמצעות Android Studio 3.5 ואילך ופלאגין של Android Gradle 3.5.0 ומעלה.

הגדרת מודול חדש למסירה על פי דרישה

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

כדי להוסיף מודול של תכונה לפרויקט האפליקציה באמצעות Android Studio: כך עושים את זה:

  1. אם עדיין לא עשיתם זאת, עליכם לפתוח את פרויקט האפליקציה בסביבת הפיתוח המשולבת (IDE).
  2. בוחרים באפשרות קובץ > חדש > מודול חדש מסרגל התפריטים.
  3. בתיבת הדו-שיח Create New Module בוחרים מודול תכונות דינמיות ולוחצים על הבא.
  4. בקטע הגדרת המודול החדש, מבצעים את הפעולות הבאות: הבאים:
    1. בוחרים את Base application מודול לפרויקט האפליקציה מתוך בתפריט הנפתח.
    2. מציינים שם מודול. סביבת הפיתוח המשולבת משתמשת בשם הזה כדי לזהות בתור פרויקט משנה של Gradle קובץ הגדרות Gradle. אחרי ש לבניית ה-App Bundle שלך, Gradle משתמשת ברכיב האחרון של פרויקט המשנה כדי להחדיר את המאפיין <manifest split> בתוך המניפסט של מודול התכונה.
    3. מציינים את שם החבילה של המודול. כברירת מחדל, Android Studio מציע שם חבילה שמשלב את שם חבילת הבסיס של את המודול הבסיסי ואת שם המודול שציינתם בשלב הקודם.
    4. בוחרים את רמת ה-API המינימלית שבה המודול יתמוך. הערך הזה צריך להיות זהה לערך של מודול הבסיס.
  5. לוחצים על הבא.
  6. בקטע אפשרויות להורדת מודול, מבצעים את הפעולות הבאות:

    1. מציינים את כותרת המודול באמצעות 50 תווים לכל היותר. הפלטפורמה משתמש בכותרת הזו כדי לזהות את המודול בפני המשתמשים כאשר, לדוגמה, ומאשרת אם המשתמש רוצה להוריד את המודול. בשביל זה ולכן המודול הבסיסי של האפליקציה חייב לכלול את כותרת המודול string resource, יכולים לתרגם. כשיוצרים את המודול באמצעות Android Studio, סביבת הפיתוח המשולבת (IDE) מוסיף בשבילך את משאב המחרוזת למודול הבסיסי ומזינה את במניפסט של מודול התכונה:

      <dist:module
          ...
          dist:title="@string/feature_title">
      </dist:module>
      
    2. בתפריט הנפתח, בקטע הכללה בזמן ההתקנה, בוחרים באפשרות לא לכלול כוללים את המודול בזמן ההתקנה. מערכת Android Studio מחדירה את במניפסט של המודול כדי לשקף את הבחירה שלכם:

      <dist:module ... >
        <dist:delivery>
            <dist:on-demand/>
        </dist:delivery>
      </dist:module>
      
    3. אם רוצים שהמודול הזה יהיה זמין, מסמנים את התיבה לצד Fusing למכשירים שמותקנת בהם גרסת Android 4.4 (רמת API 20) ומטה ומופיעים חבילות APK מרובות. כלומר, אפשר להפעיל במודול הזה התנהגות על פי דרישה ומשביתים את ההיתוך כדי להשמיט אותו ממכשירים שלא תומכים הורדה והתקנה של חבילות APK מפוצלות. מערכת Android Studio מחדירה את במניפסט של המודול כדי לשקף את הבחירה שלכם:

      <dist:module ...>
          <dist:fusing dist:include="true | false" />
      </dist:module>
      
  7. לוחצים על סיום.

כשיצירת המודול מ-Android Studio, בודקים את התוכן שלו בחלונית Project (בוחרים באפשרות View > Tool Windows > Project (תצוגה > כלי Windows > פרויקט) מסרגל התפריטים). קוד ברירת המחדל, המשאבים והארגון צריכים להיות דומים לאלה של מודול האפליקציה הרגיל.

בשלב הבא, צריך להטמיע את הפונקציונליות של התקנה על פי דרישה באמצעות ספריית Feature Delivery של Play.

הכללה של ספריית Play Feature Delivery בפרויקט

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

בקשת מודול על פי דרישה

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

לדוגמה, נניח שיש לך אפליקציה עם מודול על פי דרישה שמאפשרת לתעד ולשלוח אותו הודעות תמונה באמצעות המצלמה של המכשיר, והמודול הזה על פי דרישה מציין את split="pictureMessages" במניפסט שלו. הדוגמה הבאה משתמשת ב-SplitInstallManager כדי לבקש את pictureMessages מודול (יחד עם מודול נוסף לכמה מסננים לקידום מכירות):

Kotlin

// Creates an instance of SplitInstallManager.
val splitInstallManager = SplitInstallManagerFactory.create(context)

// Creates a request to install a module.
val request =
    SplitInstallRequest
        .newBuilder()
        // You can download multiple on demand modules per
        // request by invoking the following method for each
        // module you want to install.
        .addModule("pictureMessages")
        .addModule("promotionalFilters")
        .build()

splitInstallManager
    // Submits the request to install the module through the
    // asynchronous startInstall() task. Your app needs to be
    // in the foreground to submit the request.
    .startInstall(request)
    // You should also be able to gracefully handle
    // request state changes and errors. To learn more, go to
    // the section about how to Monitor the request state.
    .addOnSuccessListener { sessionId -> ... }
    .addOnFailureListener { exception ->  ... }

Java

// Creates an instance of SplitInstallManager.
SplitInstallManager splitInstallManager =
    SplitInstallManagerFactory.create(context);

// Creates a request to install a module.
SplitInstallRequest request =
    SplitInstallRequest
        .newBuilder()
        // You can download multiple on demand modules per
        // request by invoking the following method for each
        // module you want to install.
        .addModule("pictureMessages")
        .addModule("promotionalFilters")
        .build();

splitInstallManager
    // Submits the request to install the module through the
    // asynchronous startInstall() task. Your app needs to be
    // in the foreground to submit the request.
    .startInstall(request)
    // You should also be able to gracefully handle
    // request state changes and errors. To learn more, go to
    // the section about how to Monitor the request state.
    .addOnSuccessListener(sessionId -> { ... })
    .addOnFailureListener(exception -> { ... });

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

הערה: מותר לבקש של התכונה שכבר מותקן במכשיר. ממשק API הבקשה מתייחסת באופן מיידי לבקשה שהושלמה אם היא מזהה שהמודול כבר מותקנת. בנוסף, לאחר התקנת מודול, Google Play מעדכן אותו באופן אוטומטי. כלומר, כשמעלים גרסה חדשה של ה-App Bundle, הפלטפורמה מעדכן את כל חבילות ה-APK המותקנות ששייכות לאפליקציה. מידע נוסף זמין במאמר הבא: לנהל את העדכונים לאפליקציות.

כדי לקבל גישה לקוד ולמשאבים של המודול, האפליקציה צריכה: להפעיל את SplitCompat. לתשומת ליבכם: SplitCompat נדרש לאפליקציות ללא התקנה ל-Android.

דחיית ההתקנה של מודולים על פי דרישה

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

ניתן לציין מודול להורדה מאוחר יותר באמצעות deferredInstall() ל-Assistant, כפי שמוצג בהמשך. ובניגוד לכך, SplitInstallManager.startInstall() האפליקציה לא צריכה להיות בחזית כדי לשלוח בקשה עיכוב בהתקנה.

Kotlin

// Requests an on demand module to be downloaded when the app enters
// the background. You can specify more than one module at a time.
splitInstallManager.deferredInstall(listOf("promotionalFilters"))

Java

// Requests an on demand module to be downloaded when the app enters
// the background. You can specify more than one module at a time.
splitInstallManager.deferredInstall(Arrays.asList("promotionalFilters"));

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

מעקב אחרי מצב הבקשה

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

Kotlin

// Initializes a variable to later track the session ID for a given request.
var mySessionId = 0

// Creates a listener for request status updates.
val listener = SplitInstallStateUpdatedListener { state ->
    if (state.sessionId() == mySessionId) {
      // Read the status of the request to handle the state update.
    }
}

// Registers the listener.
splitInstallManager.registerListener(listener)

...

splitInstallManager
    .startInstall(request)
    // When the platform accepts your request to download
    // an on demand module, it binds it to the following session ID.
    // You use this ID to track further status updates for the request.
    .addOnSuccessListener { sessionId -> mySessionId = sessionId }
    // You should also add the following listener to handle any errors
    // processing the request.
    .addOnFailureListener { exception ->
        // Handle request errors.
    }

// When your app no longer requires further updates, unregister the listener.
splitInstallManager.unregisterListener(listener)

Java

// Initializes a variable to later track the session ID for a given request.
int mySessionId = 0;

// Creates a listener for request status updates.
SplitInstallStateUpdatedListener listener = state -> {
    if (state.sessionId() == mySessionId) {
      // Read the status of the request to handle the state update.
    }
};

// Registers the listener.
splitInstallManager.registerListener(listener);

...

splitInstallManager
    .startInstall(request)
    // When the platform accepts your request to download
    // an on demand module, it binds it to the following session ID.
    // You use this ID to track further status updates for the request.
    .addOnSuccessListener(sessionId -> { mySessionId = sessionId; })
    // You should also add the following listener to handle any errors
    // processing the request.
    .addOnFailureListener(exception -> {
        // Handle request errors.
    });

// When your app no longer requires further updates, unregister the listener.
splitInstallManager.unregisterListener(listener);

טיפול בשגיאות בבקשות

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

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

Kotlin

splitInstallManager
    .startInstall(request)
    .addOnFailureListener { exception ->
        when ((exception as SplitInstallException).errorCode) {
            SplitInstallErrorCode.NETWORK_ERROR -> {
                // Display a message that requests the user to establish a
                // network connection.
            }
            SplitInstallErrorCode.ACTIVE_SESSIONS_LIMIT_EXCEEDED -> checkForActiveDownloads()
            ...
        }
    }

fun checkForActiveDownloads() {
    splitInstallManager
        // Returns a SplitInstallSessionState object for each active session as a List.
        .sessionStates
        .addOnCompleteListener { task ->
            if (task.isSuccessful) {
                // Check for active sessions.
                for (state in task.result) {
                    if (state.status() == SplitInstallSessionStatus.DOWNLOADING) {
                        // Cancel the request, or request a deferred installation.
                    }
                }
            }
        }
}

Java

splitInstallManager
    .startInstall(request)
    .addOnFailureListener(exception -> {
        switch (((SplitInstallException) exception).getErrorCode()) {
            case SplitInstallErrorCode.NETWORK_ERROR:
                // Display a message that requests the user to establish a
                // network connection.
                break;
            case SplitInstallErrorCode.ACTIVE_SESSIONS_LIMIT_EXCEEDED:
                checkForActiveDownloads();
            ...
    });

void checkForActiveDownloads() {
    splitInstallManager
        // Returns a SplitInstallSessionState object for each active session as a List.
        .getSessionStates()
        .addOnCompleteListener( task -> {
            if (task.isSuccessful()) {
                // Check for active sessions.
                for (SplitInstallSessionState state : task.getResult()) {
                    if (state.status() == SplitInstallSessionStatus.DOWNLOADING) {
                        // Cancel the request, or request a deferred installation.
                    }
                }
            }
        });
}

בטבלה הבאה מתוארים מצבי השגיאה שייתכן שהאפליקציה צריכה לטפל בהם:

קוד שגיאה תיאור הצעה לפעולה
ACTIVE_SESSIONS_LIMIT_EXCEEDED הבקשה נדחתה כי יש לפחות שם אחד מתבצעת הורדה עכשיו. בודקים אם יש בקשות שעדיין בהורדה, כפי שמוצג בדוגמה שלמעלה.
MODULE_UNAVAILABLE ב-Google Play לא ניתן למצוא את המודול המבוקש בגרסה המותקנת הנוכחית של האפליקציה, המכשיר ו-Google Play של המשתמש חשבון. אם למשתמש אין גישה למודול, מודיעים לו.
INVALID_REQUEST Google Play קיבלה את הבקשה, אבל הבקשה אינו חוקי. אימות המידע שנכלל בבקשה מלאה ומדויקת.
SESSION_NOT_FOUND לא נמצאה סשן למזהה סשן נתון. אם אתם מנסים לעקוב אחר מצב הבקשה יש לוודא שמזהה הסשן נכון.
API_NOT_AVAILABLE במכשיר הנוכחי אין תמיכה בספרייה להעברת תכונות ב-Play. כלומר, לא ניתן להוריד ולהתקין את המכשיר על פי דרישה. עבור מכשירים שבהם פועל Android 4.4 (רמת API 20) ומטה, עליך כוללים מודולים של תכונות בזמן ההתקנה באמצעות מאפיין dist:fusing למניפסט. למידע נוסף, אפשר לקרוא על מניפסט של מודול התכונה.
NETWORK_ERROR הבקשה נכשלה עקב שגיאת רשת. הצגת בקשה למשתמש ליצור חיבור לרשת או לעבור לרשת אחרת.
ACCESS_DENIED האפליקציה לא יכולה לרשום את הבקשה בגלל שההרשאות לא מספיקות. מצב כזה קורה בדרך כלל כשהאפליקציה פועלת ברקע. אפשר לנסות לשלוח את הבקשה כשהאפליקציה חוזרת לקדמת התמונה.
INCOMPATIBLE_WITH_EXISTING_SESSION הבקשה מכילה מודול אחד או יותר שכבר המבוקשים, אבל הם עדיין לא הותקנו. עליכם ליצור בקשה חדשה שלא כוללת מודולים האפליקציה כבר ביקשה, או להמתין לכל המודולים הנדרשים כרגע כדי לסיים את ההתקנה לפני ניסיון חוזר של הבקשה.

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

SERVICE_DIED השירות שאחראי לטיפול בבקשה נפטר. אפשר לנסות לשלוח את הבקשה שוב.

SplitInstallStateUpdatedListener מקבל SplitInstallSessionState עם קוד השגיאה הזה, סטטוס FAILED ומזהה הסשן -1.

INSUFFICIENT_STORAGE אין במכשיר מספיק נפח אחסון פנוי להתקנת התכונה של מודל טרנספורמר. צריך להודיע למשתמש שאין לו מספיק נפח אחסון כדי להתקין את האפליקציה הזו .
SPLITCOMPAT_ תואם_ERROR, SPLITCOMPAT_EMULATION_ERROR, SPLITCOMPAT_COPY_ERROR לא ניתן היה לטעון את מודול התכונה ב-SplitCompat. השגיאות האלה אמורות להיפתר באופן אוטומטי אחרי האפליקציה הבאה להפעיל מחדש.
PLAY_STORE_NOT_FOUND האפליקציה של חנות Play לא מותקנת במכשיר. צריך ליידע את המשתמש שצריך להוריד את האפליקציה של חנות Play .
APP_NOT_OWNED האפליקציה לא הותקנה על ידי Google Play ולא ניתן להשתמש בתכונה ההורדה הושלמה. השגיאה הזו יכולה לקרות רק בהתקנות שנדחו. כדי שהמשתמש יצרף את האפליקציה ב-Google Play, צריך להשתמש startInstall() שיכול לקבל את אישור המשתמש.
INTERNAL_ERROR אירעה שגיאה פנימית בחנות Play. אפשר לנסות לשלוח את הבקשה שוב.

אם משתמש מבקש להוריד מודול על פי דרישה ומתרחשת שגיאה, מומלץ להציג תיבת דו-שיח שמציעה למשתמש שתי אפשרויות: שוב (כדי לבקש שוב את הבקשה) ו-Cancel (ביטול הפעולה). בקשה). לקבלת תמיכה נוספת, עליך לספק גם קישור עזרה מפנה את המשתמשים אל מרכז העזרה של Google Play

טיפול בעדכונים על המצב

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

Kotlin

override fun onStateUpdate(state : SplitInstallSessionState) {
    if (state.status() == SplitInstallSessionStatus.FAILED
        && state.errorCode() == SplitInstallErrorCode.SERVICE_DIED) {
       // Retry the request.
       return
    }
    if (state.sessionId() == mySessionId) {
        when (state.status()) {
            SplitInstallSessionStatus.DOWNLOADING -> {
              val totalBytes = state.totalBytesToDownload()
              val progress = state.bytesDownloaded()
              // Update progress bar.
            }
            SplitInstallSessionStatus.INSTALLED -> {

              // After a module is installed, you can start accessing its content or
              // fire an intent to start an activity in the installed module.
              // For other use cases, see access code and resources from installed modules.

              // If the request is an on demand module for an Android Instant App
              // running on Android 8.0 (API level 26) or higher, you need to
              // update the app context using the SplitInstallHelper API.
            }
        }
    }
}

Java

@Override
public void onStateUpdate(SplitInstallSessionState state) {
    if (state.status() == SplitInstallSessionStatus.FAILED
        && state.errorCode() == SplitInstallErrorCode.SERVICE_DIES) {
       // Retry the request.
       return;
    }
    if (state.sessionId() == mySessionId) {
        switch (state.status()) {
            case SplitInstallSessionStatus.DOWNLOADING:
              int totalBytes = state.totalBytesToDownload();
              int progress = state.bytesDownloaded();
              // Update progress bar.
              break;

            case SplitInstallSessionStatus.INSTALLED:

              // After a module is installed, you can start accessing its content or
              // fire an intent to start an activity in the installed module.
              // For other use cases, see access code and resources from installed modules.

              // If the request is an on demand module for an Android Instant App
              // running on Android 8.0 (API level 26) or higher, you need to
              // update the app context using the SplitInstallHelper API.
        }
    }
}

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

מצב הבקשה תיאור הצעה לפעולה
בהמתנה הבקשה אושרה ההורדה אמורה להתחיל בקרוב. לאתחל רכיבים בממשק המשתמש, כגון סרגל התקדמות, כדי לתת למשתמשים משוב על ההורדה.
REQUIRES_USER_CONFIRMATION ההורדה מחייבת אישור משתמש. הסטטוס הזה מופיע בדרך כלל אם האפליקציה לא הותקנה דרך Google Play. להציג למשתמש בקשה לאשר את הורדת התכונה דרך Google Play. מידע נוסף זמין בקטע לקבל את אישור המשתמש.
הורדה ההורדה מתבצעת. אם בחרת להציג סרגל התקדמות להורדה, עליך להשתמש ב SplitInstallSessionState.bytesDownloaded() ו-SplitInstallSessionState.totalBytesToDownload() שיטות לעדכון ממשק המשתמש (ראו את דוגמת הקוד שמעל הטבלה הזו).
הורדו המכשיר הוריד את המודול אבל ההתקנה עדיין לא התחילה. צריך להפעיל את SplitCompat באפליקציות כדי לקבל גישה למודולים שהורדו ולהימנע מלראות את המצב הזה. הפעולה הזו נדרשת כדי לגשת לקוד של מודול התכונה המשאבים.
מתקין המכשיר מתקין כרגע את המודול. מעדכנים את סרגל ההתקדמות. לרוב, המצב הזה קצר.
מותקן המודול מותקן במכשיר. קוד גישה ומשאב במודול כדי להמשיך בתהליך שעובר המשתמש.

אם המודול מיועד לאפליקציה ללא התקנה ל-Android שפועלת ב-Android 8.0 (רמת API 26) ומעלה, עליך להשתמש ב-splitInstallHelper כדי לעדכן את רכיבי האפליקציה במודול החדש.

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

קבלת אישור מהמשתמש

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

Kotlin

override fun onSessionStateUpdate(state: SplitInstallSessionState) {
    if (state.status() == SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION) {
        // Displays a confirmation for the user to confirm the request.
        splitInstallManager.startConfirmationDialogForResult(
          state,
          // an activity result launcher registered via registerForActivityResult
          activityResultLauncher)
    }
    ...
 }

Java

@Override void onSessionStateUpdate(SplitInstallSessionState state) {
    if (state.status() == SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION) {
        // Displays a confirmation for the user to confirm the request.
        splitInstallManager.startConfirmationDialogForResult(
          state,
          // an activity result launcher registered via registerForActivityResult
          activityResultLauncher);
    }
    ...
 }

ניתן לרשום מרכז אפליקציות לתוצאת פעילות באמצעות ActivityResultContracts.StartIntentSenderForResult חוזה. למידע נוסף, אפשר לעיין בקטע Activity result APIs (ממשקי API של תוצאות פעילות).

סטטוס הבקשה מתעדכן בהתאם לתגובת המשתמש:

  • אם המשתמש יאשר את האישור, סטטוס הבקשה ישתנה ל- PENDING וההורדה ממשיכה.
  • אם המשתמש ידחה את האישור, סטטוס הבקשה ישתנה ל- CANCELED
  • אם המשתמש לא מבצע בחירה לפני שתיבת הדו-שיח מושמדת, סטטוס הבקשה נשאר REQUIRES_USER_CONFIRMATION. האפליקציה יכולה לבקש מהמשתמש שוב להשלים את הבקשה.

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

Kotlin

registerForActivityResult(StartIntentSenderForResult()) { result: ActivityResult -> {
        // Handle the user's decision. For example, if the user selects "Cancel",
        // you may want to disable certain functionality that depends on the module.
    }
}

Java

registerForActivityResult(
    new ActivityResultContracts.StartIntentSenderForResult(),
    new ActivityResultCallback<ActivityResult>() {
        @Override
        public void onActivityResult(ActivityResult result) {
            // Handle the user's decision. For example, if the user selects "Cancel",
            // you may want to disable certain functionality that depends on the module.
        }
    });

ביטול בקשת התקנה

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

Kotlin

splitInstallManager
    // Cancels the request for the given session ID.
    .cancelInstall(mySessionId)

Java

splitInstallManager
    // Cancels the request for the given session ID.
    .cancelInstall(mySessionId);

גישה למודולים

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

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

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

הפעלת SplitCompat

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

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

הצהרה על SplitCompatApplication במניפסט

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

<application
    ...
    android:name="com.google.android.play.core.splitcompat.SplitCompatApplication">
</application>

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

הפעלת SplitCompat בזמן ריצה

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

אם יש לכם מחלקה מותאמת אישית של Application, אני רוצה להרחיב SplitCompatApplication כדי להפעיל את SplitCompat לאפליקציה שלך, כפי שמוצג בהמשך:

Kotlin

class MyApplication : SplitCompatApplication() {
    ...
}

Java

public class MyApplication extends SplitCompatApplication {
    ...
}

SplitCompatApplication פשוט מבטל את ContextWrapper.attachBaseContext() כדי לכלול את SplitCompat.install(Context applicationContext). אם לא תעשו זאת אני רוצה שכיתת Application להרחיב את SplitCompatApplication, אפשר לבטל את attachBaseContext() באופן ידני, באופן הבא:

Kotlin

override fun attachBaseContext(base: Context) {
    super.attachBaseContext(base)
    // Emulates installation of future on demand modules using SplitCompat.
    SplitCompat.install(this)
}

Java

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    // Emulates installation of future on demand modules using SplitCompat.
    SplitCompat.install(this);
}

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

Kotlin

override fun attachBaseContext(base: Context) {
    super.attachBaseContext(base)
    if (!InstantApps.isInstantApp(this)) {
        SplitCompat.install(this)
    }
}

Java

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    if (!InstantApps.isInstantApp(this)) {
        SplitCompat.install(this);
    }
}

הפעלת SplitCompat לפעילויות מודול

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

Kotlin

override fun attachBaseContext(base: Context) {
    super.attachBaseContext(base)
    // Emulates installation of on demand modules using SplitCompat.
    SplitCompat.installActivity(this)
}

Java

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    // Emulates installation of on demand modules using SplitCompat.
    SplitCompat.installActivity(this);
}

גישה לרכיבים שהוגדרו במודולים של מאפיינים

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

תוכלו להשיק פעילויות שהוגדרו במודולים של תכונות באמצעות startActivity() לאחר ההפעלה של SplitCompat.

Kotlin

startActivity(Intent()
  .setClassName("com.package", "com.package.module.MyActivity")
  .setFlags(...))

Java

startActivity(new Intent()
  .setClassName("com.package", "com.package.module.MyActivity")
  .setFlags(...));

הפרמטר הראשון ל-setClassName הוא שם החבילה של האפליקציה ו הפרמטר השני הוא השם המלא של המחלקה (class) של הפעילות.

כשיש לכם פעילות במודול תכונה שהורדתם על פי דרישה, אתם צריכים מפעילים את SplitCompat בפעילות.

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

ניתן להפעיל שירותים שהוגדרו במודולים של תכונות באמצעות startService() לאחר ההפעלה של SplitCompat.

Kotlin

startService(Intent()
  .setClassName("com.package", "com.package.module.MyService")
  .setFlags(...))

Java

startService(new Intent()
  .setClassName("com.package", "com.package.module.MyService")
  .setFlags(...));

ייצוא רכיב שהוגדר במודול של תכונות

אין לכלול רכיבי Android שיוצאו במודולים אופציונליים.

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

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

אם אתם צריכים רכיב מיוצא ואתם רוצים שהתוכן שלו יהיה כדאי ליישם דפוס של שרת proxy. אפשר לעשות זאת על ידי הוספת רכיב שמיוצא שרת proxy בבסיס. כשניגשים אליו, רכיב ה-Proxy יכול לבדוק את נוכחות המודול מכיל את התוכן. אם המודול קיים, שרת ה-proxy הרכיב יכול להפעיל את הרכיב הפנימי מהמודול באמצעות Intent, הכוונה להעברת הכוונה מאפליקציית המתקשר/ת. אם המודול לא נמצא, הרכיב יכול להוריד את המודול או להחזיר הודעת שגיאה מתאימה אפליקציית 'מתקשרים'.

גישה לקוד ולמשאבים ממודולים מותקנים

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

גישה לקוד ממודול אחר

גישה לקוד הבסיס ממודול

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

קוד מודול גישה ממודול אחר

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

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

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

Kotlin

// In the base module
interface MyInterface {
  fun hello(): String
}

// In the feature module
object MyInterfaceImpl : MyInterface {
  override fun hello() = "Hello"
}

// In the base module, where we want to access the feature module code
val stringFromModule = (Class.forName("com.package.module.MyInterfaceImpl")
    .kotlin.objectInstance as MyInterface).hello();

Java

// In the base module
public interface MyInterface {
  String hello();
}

// In the feature module
public class MyInterfaceImpl implements MyInterface {
  @Override
  public String hello() {
    return "Hello";
  }
}

// In the base module, where we want to access the feature module code
String stringFromModule =
   ((MyInterface) Class.forName("com.package.module.MyInterfaceImpl").getConstructor().newInstance()).hello();

גישה למשאבים ולנכסים ממודול אחר

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

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

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

מומלץ להשתמש ב-ReLinker כדי לטעון את כל בספריות המקוריות שלכם כשאתם משתמשים במסירה לפי דרישה של מודולים של מאפיינים. ReLinker פותר בעיה בטעינה של ספריות נייטיב לאחר ההתקנה של של התכונה. מידע נוסף על ReLinker זמין טיפים ל-Android JNI.

טעינת קוד מקורי ממודול אופציונלי

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

אם משתמשים ב-System.loadLibrary() כדי לטעון את קוד ה-Native תלויה בספרייה אחרת במודול, עליך באופן ידני לטעון את הספרייה האחרת הראשונה. אם אתם משתמשים ב-ReLinker, הפעולה המקבילה היא Relinker.recursively().loadLibrary()

אם משתמשים ב-dlopen() בקוד נייטיב כדי לטעון ספרייה שהוגדרה מודול אופציונלי, הוא לא יפעל עם נתיבים יחסיים של ספרייה. הפתרון הטוב ביותר הוא לאחזר את הנתיב המוחלט של הספרייה מקוד Java דרך ClassLoader.findLibrary(), ולאחר מכן להשתמש בה בשיחה שלך עם dlopen(). יש לעשות זאת לפני הזנת קוד ה-Native או להשתמש בקריאת JNI ב-Java.

גישה לאפליקציות ללא התקנה מותקנות ל-Android

אחרי שמודול של אפליקציה ללא התקנה ל-Android מדווח כ-INSTALLED, אפשר לגשת אליו את הקוד והמשאבים באמצעות אפליקציה שעברה רענון הקשר. א' ההקשר שהאפליקציה יוצרת לפני ההתקנה של מודול (לדוגמה, שכבר מאוחסן במשתנה) לא מכיל את התוכן של הפונקציה של מודל טרנספורמר. אבל הקשר חדש כן משפיע – אפשר לקבל את ההקשר הזה, למשל createPackageContext

Kotlin

// Generate a new context as soon as a request for a new module
// reports as INSTALLED.
override fun onStateUpdate(state: SplitInstallSessionState ) {
    if (state.sessionId() == mySessionId) {
        when (state.status()) {
            ...
            SplitInstallSessionStatus.INSTALLED -> {
                val newContext = context.createPackageContext(context.packageName, 0)
                // If you use AssetManager to access your app’s raw asset files, you’ll need
                // to generate a new AssetManager instance from the updated context.
                val am = newContext.assets
            }
        }
    }
}

Java

// Generate a new context as soon as a request for a new module
// reports as INSTALLED.
@Override
public void onStateUpdate(SplitInstallSessionState state) {
    if (state.sessionId() == mySessionId) {
        switch (state.status()) {
            ...
            case SplitInstallSessionStatus.INSTALLED:
                Context newContext = context.createPackageContext(context.getPackageName(), 0);
                // If you use AssetManager to access your app’s raw asset files, you’ll need
                // to generate a new AssetManager instance from the updated context.
                AssetManager am = newContext.getAssets();
        }
    }
}

אפליקציות ללא התקנה ל-Android ב-Android מגרסה 8.0 ואילך

כששולחים בקשה למודול על פי דרישה לאפליקציה ללא התקנה ל-Android ב-Android 8.0 (רמת API 26) ומעלה, לאחר בקשת התקנה מדווחת כ-INSTALLED, תצטרכו לעדכן את האפליקציה בהקשר של המודול החדש באמצעות קריאה SplitInstallHelper.updateAppInfo(Context context). אחרת, האפליקציה עדיין לא מודעת לקוד המודול ומשאבים. לאחר עדכון המטא-נתונים של האפליקציה, עליך לטעון את המודול את התוכן במהלך האירוע הבא בשרשור הראשי על ידי הפעלה של Handler, כפי שמוצג בהמשך:

Kotlin

override fun onStateUpdate(state: SplitInstallSessionState ) {
    if (state.sessionId() == mySessionId) {
        when (state.status()) {
            ...
            SplitInstallSessionStatus.INSTALLED -> {
                // You need to perform the following only for Android Instant Apps
                // running on Android 8.0 (API level 26) and higher.
                if (BuildCompat.isAtLeastO()) {
                    // Updates the app’s context with the code and resources of the
                    // installed module.
                    SplitInstallHelper.updateAppInfo(context)
                    Handler().post {
                        // Loads contents from the module using AssetManager
                        val am = context.assets
                        ...
                    }
                }
            }
        }
    }
}

Java

@Override
public void onStateUpdate(SplitInstallSessionState state) {
    if (state.sessionId() == mySessionId) {
        switch (state.status()) {
            ...
            case SplitInstallSessionStatus.INSTALLED:
            // You need to perform the following only for Android Instant Apps
            // running on Android 8.0 (API level 26) and higher.
            if (BuildCompat.isAtLeastO()) {
                // Updates the app’s context with the code and resources of the
                // installed module.
                SplitInstallHelper.updateAppInfo(context);
                new Handler().post(new Runnable() {
                    @Override public void run() {
                        // Loads contents from the module using AssetManager
                        AssetManager am = context.getAssets();
                        ...
                    }
                });
            }
        }
    }
}

טעינת ספריות C/C++

אם רוצים לטעון ספריות C/C++ ממודול שהמכשיר כבר טען שהורדתם באפליקציה ללא התקנה, צריך להשתמש ב- SplitInstallHelper.loadLibrary(Context context, String libName) כפי שמוצג בהמשך:

Kotlin

override fun onStateUpdate(state: SplitInstallSessionState) {
    if (state.sessionId() == mySessionId) {
        when (state.status()) {
            SplitInstallSessionStatus.INSTALLED -> {
                // Updates the app’s context as soon as a module is installed.
                val newContext = context.createPackageContext(context.packageName, 0)
                // To load C/C++ libraries from an installed module, use the following API
                // instead of System.load().
                SplitInstallHelper.loadLibrary(newContext, my-cpp-lib)
                ...
            }
        }
    }
}

Java

public void onStateUpdate(SplitInstallSessionState state) {
    if (state.sessionId() == mySessionId) {
        switch (state.status()) {
            case SplitInstallSessionStatus.INSTALLED:
                // Updates the app’s context as soon as a module is installed.
                Context newContext = context.createPackageContext(context.getPackageName(), 0);
                // To load C/C++ libraries from an installed module, use the following API
                // instead of System.load().
                SplitInstallHelper.loadLibrary(newContext, my-cpp-lib);
                ...
        }
    }
}

מגבלות ידועות

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

ניהול המודולים המותקנים

כדי לבדוק אילו מודולים של תכונות מותקנים כרגע במכשיר: אפשר להתקשר SplitInstallManager.getInstalledModules() שתחזיר Set<String> של שמות המודולים המותקנים, כפי שמוצג שלמטה.

Kotlin

val installedModules: Set<String> = splitInstallManager.installedModules

Java

Set<String> installedModules = splitInstallManager.getInstalledModules();

הסרת מודולים

אפשר לבקש מהמכשיר להסיר מודולים על ידי הפעלה SplitInstallManager.deferredUninstall(List<String> moduleNames) כפי שמוצג בהמשך.

Kotlin

// Specifies two feature modules for deferred uninstall.
splitInstallManager.deferredUninstall(listOf("pictureMessages", "promotionalFilters"))

Java

// Specifies two feature modules for deferred uninstall.
splitInstallManager.deferredUninstall(Arrays.asList("pictureMessages", "promotionalFilters"));

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

הורדת משאבים נוספים בשפות

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

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

Kotlin

// Captures the user’s preferred language and persists it
// through the app’s SharedPreferences.
sharedPrefs.edit().putString(LANGUAGE_SELECTION, "fr").apply()
...

// Creates a request to download and install additional language resources.
val request = SplitInstallRequest.newBuilder()
        // Uses the addLanguage() method to include French language resources in the request.
        // Note that country codes are ignored. That is, if your app
        // includes resources for “fr-FR” and “fr-CA”, resources for both
        // country codes are downloaded when requesting resources for "fr".
        .addLanguage(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
        .build()

// Submits the request to install the additional language resources.
splitInstallManager.startInstall(request)

Java

// Captures the user’s preferred language and persists it
// through the app’s SharedPreferences.
sharedPrefs.edit().putString(LANGUAGE_SELECTION, "fr").apply();
...

// Creates a request to download and install additional language resources.
SplitInstallRequest request =
    SplitInstallRequest.newBuilder()
        // Uses the addLanguage() method to include French language resources in the request.
        // Note that country codes are ignored. That is, if your app
        // includes resources for “fr-FR” and “fr-CA”, resources for both
        // country codes are downloaded when requesting resources for "fr".
        .addLanguage(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
        .build();

// Submits the request to install the additional language resources.
splitInstallManager.startInstall(request);

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

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

Kotlin

splitInstallManager.deferredLanguageInstall(
    Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))

Java

splitInstallManager.deferredLanguageInstall(
    Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)));

גישה למשאבי שפה שהורדו

כדי לקבל גישה למשאבי שפה שהורדת, האפליקציה שלך צריכה להריץ את אמצעי תשלום SplitCompat.installActivity() בתוך ה-method attachBaseContext() של כל פעילות שדורשת גישה למשאבים האלה, כפי שמוצג בהמשך.

Kotlin

override fun attachBaseContext(base: Context) {
  super.attachBaseContext(base)
  SplitCompat.installActivity(this)
}

Java

@Override
protected void attachBaseContext(Context base) {
  super.attachBaseContext(base);
  SplitCompat.installActivity(this);
}

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

Kotlin

override fun attachBaseContext(base: Context) {
  val configuration = Configuration()
  configuration.setLocale(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
  val context = base.createConfigurationContext(configuration)
  super.attachBaseContext(context)
  SplitCompat.install(this)
}

Java

@Override
protected void attachBaseContext(Context base) {
  Configuration configuration = new Configuration();
  configuration.setLocale(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)));
  Context context = base.createConfigurationContext(configuration);
  super.attachBaseContext(context);
  SplitCompat.install(this);
}

כדי שהשינויים האלה ייכנסו לתוקף, צריך ליצור מחדש את הפעילות לאחר התקנת השפה החדשה ומוכנה לשימוש. אפשר להשתמש אמצעי תשלום אחד (Activity#recreate()).

Kotlin

when (state.status()) {
  SplitInstallSessionStatus.INSTALLED -> {
      // Recreates the activity to load resources for the new language
      // preference.
      activity.recreate()
  }
  ...
}

Java

switch (state.status()) {
  case SplitInstallSessionStatus.INSTALLED:
      // Recreates the activity to load resources for the new language
      // preference.
      activity.recreate();
  ...
}

הסרת משאבי שפות נוספים

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

Kotlin

val installedLanguages: Set<String> = splitInstallManager.installedLanguages

Java

Set<String> installedLanguages = splitInstallManager.getInstalledLanguages();

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

Kotlin

splitInstallManager.deferredLanguageUninstall(
    Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))

Java

splitInstallManager.deferredLanguageUninstall(
    Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)));

בדיקה מקומית של התקנות של מודול

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

בדף הזה נסביר איך לפרוס את חבילות ה-APK המפוצלות של האפליקציה למכשיר הבדיקה כדי ש-Play Feature Delivery משתמש באופן אוטומטי בחבילות ה-APK האלה כדי לדמות בקשות, הורדה, ולהתקין מודולים מחנות Play.

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

בניית קבוצה של חבילות APK

אם עדיין לא עשית זאת, עליך ליצור את חבילות ה-APK המפוצלות של האפליקציה באופן הבא:

  1. אפשר ליצור App Bundle לאפליקציה באחת מהשיטות הבאות:
  2. השתמש ב-bundletool כדי ליצור קבוצה של חבילות APK לכל המכשירים באמצעות הפקודה הבאה:

    bundletool build-apks --local-testing
      --bundle my_app.aab
      --output my_app.apks
    

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

פורסים את האפליקציה במכשיר

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

bundletool install-apks --apks my_app.apks

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

הדמיה של שגיאת רשת

כדי לדמות התקנות של מודולים מחנות Play, ספריית העברת התכונות של Play משתמשת ב- היא חלופה ל-SplitInstallManager, שנקראת FakeSplitInstallManager, כדי לבקש את המודול. כשמשתמשים ב-bundletool עם הדגל --local-testing כדי ליצור קבוצת חבילות APK ולפרוס אותן במכשיר הבדיקה, כוללים מטא-נתונים עם הוראה לספריית העברת התכונות ב-Play לעבור באופן אוטומטי קריאות ל-API של האפליקציה שלך כדי להפעיל FakeSplitInstallManager, במקום SplitInstallManager.

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

Kotlin

// Creates an instance of FakeSplitInstallManager with the app's context.
val fakeSplitInstallManager = FakeSplitInstallManagerFactory.create(context)
// Tells Play Feature Delivery Library to force the next module request to
// result in a network error.
fakeSplitInstallManager.setShouldNetworkError(true)

Java

// Creates an instance of FakeSplitInstallManager with the app's context.
FakeSplitInstallManager fakeSplitInstallManager =
    FakeSplitInstallManagerFactory.create(context);
// Tells Play Feature Delivery Library to force the next module request to
// result in a network error.
fakeSplitInstallManager.setShouldNetworkError(true);