במדריך הזה מוסבר איך להוסיף תמיכה בעדכונים בתוך האפליקציה באפליקציה שלכם באמצעות Kotlin או Java. יש מדריכים נפרדים למקרים שבהם ההטמעה משתמשת בקוד מקורי (C/C++) ולמקרים שבהם ההטמעה משתמשת ב-Unity או ב-Unreal Engine.
הגדרת סביבת הפיתוח
ספריית העדכונים בתוך האפליקציה ב-Play היא חלק מספריות הליבה של Google Play. כדי לשלב את ספריית העדכונים מתוך האפליקציה ב-Play, צריך לכלול את התלות הבאה ב-Gradle.
מגניב
// In your app's build.gradle file: ... dependencies { // This dependency is downloaded from the Google's Maven repository. // So, make sure you also include that repository in your project's build.gradle file. implementation 'com.google.android.play:app-update:2.1.0' // For Kotlin users also add the Kotlin extensions library for Play In-App Update: implementation 'com.google.android.play:app-update-ktx:2.1.0' ... }
Kotlin
// In your app's build.gradle.kts file: ... dependencies { // This dependency is downloaded from the Google's Maven repository. // So, make sure you also include that repository in your project's build.gradle file. implementation("com.google.android.play:app-update:2.1.0") // For Kotlin users also import the Kotlin extensions library for Play In-App Update: implementation("com.google.android.play:app-update-ktx:2.1.0") ... }
בדיקה אם יש עדכונים שצריך להתקין
לפני שמבקשים עדכון, בודקים אם יש עדכון זמין לאפליקציה.
כדי לבדוק אם יש עדכון, משתמשים ב-AppUpdateManager
:
Kotlin
val appUpdateManager = AppUpdateManagerFactory.create(context) // Returns an intent object that you use to check for an update. val appUpdateInfoTask = appUpdateManager.appUpdateInfo // Checks that the platform will allow the specified type of update. appUpdateInfoTask.addOnSuccessListener { appUpdateInfo -> if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE // This example applies an immediate update. To apply a flexible update // instead, pass in AppUpdateType.FLEXIBLE && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) ) { // Request the update. } }
Java
AppUpdateManager appUpdateManager = AppUpdateManagerFactory.create(context); // Returns an intent object that you use to check for an update. Task<AppUpdateInfo> appUpdateInfoTask = appUpdateManager.getAppUpdateInfo(); // Checks that the platform will allow the specified type of update. appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> { if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE // This example applies an immediate update. To apply a flexible update // instead, pass in AppUpdateType.FLEXIBLE && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) { // Request the update. } });
מופע AppUpdateInfo
שמוחזר מכיל את סטטוס הזמינות של העדכון. בהתאם לסטטוס העדכון, המופע מכיל גם את הפרטים הבאים:
- אם יש עדכון זמין והעדכון מותר, המופע מכיל גם כוונה להתחיל את העדכון.
- אם עדכון בתוך האפליקציה כבר מתבצע, המופע מדווח גם על הסטטוס של העדכון שמתבצע.
בדיקת עדכונים ישנים
בנוסף לבדיקה אם יש עדכון זמין, כדאי גם לבדוק כמה זמן עבר מאז שהמשתמש קיבל לאחרונה הודעה על עדכון דרך חנות Play. המידע הזה יכול לעזור לכם להחליט אם כדאי להתחיל עדכון גמיש או עדכון מיידי. לדוגמה, אפשר לחכות כמה ימים לפני שמודיעים למשתמש על עדכון גמיש, וכמה ימים אחרי זה לפני שדורשים עדכון מיידי.
אפשר להשתמש ב-clientVersionStalenessDays()
כדי לבדוק כמה ימים עברו מאז שהעדכון הפך לזמין בחנות Play:
Kotlin
val appUpdateManager = AppUpdateManagerFactory.create(context) // Returns an intent object that you use to check for an update. val appUpdateInfoTask = appUpdateManager.appUpdateInfo // Checks whether the platform allows the specified type of update, // and current version staleness. appUpdateInfoTask.addOnSuccessListener { appUpdateInfo -> if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && (appUpdateInfo.clientVersionStalenessDays() ?: -1) >= DAYS_FOR_FLEXIBLE_UPDATE && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) { // Request the update. } }
Java
AppUpdateManager appUpdateManager = AppUpdateManagerFactory.create(context); // Returns an intent object that you use to check for an update. Task<AppUpdateInfo> appUpdateInfoTask = appUpdateManager.getAppUpdateInfo(); // Checks whether the platform allows the specified type of update, // and current version staleness. appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> { if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && appUpdateInfo.clientVersionStalenessDays() != null && appUpdateInfo.clientVersionStalenessDays() >= DAYS_FOR_FLEXIBLE_UPDATE && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) { // Request the update. } });
בדיקת סדר העדיפויות של העדכונים
ממשק Google Play Developer API מאפשר להגדיר את העדיפות של כל עדכון. כך האפליקציה יכולה להחליט עד כמה מומלץ למשתמש לעדכן אותה. לדוגמה, אפשר להשתמש באסטרטגיה הבאה להגדרת עדיפות העדכון:
- שיפורים קלים בממשק המשתמש: עדכון בסדר עדיפות נמוך. לא נדרש עדכון גמיש או עדכון מיידי. העדכון יתבצע רק כשאין אינטראקציה של המשתמש עם האפליקציה.
- שיפורים בביצועים: עדכון בעדיפות בינונית. אפשר לבקש עדכון גמיש.
- עדכון אבטחה קריטי: עדכון בעדיפות גבוהה; צריך לבקש עדכון מיידי.
כדי לקבוע את העדיפות, מערכת Google Play משתמשת בערך מספרי שלם בין 0 ל-5, כאשר 0 הוא ברירת המחדל ו-5 היא העדיפות הגבוהה ביותר. כדי להגדיר את העדיפות של עדכון, משתמשים בשדה inAppUpdatePriority
בקטע Edits.tracks.releases
ב-Google Play Developer API. כל הגרסאות החדשות שנוספו לגרסה נחשבות בעדיפות זהה לגרסה. אפשר להגדיר עדיפות רק כשמשיקים גרסה חדשה, ואי אפשר לשנות אותה מאוחר יותר.
מגדירים את העדיפות באמצעות Google Play Developer API, כפי שמתואר במסמכי התיעוד של Play Developer API. צריך לציין את העדיפות של העדכון בתוך האפליקציה במשאב Edit.tracks
שמועבר בשיטה Edit.tracks: update
. בדוגמה הבאה מוצג תהליך של פרסום אפליקציה עם קוד גרסה 88
ו-inAppUpdatePriority
5:
{ "releases": [{ "versionCodes": ["88"], "inAppUpdatePriority": 5, "status": "completed" }] }
בקוד של האפליקציה, אפשר לבדוק את רמת העדיפות של עדכון מסוים באמצעות updatePriority()
. העדיפות שמוחזרת לוקחת בחשבון את inAppUpdatePriority
של כל קודי הגרסאות של האפליקציה בין הגרסה המותקנת לבין הגרסה הזמינה האחרונה, ללא קשר לערוץ ההפצה. לדוגמה, נבחן את התרחיש הבא:
- מפרסמים את גרסה 1 במסלול לסביבת הייצור ללא עדיפות.
- אתם מפיצים את גרסה 2 במסלול בדיקה פנימית עם עדיפות 5.
- אתם מפרסמים את גרסה 3 במסלול לסביבת הייצור ללא עדיפות.
כשמשתמשים בסביבת הייצור מעדכנים מגרסה 1 לגרסה 3, הם מקבלים עדיפות 5, למרות שגרסה 2 פורסמה במסלול אחר.
Kotlin
val appUpdateManager = AppUpdateManagerFactory.create(context) // Returns an intent object that you use to check for an update. val appUpdateInfoTask = appUpdateManager.appUpdateInfo // Checks whether the platform allows the specified type of update, // and checks the update priority. appUpdateInfoTask.addOnSuccessListener { appUpdateInfo -> if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && appUpdateInfo.updatePriority() >= 4 /* high priority */ && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) { // Request an immediate update. } }
Java
AppUpdateManager appUpdateManager = AppUpdateManagerFactory.create(context); // Returns an intent object that you use to check for an update. Task<AppUpdateInfo> appUpdateInfoTask = appUpdateManager.getAppUpdateInfo(); // Checks whether the platform allows the specified type of update, // and checks the update priority. appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> { if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && appUpdateInfo.updatePriority() >= 4 /* high priority */ && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) { // Request an immediate update. } });
התחלת עדכון
אחרי שמאשרים שיש עדכון זמין, אפשר לבקש עדכון באמצעות AppUpdateManager.startUpdateFlowForResult()
:
Kotlin
appUpdateManager.startUpdateFlowForResult( // Pass the intent that is returned by 'getAppUpdateInfo()'. appUpdateInfo, // an activity result launcher registered via registerForActivityResult activityResultLauncher, // Or pass 'AppUpdateType.FLEXIBLE' to newBuilder() for // flexible updates. AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build())
Java
appUpdateManager.startUpdateFlowForResult( // Pass the intent that is returned by 'getAppUpdateInfo()'. appUpdateInfo, // an activity result launcher registered via registerForActivityResult activityResultLauncher, // Or pass 'AppUpdateType.FLEXIBLE' to newBuilder() for // flexible updates. AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build());
אפשר להשתמש בכל מופע של AppUpdateInfo
כדי להתחיל עדכון רק פעם אחת. כדי לנסות שוב לעדכן במקרה של כשל, צריך לבקש AppUpdateInfo
חדש ולבדוק שוב שהעדכון זמין ומותר.
אפשר לרשום את מפעיל התוצאה של הפעילות באמצעות החוזה המובנה ActivityResultContracts.StartIntentSenderForResult
. כדאי לעיין בקטע בנושא קבלת קריאה חוזרת לעדכון הסטטוס.
השלבים הבאים תלויים בסוג העדכון שאתם מבקשים: עדכון גמיש או עדכון מיידי.
הגדרת עדכון באמצעות AppUpdateOptions
AppUpdateOptions
מכיל שדה AllowAssetPackDeletion
שמגדיר אם העדכון יכול למחוק חבילות Asset Pack במקרה של נפח אחסון מוגבל במכשיר. שדה זה מוגדר כ-false
כברירת מחדל, אבל אפשר להשתמש בשיטה setAllowAssetPackDeletion()
כדי להגדיר אותו כ-true
:
Kotlin
appUpdateManager.startUpdateFlowForResult( // Pass the intent that is returned by 'getAppUpdateInfo()'. appUpdateInfo, // an activity result launcher registered via registerForActivityResult activityResultLauncher, // Or pass 'AppUpdateType.FLEXIBLE' to newBuilder() for // flexible updates. AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE) .setAllowAssetPackDeletion(true) .build())
Java
appUpdateManager.startUpdateFlowForResult( // Pass the intent that is returned by 'getAppUpdateInfo()'. appUpdateInfo, // an activity result launcher registered via registerForActivityResult activityResultLauncher, // Or pass 'AppUpdateType.FLEXIBLE' to newBuilder() for // flexible updates. AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE) .setAllowAssetPackDeletion(true) .build());
קבלת שיחה חוזרת לעדכון הסטטוס
אחרי שמתחילים עדכון, הפונקציה הרשומה להחזרת תוצאות של פעילות להפעלת אפליקציה מקבלת את התוצאה של תיבת הדו-שיח לאישור:
Kotlin
registerForActivityResult(StartIntentSenderForResult()) { result: ActivityResult -> // handle callback if (result.resultCode != RESULT_OK) { log("Update flow failed! Result code: " + result.resultCode); // If the update is canceled or fails, // you can request to start the update again. } }
Java
registerForActivityResult( new ActivityResultContracts.StartIntentSenderForResult(), new ActivityResultCallback<ActivityResult>() { @Override public void onActivityResult(ActivityResult result) { // handle callback if (result.getResultCode() != RESULT_OK) { log("Update flow failed! Result code: " + result.getResultCode()); // If the update is canceled or fails, // you can request to start the update again. } } });
יש כמה ערכים שאפשר לקבל מהקריאה החוזרת onActivityResult()
:
-
RESULT_OK
: המשתמש אישר את העדכון. יכול להיות שלא תקבלו את הקריאה החוזרת הזו לעדכונים מיידיים, כי העדכון אמור להסתיים עד שהשליטה תחזור לאפליקציה שלכם. -
RESULT_CANCELED
: המשתמש דחה או ביטל את העדכון. -
ActivityResult.RESULT_IN_APP_UPDATE_FAILED
: שגיאה אחרת מנעה מהמשתמש להביע הסכמה או מהעדכון להמשיך.
טיפול בעדכון גמיש
כשמתחילים עדכון גמיש, קודם מופיעה תיבת דו-שיח למשתמש כדי לבקש ממנו הסכמה. אם המשתמש מאשר, ההורדה מתחילה ברקע והמשתמש יכול להמשיך להשתמש באפליקציה. בקטע הזה מתואר איך לעקוב אחרי עדכון גמיש בתוך האפליקציה ולהשלים אותו.
מעקב אחר מצב העדכון הגמיש
אחרי שההורדה של עדכון גמיש מתחילה, האפליקציה צריכה לעקוב אחרי מצב העדכון כדי לדעת מתי אפשר להתקין את העדכון ולהציג את ההתקדמות בממשק המשתמש של האפליקציה.
כדי לעקוב אחרי סטטוס העדכון במהלך ההתקנה, צריך לרשום מאזין לעדכוני סטטוס ההתקנה. אפשר גם להוסיף סרגל התקדמות בממשק המשתמש של האפליקציה כדי שהמשתמשים ידעו מה מצב ההורדה.
Kotlin
// Create a listener to track request state updates. val listener = InstallStateUpdatedListener { state -> // (Optional) Provide a download progress bar. if (state.installStatus() == InstallStatus.DOWNLOADING) { val bytesDownloaded = state.bytesDownloaded() val totalBytesToDownload = state.totalBytesToDownload() // Show update progress bar. } // Log state or install the update. } // Before starting an update, register a listener for updates. appUpdateManager.registerListener(listener) // Start an update. // When status updates are no longer needed, unregister the listener. appUpdateManager.unregisterListener(listener)
Java
// Create a listener to track request state updates. InstallStateUpdatedListener listener = state -> { // (Optional) Provide a download progress bar. if (state.installStatus() == InstallStatus.DOWNLOADING) { long bytesDownloaded = state.bytesDownloaded(); long totalBytesToDownload = state.totalBytesToDownload(); // Implement progress bar. } // Log state or install the update. }; // Before starting an update, register a listener for updates. appUpdateManager.registerListener(listener); // Start an update. // When status updates are no longer needed, unregister the listener. appUpdateManager.unregisterListener(listener);
התקנת עדכון גמיש
כשמזהים את המצב InstallStatus.DOWNLOADED
, צריך להפעיל מחדש את האפליקציה כדי להתקין את העדכון.
בניגוד לעדכונים מיידיים, Google Play לא מפעיל באופן אוטומטי הפעלה מחדש של אפליקציה עבור עדכון גמיש. הסיבה לכך היא שבמהלך עדכון גמיש, המשתמש מצפה להמשיך באינטראקציה עם האפליקציה עד שהוא מחליט שהוא רוצה להתקין את העדכון.
מומלץ להציג התראה (או אינדיקציה אחרת בממשק המשתמש) כדי ליידע את המשתמש שהעדכון מוכן להתקנה ולבקש אישור לפני הפעלה מחדש של האפליקציה.
בדוגמה הבאה מוצגת הטמעה של סרגל חטיפים של Material Design שמבקש מהמשתמש לאשר את ההפעלה מחדש של האפליקציה:
Kotlin
val listener = { state -> if (state.installStatus() == InstallStatus.DOWNLOADED) { // After the update is downloaded, show a notification // and request user confirmation to restart the app. popupSnackbarForCompleteUpdate() } ... } // Displays the snackbar notification and call to action. fun popupSnackbarForCompleteUpdate() { Snackbar.make( findViewById(R.id.activity_main_layout), "An update has just been downloaded.", Snackbar.LENGTH_INDEFINITE ).apply { setAction("RESTART") { appUpdateManager.completeUpdate() } setActionTextColor(resources.getColor(R.color.snackbar_action_text_color)) show() } }
Java
InstallStateUpdatedListener listener = state -> { if (state.installStatus() == InstallStatus.DOWNLOADED) { // After the update is downloaded, show a notification // and request user confirmation to restart the app. popupSnackbarForCompleteUpdate(); } ... }; // Displays the snackbar notification and call to action. private void popupSnackbarForCompleteUpdate() { Snackbar snackbar = Snackbar.make( findViewById(R.id.activity_main_layout), "An update has just been downloaded.", Snackbar.LENGTH_INDEFINITE); snackbar.setAction("RESTART", view -> appUpdateManager.completeUpdate()); snackbar.setActionTextColor( getResources().getColor(R.color.snackbar_action_text_color)); snackbar.show(); }
כשמתקשרים אל appUpdateManager.completeUpdate()
בחזית, הפלטפורמה מציגה ממשק משתמש במסך מלא שמפעיל מחדש את האפליקציה ברקע.
אחרי שהפלטפורמה מתקינה את העדכון, האפליקציה מופעלת מחדש בפעילות הראשית שלה.
אם במקום זאת מתקשרים אל completeUpdate()
כשהאפליקציה ברקע,
העדכון מותקן בשקט בלי להסתיר את ממשק המשתמש של המכשיר.
בכל פעם שהמשתמש מעביר את האפליקציה לחזית, צריך לבדוק אם יש עדכון לאפליקציה שממתין להתקנה. אם יש עדכון לאפליקציה במצב DOWNLOADED
state, צריך להציג למשתמש בקשה להתקין את העדכון. אחרת, נתוני העדכון ממשיכים לתפוס מקום באחסון של המכשיר של המשתמש.
Kotlin
// Checks that the update is not stalled during 'onResume()'. // However, you should execute this check at all app entry points. override fun onResume() { super.onResume() appUpdateManager .appUpdateInfo .addOnSuccessListener { appUpdateInfo -> ... // If the update is downloaded but not installed, // notify the user to complete the update. if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) { popupSnackbarForCompleteUpdate() } } }
Java
// Checks that the update is not stalled during 'onResume()'. // However, you should execute this check at all app entry points. @Override protected void onResume() { super.onResume(); appUpdateManager .getAppUpdateInfo() .addOnSuccessListener(appUpdateInfo -> { ... // If the update is downloaded but not installed, // notify the user to complete the update. if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) { popupSnackbarForCompleteUpdate(); } }); }
איך מבצעים עדכון מיידי
כשמתחילים עדכון מיידי והמשתמש מאשר את תחילת העדכון, Google Play מציג את התקדמות העדכון בחלק העליון של ממשק המשתמש של האפליקציה לאורך כל משך העדכון. אם המשתמש סוגר או מפסיק את השימוש באפליקציה במהלך העדכון, העדכון צריך להמשיך להוריד ולהתקין ברקע ללא אישור נוסף מהמשתמש.
עם זאת, כשחוזרים לאפליקציה, צריך לוודא שהעדכון לא תקוע במצב UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS
. אם העדכון נתקע במצב הזה, צריך להמשיך את העדכון:
Kotlin
// Checks that the update is not stalled during 'onResume()'. // However, you should execute this check at all entry points into the app. override fun onResume() { super.onResume() appUpdateManager .appUpdateInfo .addOnSuccessListener { appUpdateInfo -> ... if (appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS ) { // If an in-app update is already running, resume the update. appUpdateManager.startUpdateFlowForResult( appUpdateInfo, activityResultLauncher, AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build()) } } }
Java
// Checks that the update is not stalled during 'onResume()'. // However, you should execute this check at all entry points into the app. @Override protected void onResume() { super.onResume(); appUpdateManager .getAppUpdateInfo() .addOnSuccessListener( appUpdateInfo -> { ... if (appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) { // If an in-app update is already running, resume the update. appUpdateManager.startUpdateFlowForResult( appUpdateInfo, activityResultLauncher, AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build()); } }); }
תהליך העדכון מחזיר תוצאה כפי שמתואר במסמך העזר בנושא startUpdateFlowForResult()
. באופן ספציפי, האפליקציה צריכה להיות מסוגלת לטפל במקרים שבהם משתמש דוחה את העדכון או מבטל את ההורדה. כשהמשתמש מבצע אחת מהפעולות האלה, ממשק המשתמש של Google Play נסגר. האפליקציה צריכה לקבוע את הדרך הטובה ביותר להמשיך.
אם אפשר, מאפשרים למשתמש להמשיך בלי העדכון ומציגים לו שוב את ההודעה מאוחר יותר. אם אי אפשר להפעיל את האפליקציה בלי העדכון, כדאי להציג הודעה אינפורמטיבית לפני שמפעילים מחדש את תהליך העדכון או מבקשים מהמשתמש לסגור את האפליקציה. כך המשתמש יבין שהוא יוכל להפעיל מחדש את האפליקציה כשהוא יהיה מוכן להתקין את העדכון הנדרש.
השלבים הבאים
בודקים את העדכונים בתוך האפליקציה כדי לוודא שההטמעה פועלת כמו שצריך.