תמיכה בעדכונים מתוך האפליקציה (Kotlin או Java)

במדריך הזה מוסבר איך לתמוך בבתוך האפליקציה באפליקציה שלך באמצעות Kotlin או Java. יש מדריכים נפרדים למקרים שבהם ההטמעה משתמשת בקוד נייטיב (C/C++ ) ומקרים שבהם בהטמעה נעשה שימוש ב-Unity.

הגדרת סביבת הפיתוח

ספריית העדכונים בתוך האפליקציה של Play היא חלק מספריות הליבה של Google Play. צריך לכלול את התלות הבאה של Gradle כדי לשלב את Play In-App עדכון הספרייה.

מגניב

// 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 בקובץ ה- ממשק API של Google Play למפתחים. כל הגרסאות החדשות שנוספו בגרסה הן נחשבת לאותה עדיפות כמו של פריט התוכן. ניתן להגדיר עדיפות רק כאשר בתהליך השקה של גרסה חדשה, ואי אפשר לשנות אותה מאוחר יותר.

הגדרת העדיפות באמצעות ממשק API למפתחים של Google Play כפי שמתואר ב-Play ממשק 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 שקובע אם העדכון מורשים לנקות חבילות נכסים במקרה נפח אחסון מוגבל במכשיר. הערך של השדה הזה הוא 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: למשתמש יש אישר את העדכון. לקבלת עדכונים מיידיים, יכול להיות שלא תקבלו את ההודעה הזו קריאה חוזרת (callback) מפני שהעדכון אמור כבר להסתיים על ידי פקד הזמן שניתנו לאפליקציה שלך.
  • 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 לבקש מהמשתמש להתקין את העדכון. אחרת, עדכון הנתונים ממשיכה תופסת נפח האחסון במכשיר של המשתמש.

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());
            }
          });
}

תהליך העדכון מחזיר תוצאה כפי שמתואר במסמכי העזרה של startUpdateFlowForAmount(). באופן ספציפי, האפליקציה שלך צריכה להיות מסוגלת לטפל במקרים שבהם משתמש דוחה את יעדכן או יבטל את ההורדה. כשהמשתמש מבצע אחת מהפעולות האלה, ממשק המשתמש של Google Play נסגר. האפליקציה שלך אמורה לקבוע מהי הדרך הטובה ביותר להמשיך בתהליך.

אם אפשר, צריך לאפשר למשתמש להמשיך בלי העדכון ולבקש ממנו שוב מאוחר יותר. אם האפליקציה לא יכולה לפעול בלי העדכון, כדאי להציג הודעה אינפורמטיבית לפני הפעלה מחדש של תהליך העדכון או בקשה מהמשתמש סוגרים את האפליקציה. כך המשתמש יבין שהוא יכול להפעיל מחדש את האפליקציה כשהם יהיו מוכנים להתקין את העדכון הנדרש.

השלבים הבאים

לבדוק את העדכונים בתוך האפליקציה כדי לאמת שהשילוב פועל כמו שצריך.