מודולים של תכונות מאפשרים להפריד תכונות ומשאבים מסוימים מהמודול הבסיסי של האפליקציה ולכלול אותם ב-App Bundle. באמצעות Play Feature Delivery, המשתמשים יכולים, למשל, להוריד ולהתקין את הרכיבים האלה על פי דרישה מאוחר יותר, אחרי שהם כבר התקינו את קובץ ה-APK הבסיסי של האפליקציה.
לדוגמה, נניח שאפליקציית שליחת הודעות טקסט כוללת פונקציונליות לצילום ולשליחה של הודעות תמונה, אבל רק אחוז קטן מהמשתמשים שולחים הודעות תמונה. יכול להיות שיהיה הגיוני לכלול את התכונה 'הודעות תמונה' כמודול תכונה שניתן להורדה. כך, גודל ההורדה הראשונית של האפליקציה קטן יותר לכל המשתמשים, ורק משתמשים ששולחים הודעות תמונה צריכים להוריד את הרכיב הנוסף הזה.
חשוב לזכור שסוג המבנה הזה דורש יותר מאמץ, וייתכן שיהיה צורך לבצע ריפרקטורינג לקוד הקיים של האפליקציה. לכן, כדאי לשקול היטב אילו מהתכונות של האפליקציה יהיו הכי מועילות אם יהיו זמינות למשתמשים על פי דרישה. כדי להבין טוב יותר את התרחישים לדוגמה ואת ההנחיות האופטימליים לשימוש בתכונות על פי דרישה, כדאי לקרוא את המאמר שיטות מומלצות בנושא חוויית המשתמש להצגת מודעות על פי דרישה.
אם אתם רוצים להפוך בהדרגה את תכונות האפליקציה למודולריות לאורך זמן, בלי להפעיל אפשרויות הפצה מתקדמות כמו הפצה על פי דרישה, תוכלו במקום זאת להגדיר הפצה בזמן ההתקנה.
בדף הזה תלמדו איך להוסיף מודול תכונה לפרויקט האפליקציה ולהגדיר אותו להעברה על פי דרישה. לפני שמתחילים, חשוב לוודא שאתם משתמשים ב-Android Studio 3.5 ואילך ובפלאגין Android Gradle מגרסה 3.5.0 ואילך.
הגדרת מודול חדש להעברה על פי דרישה
הדרך הקלה ביותר ליצור מודול תכונות חדש היא להשתמש ב-Android Studio 3.5 ואילך. מכיוון שמודולים של תכונות תלויים מעצם טבעם במודול האפליקציה הבסיסי, אפשר להוסיף אותם רק לפרויקטים קיימים של אפליקציות.
כדי להוסיף מודול תכונה לפרויקט האפליקציה באמצעות Android Studio:
- אם עדיין לא עשיתם זאת, פותחים את פרויקט האפליקציה בסביבת הפיתוח המשולבת.
- בסרגל התפריטים, בוחרים באפשרות קובץ > חדש > מודול חדש.
- בתיבת הדו-שיח 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>
מסמנים את התיבה שלצד מיזוג אם רוצים שהמודול יהיה זמין למכשירים עם 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 לפרויקט.
בקשה לקבלת מודול על פי דרישה
אם האפליקציה צריכה להשתמש במודול של תכונות, היא יכולה לבקש אותו בזמן שהוא בחזית המחלקה 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.
דחיית ההתקנה של מודולים על פי דרישה
אם אתם לא צריכים שהאפליקציה שלכם תוריד ותתקין באופן מיידי מודול על פי דרישה, תוכלו לדחות את ההתקנה כשהאפליקציה פועלת ברקע. לדוגמה, אם אתם רוצים לטעון מראש קצת חומר פרסומי לקראת השקת האפליקציה.
אפשר לציין מודול להורדה מאוחר יותר באמצעות השיטה 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 של המשתמש. | אם למשתמש אין גישה למודול, צריך להודיע לו על כך. |
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 Store לא מותקנת במכשיר. | צריך ליידע את המשתמש שהאפליקציה של חנות 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
. האפליקציה יכולה לבקש מהמשתמש שוב להשלים את הבקשה.
כדי לקבל קריאה חוזרת עם התגובה של המשתמש, אפשר לשנות את ה-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. } });
ביטול בקשת התקנה
אם האפליקציה צריכה לבטל בקשה לפני שהיא מותקנת, היא יכולה להפעיל את ה-method 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
הוא שם החבילה של האפליקציה והפרמטר השני הוא השם המלא של המחלקה (class) של הפעילות.
אם יש לכם פעילות במודול תכונות שהורדתם על פי דרישה, עליכם להפעיל את 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
, ולהעביר את ה-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 כדי לטעון את כל ספריות ה-Native כשאתם משתמשים במסירה לפי דרישה של מודולים של תכונות. 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 בגרסה 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 המפוצלות של האפליקציה באופן הבא:
- יוצרים חבילת אפליקציות לאפליקציה שלכם באחת מהשיטות הבאות:
- שימוש ב-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 Feature Delivery משתמשת בקובצי ה-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);