במדריך הזה נסביר איך לתמוך בעדכונים באפליקציה באמצעות Kotlin או Java. יש מדריכים נפרדים למקרים שבהם ההטמעה מתבצעת באמצעות קוד מקומי (C/C++), ומקרים שבהם ההטמעה מתבצעת באמצעות Unity.
הגדרת סביבת הפיתוח
ספריית העדכונים מתוך האפליקציה של Play היא חלק מספריות הליבה של Google Play. כדי לשלב את ספריית העדכונים מתוך האפליקציה של Play, צריך לכלול את יחסי התלות הבאים ב-Gradle.
Groovy
// 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. } });
בדיקת העדיפות של העדכון
באמצעות ממשק ה-API של Google Play למפתחים אפשר להגדיר את העדיפות של כל עדכון. כך האפליקציה תוכל להחליט עד כמה להמליץ למשתמש על עדכון. לדוגמה, כדאי להשתמש באסטרטגיה הבאה כדי להגדיר את העדיפות של העדכונים:
- שיפורים קלים בממשק המשתמש: עדכון בעדיפות נמוכה. לא צריך לבקש עדכון גמיש או עדכון מיידי. כדאי לבצע את העדכון רק כשהמשתמש לא מבצע פעולות באפליקציה.
- שיפורים בביצועים: עדכון בעדיפות בינונית. צריך לבקש עדכון גמיש.
- עדכון אבטחה קריטי: עדכון בעדיפות גבוהה. צריך לבקש עדכון מיידי.
כדי לקבוע את רמת העדיפות, מערכת 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.
- אתם משחררים את הגרסה השלישית למסלול ייצור ללא עדיפות.
כשמשתמשים בסביבת הייצור יעדכנו מגרסה 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
שמגדיר אם העדכון יכול למחוק חבילות נכסים במקרה של נפח אחסון מוגבל במכשיר. השדה הזה מוגדר כברירת מחדל ל-false
, אבל אפשר להשתמש ב-method 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());
קבלת שיחה חוזרת לעדכון הסטטוס
אחרי שמתחילים עדכון, פונקציית ה-callbak של מרכז האפליקציות שמקבלת את תוצאת הפעילות הרשומה מקבלת את התוצאה של תיבת הדו-שיח לאישור:
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. } } });
יש כמה ערכים שיכולים להגיע מהקריאה החוזרת (callback) של 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 לא מפעיל באופן אוטומטי הפעלה מחדש של האפליקציה כשמדובר בעדכון גמיש. הסיבה לכך היא שבמהלך עדכון גמיש, המשתמש צפוי להמשיך באינטראקציה עם האפליקציה עד שהוא יחליט שהוא רוצה להתקין את העדכון.
מומלץ להציג התראה (או אינדיקציה אחרת בממשק המשתמש) כדי להודיע למשתמש שהעדכון מוכן להתקנה ולבקש אישור לפני הפעלת האפליקציה מחדש.
בדוגמה הבאה מוצגת הטמעה של סרגל סטטוס (snackbar) בסגנון 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()
בחזית, פלטפורמת Meet מציגה ממשק משתמש במסך מלא שמפעיל מחדש את האפליקציה ברקע. אחרי שהפלטפורמה מתקינה את העדכון, האפליקציה מופעלת מחדש לפעילות הראשית שלה.
אם במקום זאת קוראים לפונקציה completeUpdate()
כשהאפליקציה ברקע, העדכון מותקן בשקט בלי להסתיר את ממשק המשתמש של המכשיר.
בכל פעם שהמשתמש מעביר את האפליקציה לחזית, כדאי לבדוק אם יש לה עדכון שממתין להתקנה. אם יש עדכון לאפליקציה במצב DOWNLOADED
, צריך לבקש מהמשתמש להתקין את העדכון. אחרת, נתוני העדכון ימשיכו להשתמש באחסון של המכשיר של המשתמש.
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 נסגר. האפליקציה צריכה לקבוע את הדרך הטובה ביותר להמשיך.
אם אפשר, כדאי לאפשר למשתמש להמשיך בלי העדכון ולהציג לו שוב את ההודעה מאוחר יותר. אם האפליקציה לא יכולה לפעול בלי העדכון, כדאי להציג הודעה מפורטת לפני שמפעילים מחדש את תהליך העדכון או לבקש מהמשתמש לסגור את האפליקציה. כך המשתמש יבין שהוא יכול להפעיל מחדש את האפליקציה כשיהיה מוכן להתקין את העדכון הנדרש.
השלבים הבאים
בודקים את העדכונים בתוך האפליקציה כדי לוודא שהשילוב פועל כמו שצריך.