מודולים של תכונות מאפשרים להפריד תכונות ומשאבים מסוימים מהמודול הבסיסי של האפליקציה ולכלול אותם ב-App Bundle. באמצעות Play Feature Delivery, המשתמשים יכולים, למשל, להוריד ולהתקין את הרכיבים האלה על פי דרישה מאוחר יותר, אחרי שהם כבר התקינו את קובץ ה-APK הבסיסי של האפליקציה.
לדוגמה, נניח שאפליקציית שליחת הודעות טקסט כוללת פונקציונליות לצילום ולשליחה של הודעות תמונה, אבל רק אחוז קטן מהמשתמשים שולחים הודעות תמונה. יכול להיות שיהיה הגיוני לכלול את התכונה 'הודעות תמונה' כמודול תכונה שניתן להורדה. כך ההורדה הראשונית של האפליקציה תהיה קטנה יותר לכל המשתמשים, ורק המשתמשים ששולחים הודעות תמונה יצטרכו להוריד את הרכיב הנוסף.
חשוב לזכור שמודולריזציה מהסוג הזה מצריך יותר מאמץ ולפעמים גם ארגון מחדש של הקוד (Refactoring) באפליקציה שלכם. לכן חשוב לבדוק היטב לאילו תכונות של האפליקציה תהיה הכי הרבה זמינות אם הן יהיו זמינות על פי דרישה למשתמשים על פי דרישה. כדי להבין טוב יותר את התרחישים לדוגמה ואת ההנחיות האופטימליים לשימוש בתכונות על פי דרישה, כדאי לקרוא את המאמר שיטות מומלצות בנושא חוויית המשתמש להצגת מודעות על פי דרישה.
אם אתם רוצים להפוך בהדרגה את תכונות האפליקציה למודולריות לאורך זמן, בלי להפעיל אפשרויות הפצה מתקדמות כמו הפצה על פי דרישה, תוכלו במקום זאת להגדיר הפצה בזמן ההתקנה.
בדף הזה מוסבר איך להוסיף מודול של תכונה לפרויקט האפליקציה ולהגדיר אותו כך שיפעל על פי דרישה. לפני שמתחילים, חשוב לוודא שאתם משתמשים ב-Android Studio 3.5 ואילך וב-Android Gradle Plugin 3.5.0 ואילך.
הגדרת מודול חדש למסירה על פי דרישה
הדרך הקלה ביותר ליצור מודול תכונות חדש היא להשתמש ב-Android Studio 3.5 ואילך. מכיוון שמודולים של תכונות תלויים מעצם טבעם במודול האפליקציה הבסיסי, אפשר להוסיף אותם רק לפרויקטים קיימים של אפליקציות.
כדי להוסיף מודול תכונה לפרויקט האפליקציה באמצעות Android Studio:
- אם עדיין לא עשיתם זאת, עליכם לפתוח את פרויקט האפליקציה בסביבת הפיתוח המשולבת (IDE).
- בסרגל התפריטים, בוחרים באפשרות קובץ > חדש > מודול חדש.
- בתיבת הדו-שיח Create New Module בוחרים באפשרות Dynamic Feature Module ולוחצים על Next.
- בקטע Configure your new module (הגדרת המודול החדש), ממלאים את הפרטים הבאים:
- בתפריט הנפתח, בוחרים את Base applicationModule של פרויקט האפליקציה.
- מציינים שם מודול. סביבת הפיתוח המשולבת משתמשת בשם הזה כדי לזהות את המודול כפרויקט משנה של Gradle בקובץ ההגדרות של Gradle. כשבונים את ה-App Bundle, Gradle משתמשת ברכיב האחרון של שם הפרויקט המשני כדי להחדיר את המאפיין
<manifest split>
למניפסט של מודול התכונה. - מציינים את שם החבילה של המודול. כברירת מחדל, Android Studio מציע שם חבילה שמשלב את שם החבילה ברמה הבסיסית של המודול הבסיסי ואת שם המודול שציינתם בשלב הקודם.
- בוחרים את רמת ה-API המינימלית שרוצים שהמודול יתמוך בה. הערך הזה צריך להיות זהה לערך של מודול הבסיס.
- לוחצים על הבא.
בקטע אפשרויות להורדת מודול, מבצעים את הפעולות הבאות:
מציינים את כותרת המודול באמצעות 50 תווים לכל היותר. הפלטפורמה משתמשת בכותרת כדי לזהות את המודול בפני המשתמשים, למשל, כשאתם מאשרים אם המשתמש רוצה להוריד את המודול. לכן המודול הבסיסי של האפליקציה חייב לכלול את כותרת המודול בתור משאב מחרוזת, שאותו אפשר לתרגם. כשיוצרים את המודול באמצעות Android Studio, סביבת הפיתוח המשולבת מוסיפה בשבילכם את משאב המחרוזת למודול הבסיסי ומזינה את הרשומה הבאה במניפסט של מודול התכונה:
<dist:module ... dist:title="@string/feature_title"> </dist:module>
בתפריט הנפתח, בקטע Install-time Include (הכללה של זמן התקנה), בוחרים באפשרות Do not include מודול at installation-time (לא לכלול את המודול בזמן ההתקנה). כדי לשקף את הבחירה שלכם, Android Studio מזין את הפרטים הבאים במניפסט של המודול:
<dist:module ... > <dist:delivery> <dist:on-demand/> </dist:delivery> </dist:module>
מסמנים את התיבה לצד Fusing אם רוצים שהמודול הזה יהיה זמין למכשירים עם Android 4.4 (רמת API 20) ומטה וכלול בחבילות APK מרובות. המשמעות היא שאפשר להפעיל התנהגות על פי דרישה למודול הזה ולהשבית את ההיתוך כדי להשמיט אותו ממכשירים שלא תומכים בהורדה והתקנה של חבילות APK מפוצלות. כדי לשקף את הבחירה שלכם, Android Studio מזין את הפרטים הבאים במניפסט של המודול:
<dist:module ...> <dist:fusing dist:include="true | false" /> </dist:module>
לוחצים על סיום.
אחרי ש-Android Studio יסיים ליצור את המודול, תוכלו לבדוק את התוכן שלו בחלונית Project (בוחרים באפשרות View > Tool Windows > Project בסרגל התפריטים). קוד ברירת המחדל, המשאבים והארגון צריכים להיות דומים לאלה של מודול האפליקציה הרגיל.
בשלב הבא, תצטרכו להטמיע את הפונקציונליות של התקנה על פי דרישה באמצעות הספרייה של Play Feature Delivery.
הכללה של ספריית Play Feature Delivery בפרויקט
לפני שמתחילים, צריך קודם להוסיף את ה-Play Feature Delivery Library לפרויקט.
בקשת מודול על פי דרישה
כשהאפליקציה צריכה להשתמש במודול תכונות, היא יכולה לבקש אחד כשהוא בחזית באמצעות הכיתה 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 מעדכן אותו באופן אוטומטי. כלומר, כשאתם מעלים גרסה חדשה של חבילת האפליקציות, הפלטפורמה מעדכנת את כל חבילות ה-APK שהותקנו וששייכות לאפליקציה. למידע נוסף, קראו את המאמר ניהול עדכוני האפליקציות.
כדי לקבל גישה לקוד ולמשאבים של המודול, האפליקציה צריכה להפעיל את SplitCompat. חשוב לזכור ש-SplitCompat לא נדרש לאפליקציות Android Instant.
דחיית ההתקנה של מודולים על פי דרישה
אם אתם לא צריכים שהאפליקציה תוריד ותתקין מודול על פי דרישה באופן מיידי, תוכלו לדחות את ההתקנה למועד שבו האפליקציה תפעל ברקע. לדוגמה, אם תרצו לטעון מראש תוכן שיווקי להשקה מאוחרת יותר של האפליקציה.
תוכלו לציין את המודול להורדה מאוחר יותר באמצעות השיטה deferredInstall()
, כפי שמוצג בהמשך. בניגוד ל-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()
.
לפני שתוכלו להתחיל לקבל עדכונים לגבי בקשת ההתקנה, אתם צריכים לרשום מאזין ולקבל את מזהה הסשן של הבקשה, כפי שמוצג בהמשך.
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 של המשתמש. | אם למשתמש אין גישה למודול, מודיעים לו. |
בקשה לא חוקית | 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 | השירות שאחראי לטיפול בבקשה הפסיק לפעול. | אפשר לנסות לשלוח את הבקשה שוב.
|
INSUFFICIENT_STORAGE | אין במכשיר מספיק נפח אחסון פנוי כדי להתקין את מודול התכונה. | להודיע למשתמש שאין לו מספיק נפח אחסון כדי להתקין את התכונה הזו. |
SPLITCOMPAT_VERIFICATION_ERROR, SPLITCOMPAT_EMULATION_ERROR, SPLITCOMPAT_COPY_ERROR | לא ניתן היה לטעון את מודול התכונה ב-SplitCompat. | השגיאות האלה אמורות להיפתר באופן אוטומטי אחרי ההפעלה הבאה של האפליקציה. |
PLAY_STORE_NOT_FOUND | אפליקציית Play Store לא מותקנת במכשיר. | צריך ליידע את המשתמש שהאפליקציה של חנות Play נדרשת כדי להוריד את התכונה הזו. |
APP_NOT_OWNED | האפליקציה לא הותקנה על ידי Google Play ואי אפשר להוריד את התכונה. השגיאה הזו יכולה לקרות רק בהתקנות שנדחו. | אם רוצים שהמשתמש יוריד את האפליקציה מ-Google Play, צריך להשתמש ב-startInstall() , שיכול לקבל את אישור המשתמש הנדרש. |
שגיאה פנימית | אירעה שגיאה פנימית ב-Play Store. | מנסים שוב לשלוח את הבקשה. |
אם משתמש מבקש להוריד מודול לפי דרישה ומוצגת הודעת שגיאה, כדאי להציג למשתמש תיבת דו-שיח עם שתי אפשרויות: Try a שוב (ניסיון חוזר על הבקשה) ו-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 Instant שפועלת ב-Android 8.0 (רמת API 26) ואילך, צריך להשתמש ב- |
נכשל | הבקשה נכשלה לפני שהמודול הותקן במכשיר. | מבקשים מהמשתמש לנסות שוב את הבקשה או לבטל אותה. |
מתבצע ביטול | המכשיר בתהליך ביטול של הבקשה. | מידע נוסף זמין בקטע ביטול בקשת התקנה. |
בוטלה | הבקשה בוטלה. |
קבלת אישור מהמשתמש
במקרים מסוימים, יכול להיות שמערכת Google Play תדרוש אישור מהמשתמשים לפני שתספק בקשת הורדה. לדוגמה, אם האפליקציה לא הותקנה על ידי Google Play או אם אתם מנסים להוריד נפח גדול באמצעות חבילת הגלישה. במקרים כאלה, סטטוס הבקשה מדווח על 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
. האפליקציה יכולה לבקש מהמשתמש להשלים את הבקשה שוב.
כדי לקבל קריאה חוזרת (callback) עם תגובת המשתמש, אפשר לשנות את Activity resultCallback באופן הבא.
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 Library גם באפליקציה וגם בכל פעילות במודולים של התכונות שהאפליקציה שלכם מורידת.
עם זאת, שימו לב שהפלטפורמה כפופה למגבלות הבאות על גישה לתוכן של מודול, למשך זמן מסוים (בימים, במקרים מסוימים) לאחר הורדת המודול:
- הפלטפורמה לא יכולה להחיל רשומות מניפסט חדשות שנוספו על ידי המודול.
- הפלטפורמה לא יכולה לגשת למשאבי המודול עבור רכיבי ממשק המשתמש של המערכת, כמו התראות. אם אתם צריכים להשתמש במשאבים כאלה באופן מיידי, כדאי לכלול אותם במודול הבסיסי של האפליקציה.
הפעלת 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 לכל פעילות שהאפליקציה מורידה במודול של התכונות. כדי לעשות את זה, צריך להשתמש ב-method 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); }
גישה לרכיבים שהוגדרו במודולים של מאפיינים
הפעלת פעילות שמוגדרת במודול תכונות
אחרי שמפעילים את SplitCompat, אפשר להפעיל פעילויות שהוגדרו במודולים של תכונות באמצעות startActivity()
.
Kotlin
startActivity(Intent() .setClassName("com.package", "com.package.module.MyActivity") .setFlags(...))
Java
startActivity(new Intent() .setClassName("com.package", "com.package.module.MyActivity") .setFlags(...));
הפרמטר הראשון של setClassName
הוא שם החבילה של האפליקציה, והפרמטר השני הוא שם הכיתה המלא של הפעילות.
כשיש פעילות במודול של מאפיינים שהורדתם לפי דרישה, צריך להפעיל את SplitCompat בפעילות.
הפעלת שירות שמוגדר במודול של תכונות
אחרי שמפעילים את SplitCompat, אפשר להפעיל שירותים שמוגדרים במודולים של תכונות באמצעות startService()
.
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.
כדי לעשות זאת, מוסיפים ל-base רכיב proxy שיוצאו. כשנכנסים לרכיב ה-proxy, הוא יכול לבדוק אם המודול שמכיל את התוכן נמצא. אם המודול קיים, רכיב שרת ה-proxy יכול להפעיל את הרכיב הפנימי מהמודול באמצעות Intent
ולהעביר את הכוונה מאפליקציית הקריאה החוזרת. אם המודול לא נמצא, הרכיב יכול להוריד את המודול או להחזיר הודעת שגיאה מתאימה לאפליקציית הקריאה.
גישה לקוד ולמשאבים ממודולים מותקנים
אם מפעילים את SplitCompat בהקשר של האפליקציה הבסיסית ובשביל הפעילויות במודול התכונות, אפשר להשתמש בקוד ובמשאבים ממודול של מאפיינים כאילו היו חלק מ-APK הבסיס, לאחר התקנת המודול האופציונלי.
גישה לקוד ממודול אחר
גישה לקוד הבסיס מתוך מודול
מודולים אחרים יכולים להשתמש ישירות בקוד שנמצא בתוך מודול הבסיס. לא צריך לבצע שום פעולה מיוחדת. אפשר פשוט לייבא את הכיתות ולהשתמש בהן.
גישה לקוד של מודול מתוך מודול אחר
אי אפשר לגשת באופן סטטי לאובייקט או למחלקה בתוך מודול ממודול אחר, אבל ניתן לגשת אליו באופן עקיף באמצעות השתקפות.
חשוב לשים לב לתדירות שבה זה קורה, בגלל עלויות הביצועים של ההחזרה. בתרחישי שימוש מורכבים, כדאי להשתמש במסגרות להזרקת יחסי תלות כמו 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 מתקן בעיה בטעינה של ספריות Native אחרי התקנת מודול תכונות. מידע נוסף על ReLinker זמין במאמר טיפים ל-JNI ב-Android.
טעינת קוד מקורי ממודול אופציונלי
אחרי שמתקינים את הפיצול, מומלץ לטעון את הקוד המקורי באמצעות ReLinker. באפליקציות מיידיות צריך להשתמש בשיטה המיוחדת הזו.
אם אתם משתמשים ב-System.loadLibrary()
כדי לטעון את הקוד המקורי, והספרייה המקומית שלכם תלויה בספרייה אחרת במודול, תחילה תצטרכו לטעון את הספרייה האחרת באופן ידני.
אם משתמשים ב-ReLinker, הפעולה המקבילה היא Relinker.recursively().loadLibrary()
.
אם משתמשים ב-dlopen()
בקוד נייטיב כדי לטעון ספרייה שמוגדרת במודול אופציונלי, היא לא תעבוד עם נתיבים יחסיים של ספריות.
הפתרון הטוב ביותר הוא לאחזר את הנתיב המוחלט של הספרייה מקוד Java דרך ClassLoader.findLibrary()
, ואז להשתמש בו בקריאה dlopen()
.
צריך לעשות זאת לפני שמזינים את הקוד המקורי, או להשתמש בקריאה ל-JNI מהקוד המקורי ל-Java.
גישה לאפליקציות ללא התקנה מותקנות ל-Android
אחרי שמופיע הדיווח INSTALLED
לגבי מודול של אפליקציה ללא התקנה ל-Android, אפשר לגשת לקוד ולמשאבים שלו באמצעות הקשר של האפליקציה המעודכן. הקשר שהאפליקציה יוצרת לפני התקנת מודול (למשל, שכבר מאוחסן במשתנה) לא מכיל את התוכן של המודול החדש. אבל הקשר חדש כן עוזר – אפשר לקבל אותו, למשל, באמצעות 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 בגרסה 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++ ממודול שכבר הורדתם למכשיר באפליקציית Instant, צריך להשתמש ב-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 ומטה.
- אי אפשר לשמור אובייקטים מסוג
ApplicationInfo
של Android, את התוכן שלהם או אובייקטים שמכילים אותם באפליקציה במטמון. תמיד צריך לאחזר את האובייקטים האלה לפי הצורך מההקשר של האפליקציה. שמירת אובייקטים כאלה במטמון עלולה לגרום לקריסת האפליקציה במהלך התקנת מודול תכונה.
ניהול המודולים המותקנים
כדי לבדוק אילו מודולים של תכונות מותקנים כרגע במכשיר, אפשר להפעיל את הפונקציה 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()
ולבדוק את התוצאה, כפי שמתואר בקטע הקודם.
הורדת משאבים נוספים לשפות
כשמשתמשים בחבילות אפליקציות, המכשירים מורידים רק את הקוד ואת המשאבים שנדרשים להפעלת האפליקציה. לכן, לגבי משאבי שפה, המכשיר של המשתמש מוריד רק את משאבי השפה של האפליקציה שתואמים לשפה אחת או יותר שנבחרה כרגע בהגדרות המכשיר.
אם אתם רוצים לתת לאפליקציה גישה למשאבי שפה נוספים, למשל כדי להטמיע בורר שפות באפליקציה, תוכלו להשתמש בספרייה של 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)));
גישה למשאבי שפה שהורדו
כדי לקבל גישה למשאבי השפה שהורדתם, האפליקציה צריכה להריץ את ה-method 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); }
כדי שהשינויים האלה ייכנסו לתוקף, צריך ליצור מחדש את הפעילות אחרי התקנת השפה החדשה ומוכנה לשימוש. אפשר להשתמש ב-method 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 Feature Delivery Library תוכלו לבדוק באופן מקומי את היכולת של האפליקציה לבצע את הפעולות הבאות, בלי להתחבר לחנות Play:
- שליחת בקשות להתקנת מודולים ומעקב אחריהן.
- טיפול בשגיאות התקנה.
- משתמשים ב-
SplitCompat
כדי לגשת למודולים.
בדף הזה נסביר איך לפרוס את חבילות ה-APK המפוצלות של האפליקציה במכשיר הבדיקה, כדי שמערכת Play Feature Delivery תשתמש בחבילות ה-APK האלה באופן אוטומטי כדי לדמות בקשה, הורדה והתקנה של מודולים מחנות Play.
אתם לא צריכים לשנות את הלוגיקה של האפליקציה, אבל אתם צריכים לעמוד בדרישות הבאות:
- מורידים ומתקינים את הגרסה האחרונה של
bundletool
. צריך אתbundletool
כדי ליצור קבוצה חדשה של חבילות APK שניתן להתקין מחבילת האפליקציה.
פיתוח קבוצה של חבילות APK
אם עדיין לא עשיתם זאת, עליכם ליצור את חבילות ה-APK המפוצלות של האפליקציה באופן הבא:
- אפשר ליצור App Bundle לאפליקציה באחת מהשיטות הבאות:
- משתמשים ב-Android Studio ובפלאגין של Android ל-Gradle כדי ליצור קובץ Android App Bundle ולחתום עליו.
- איך יוצרים את חבילת האפליקציה משורת הפקודה
משתמשים ב-
bundletool
כדי ליצור קבוצה של קובצי APK לכל הגדרות המכשיר באמצעות הפקודה הבאה:bundletool build-apks --local-testing --bundle my_app.aab --output my_app.apks
הדגל --local-testing
כולל מטא-נתונים במניפסטים של קובצי ה-APK, שמאפשרים לספריית Play Feature Delivery להשתמש בקובצי ה-APK המקומיים המפוצלים כדי לבדוק את התקנת המודולים של התכונות, בלי להתחבר לחנות Play.
פריסה של האפליקציה במכשיר
אחרי שיוצרים קבוצת חבילות APK באמצעות הדגל --local-testing
, משתמשים ב-bundletool
כדי להתקין את גרסת הבסיס של האפליקציה ולהעביר חבילות APK נוספות לאחסון המקומי של המכשיר. אפשר לבצע את שתי הפעולות באמצעות הפקודה הבאה:
bundletool install-apks --apks my_app.apks
עכשיו, כשמפעילים את האפליקציה ומשלימים את תהליך ההורדה וההתקנה של
מודול התכונות של Play, ספריית העברת התכונות של Play משתמשת בחבילות ה-APK שbundletool
הועברו לאחסון המקומי של המכשיר.
סימולציה של שגיאת רשת
כדי לדמות התקנות של מודולים מחנות Play, ספריית Play Feature Delivery משתמשת באפשרות חלופית ל-SplitInstallManager
, שנקראת FakeSplitInstallManager
, כדי לבקש את המודול. כשמשתמשים ב-bundletool
עם הדגל --local-testing
כדי ליצור קבוצה של חבילות APK ולפרוס אותן במכשיר הבדיקה, ה-build כולל מטא-נתונים שמנחים את ספריית Play Feature Delivery להעביר באופן אוטומטי את הקריאות ל-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);