מודולים של תכונות מאפשרים להפריד תכונות ומשאבים מסוימים מהמודול הבסיסי של האפליקציה ולכלול אותם ב-App Bundle. באמצעות הפצת פיצ'רים ב-Play, המשתמשים יכולים, למשל, להוריד ולהתקין את הרכיבים האלה מאוחר יותר על פי דרישה, אחרי שהם כבר התקינו את ה-APK הבסיסי של האפליקציה.
לדוגמה, נניח שיש אפליקציה להודעות טקסט שכוללת פונקציונליות לצילום ושליחה של הודעות תמונה, אבל רק אחוז קטן מהמשתמשים שולחים הודעות תמונה. יכול להיות שיהיה הגיוני לכלול את האפשרות לשלוח הודעות עם תמונות כמודול של תכונות שניתן להורדה. כך, ההורדה הראשונית של האפליקציה קטנה יותר לכל המשתמשים, ורק המשתמשים ששולחים הודעות עם תמונות צריכים להוריד את הרכיב הנוסף.
חשוב לזכור שסוג כזה של מודולריזציה דורש יותר מאמץ, ואולי גם ארגון מחדש של הקוד הקיים של האפליקציה. לכן, כדאי לשקול היטב אילו מהתכונות של האפליקציה יפיקו את התועלת הרבה ביותר מכך שיהיו זמינות למשתמשים על פי דרישה. כדי להבין טוב יותר את תרחישי השימוש האופטימליים ואת ההנחיות לתכונות על פי דרישה, קראו את השיטות המומלצות לחוויית משתמש (UX) למשלוח על פי דרישה.
אם אתם רוצים להפוך את תכונות האפליקציה למודולריות בהדרגה לאורך זמן, בלי להפעיל אפשרויות מתקדמות להצגת מודעות, כמו הצגת מודעות על פי דרישה, אתם יכולים להגדיר הצגת מודעות בזמן ההתקנה.
בדף הזה מוסבר איך להוסיף מודול של תכונות לפרויקט של האפליקציה ולהגדיר אותו להצגה על פי דרישה. לפני שמתחילים, חשוב לוודא שאתם משתמשים ב-Android Studio בגרסה 3.5 ואילך ובפלאגין של Android Gradle בגרסה 3.5.0 ואילך.
הגדרת מודול חדש להעברה על פי דרישה
הדרך הקלה ביותר ליצור מודול של תכונות חדש היא באמצעות Android Studio 3.5 ואילך. למודולים של תכונות יש תלות מובנית במודול הבסיסי של האפליקציה, ולכן אפשר להוסיף אותם רק לפרויקטים קיימים של אפליקציות.
כדי להוסיף מודול של תכונות לפרויקט של האפליקציה באמצעות Android Studio, פועלים לפי השלבים הבאים:
- אם עדיין לא עשיתם את זה, פותחים את פרויקט האפליקציה ב-IDE.
- בסרגל התפריטים, לוחצים על קובץ > חדש > מודול חדש.
- בתיבת הדו-שיח Create New Module, בוחרים באפשרות Dynamic Feature Module ולוחצים על Next.
- בקטע Configure your new module, מבצעים את הפעולות הבאות:
- בתפריט הנפתח, בוחרים את Base application module של פרויקט האפליקציה.
- מציינים שם מודול. ה-IDE משתמש בשם הזה כדי לזהות את המודול כפרויקט משנה של Gradle בקובץ ההגדרות של Gradle. כשיוצרים את ה-App Bundle, Gradle משתמש ברכיב האחרון של שם פרויקט המשנה כדי להוסיף את המאפיין
<manifest split>למניפסט של מודול התכונות. - מציינים את שם החבילה של המודול. כברירת מחדל, Android Studio מציע שם חבילה שמשלב את שם חבילת הבסיס של מודול הבסיס ואת שם המודול שציינתם בשלב הקודם.
- בוחרים את רמת ה-API המינימלית שהמודול צריך לתמוך בה. הערך הזה צריך להיות זהה לערך של מודול הבסיס.
- לוחצים על הבא.
בקטע Module Download Options, מבצעים את הפעולות הבאות:
מציינים את שם המודול (עד 50 תווים). הפלטפורמה משתמשת בשם הזה כדי לזהות את המודול למשתמשים, למשל, כדי לאשר אם המשתמש רוצה להוריד את המודול. לכן, המודול הבסיסי של האפליקציה חייב לכלול את שם המודול כמשאב מחרוזת, שאותו אפשר לתרגם. כשיוצרים את המודול באמצעות Android Studio, סביבת הפיתוח המשולבת (IDE) מוסיפה את משאב המחרוזת למודול הבסיס ומוסיפה את הרשומה הבאה למניפסט של מודול התכונות:
<dist:module ... dist:title="@string/feature_title"> </dist:module>בתפריט הנפתח בקטע Install-time inclusion, בוחרים באפשרות Do not include module at install-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".
הוספת הספרייה של "הפצת פיצ'רים ב-Play" לפרויקט
לפני שמתחילים, צריך להוסיף את ספריית הפצת הפיצ'רים ב-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(), כמו שמוצג בהמשך. בניגוד ל-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. בהנחיות בנושא חוויית המשתמש (UX) למשלוחים על פי דרישה מפורטות הצעות לטיפול במצבים כאלה בצורה חלקה מבחינת המשתמש.
מבחינת הקוד, צריך לטפל בכשלים בהורדה או בהתקנה של מודול באמצעות 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 | השירות שאחראי לטיפול בבקשה הושבת. | לנסות שוב לשלוח את הבקשה.
ה- |
| INSUFFICIENT_STORAGE | אין מספיק נפח אחסון פנוי במכשיר כדי להתקין את מודול התכונות. | הודעה למשתמש שאין לו מספיק נפח אחסון כדי להתקין את התכונה הזו. |
| SPLITCOMPAT_VERIFICATION_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. | לנסות שוב לשלוח את הבקשה. |
אם משתמש מבקש להוריד מודול על פי דרישה ומתרחשת שגיאה, כדאי להציג תיבת דו-שיח עם שתי אפשרויות: Try again (ניסיון חוזר של הבקשה) ו-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) ומעלה, צריך להשתמש ב- |
| הבדיקה הסתיימה בכישלון | הבקשה נכשלה לפני שהמודול הותקן במכשיר. | מבקשים מהמשתמש לנסות שוב או לבטל את הבקשה. |
| בתהליך ביטול | המכשיר בתהליך של ביטול הבקשה. | מידע נוסף זמין בקטע בנושא ביטול בקשת התקנה. |
| בוטלה | הבקשה בוטלה. |
קבלת אישור מהמשתמשים
במקרים מסוימים, יכול להיות ש-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. אפשר לעיין בממשקי API של תוצאות פעילות.
סטטוס הבקשה מתעדכן בהתאם לתגובת המשתמש:
- אם המשתמש מאשר את הבקשה, הסטטוס שלה משתנה ל-
PENDINGוההורדה ממשיכה. - אם המשתמש דוחה את האישור, סטטוס הבקשה משתנה ל-
CANCELED. - אם המשתמש לא בוחר אפשרות לפני שתיבת הדו-שיח נסגרת, סטטוס הבקשה נשאר
REQUIRES_USER_CONFIRMATION. האפליקציה יכולה להציג למשתמש בקשה נוספת להשלמת הפעולה.
כדי לקבל קריאה חוזרת (callback) עם התשובה של המשתמש, אפשר לשנות את ActivityResultCallback כמו שמוצג בהמשך.
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 לכל פעילות שהאפליקציה מורידה במודול של תכונות. כדי לעשות את זה, משתמשים ב-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 מיוצא בבסיס. כשניגשים לרכיב ה-proxy, הוא יכול לבדוק אם המודול שמכיל את התוכן קיים. אם המודול קיים, רכיב ה-proxy יכול להפעיל את הרכיב הפנימי מהמודול באמצעות Intent, ולהעביר את ה-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 ברכיב אחרי ההתקנה של מודול התכונות.
טעינת קוד Native באפליקציה באמצעות העברה על פי דרישה
מומלץ להשתמש ב-ReLinker כדי לטעון את כל ספריות ה-Native כשמשתמשים בהצגה על פי דרישה של מודולים של תכונות. ReLinker פותר בעיה בטעינה של ספריות Native אחרי התקנה של מודול של תכונות. מידע נוסף על ReLinker זמין בטיפים ל-Android JNI.
טעינת קוד Native ממודול אופציונלי
אחרי שמתקינים את ה-split, מומלץ לטעון את קוד ה-Native שלו באמצעות ReLinker. באפליקציות ללא התקנה צריך להשתמש בשיטה המיוחדת הזו.
אם אתם משתמשים ב-System.loadLibrary() כדי לטעון את קוד ה-Native, וספריית ה-Native תלויה בספרייה אחרת במודול, אתם צריכים לטעון קודם את הספרייה האחרת באופן ידני.
אם אתם משתמשים ב-ReLinker, הפעולה המקבילה היא Relinker.recursively().loadLibrary().
אם אתם משתמשים ב-dlopen() בקוד Native כדי לטעון ספרייה שמוגדרת במודול אופציונלי, זה לא יעבוד עם נתיבי ספרייה יחסיים.
הפתרון הכי טוב הוא לאחזר את הנתיב המוחלט של הספרייה מקוד Java באמצעות ClassLoader.findLibrary(), ואז להשתמש בו בקריאה ל-dlopen().
צריך לעשות את זה לפני שמזינים את קוד ה-Native או להשתמש בקריאת JNI מקוד ה-Native ל-Java.
גישה לאפליקציות ללא התקנה ל-Android
אחרי שמודול של אפליקציה ללא התקנה ל-Android מדווח כ-INSTALLED, אפשר לגשת לקוד ולמשאבים שלו באמצעות Context של אפליקציה שעברה רענון. הקשר שהאפליקציה יוצרת לפני התקנת מודול (לדוגמה, הקשר שכבר מאוחסן במשתנה) לא מכיל את התוכן של המודול החדש. אבל הקשר חדש כן – אפשר לקבל אותו, למשל, באמצעות 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).
אחרת, האפליקציה עדיין לא תזהה את הקוד והמשאבים של המודול. אחרי שמעדכנים את המטא-נתונים של האפליקציה, צריך לטעון את תוכן המודול במהלך האירוע הבא ב-main thread על ידי הפעלת 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 ברמת 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() ולבדוק את התוצאה, כמו שמתואר בסעיף הקודם.
הורדת משאבי שפה נוספים
כשמשתמשים בחבילות של אפליקציות, המכשירים מורידים רק את הקוד והמשאבים שנדרשים להפעלת האפליקציה. לכן, לגבי משאבי שפה, המכשיר של המשתמש מוריד רק את משאבי השפה של האפליקציה שתואמים לשפה אחת או יותר שנבחרה כרגע בהגדרות המכשיר.
אם אתם רוצים שלאפליקציה תהיה גישה למשאבי שפה נוספים – לדוגמה, כדי להטמיע כלי לבחירת שפה בתוך האפליקציה, אתם יכולים להשתמש בספריית הפצת הפיצ'רים ב-Play כדי להוריד אותם על פי דרישה. התהליך דומה לתהליך של הורדת מודול של תכונות, כמו שמוצג בהמשך.
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() בתוך השיטה 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 מאפשרת לבדוק באופן מקומי את היכולת של האפליקציה לבצע את הפעולות הבאות, בלי להתחבר לחנות Play:
- לבקש התקנות של מודולים ולעקוב אחריהן.
- טיפול בשגיאות בהתקנה.
- משתמשים ב-
SplitCompatכדי לגשת למודולים.
בדף הזה מוסבר איך לפרוס את קובצי ה-APK המפוצלים של האפליקציה במכשיר הבדיקה, כדי שהפצת הפיצ'רים ב-Play תשתמש באופן אוטומטי בקובצי ה-APK האלה כדי לדמות בקשה, הורדה והתקנה של מודולים מחנות Play.
לא צריך לבצע שינויים בלוגיקה של האפליקציה, אבל צריך לעמוד בדרישות הבאות:
- מורידים ומתקינים את הגרסה העדכנית של
bundletool. כדי ליצור קבוצה חדשה של קובצי APK שניתנים להתקנה מ-App Bundle של האפליקציה, צריך אתbundletool.
יצירת קבוצה של קובצי APK
אם עדיין לא עשיתם את זה, אתם צריכים ליצור את קובצי ה-APK המפוצלים של האפליקציה, באופן הבא:
- יוצרים App Bundle לאפליקציה באחת מהשיטות הבאות:
- משתמשים ב-Android Studio ובפלאגין Android ל-Gradle כדי ליצור ולחתום על קובץ Android App Bundle.
- יוצרים את ה-App Bundle משורת הפקודה.
משתמשים ב-
bundletoolכדי ליצור קבוצה של קובצי APK לכל תצורות המכשירים באמצעות הפקודה הבאה:bundletool build-apks --local-testing --bundle my_app.aab --output my_app.apks
הדגל --local-testing כולל מטא-נתונים במניפסטים של קובצי ה-APK, שמאפשרים לספריית הפצת הפיצ'רים ב-Play לדעת להשתמש בקובצי ה-APK המפוצלים המקומיים כדי לבדוק את ההתקנה של מודולים של תכונות, בלי להתחבר לחנות 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);