Mendukung update dalam aplikasi (Kotlin atau Java)

Panduan ini menjelaskan cara mendukung update dalam aplikasi di aplikasi Anda menggunakan Kotlin atau Java. Ada panduan terpisah untuk kasus saat implementasi Anda menggunakan kode native (C/C++) dan saat implementasi Anda menggunakan Unity.

Menyiapkan lingkungan pengembangan

Library Play In-App Update adalah bagian dari library Google Play Core. Harap sertakan dependensi Gradle berikut untuk mengintegrasikan Library Play In-App Update.

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")
    ...
}

Memeriksa ketersediaan update

Sebelum meminta update, periksa apakah ada update yang tersedia untuk aplikasi Anda. Gunakan AppUpdateManager untuk memeriksa update:

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

Instance AppUpdateInfo yang ditampilkan berisi status ketersediaan update. Bergantung pada status update, instance juga berisi hal berikut:

  • Jika update tersedia dan diizinkan, instance juga berisi intent untuk memulai update.
  • Jika update dalam aplikasi sedang berjalan, instance juga melaporkan status update yang sedang berjalan.

Memeriksa penghentian update

Selain memeriksa apakah update tersedia, Anda juga perlu memeriksa jumlah waktu yang telah berlalu sejak pengguna terakhir kali diberi tahu tentang update melalui Play Store. Hal ini dapat membantu Anda menentukan apakah harus melakukan inisialisasi update fleksibel atau update langsung. Misalnya, Anda mungkin menunggu beberapa hari sebelum memberi tahu pengguna dengan update fleksibel, dan beberapa hari setelahnya sebelum meminta update langsung.

Gunakan clientVersionStalenessDays() untuk memeriksa jumlah hari sejak update tersedia di Play Store:

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

Memeriksa prioritas update

Dengan Google Play Developer API, Anda dapat menyetel prioritas setiap update. Hal ini memungkinkan aplikasi Anda menentukan seberapa kuat rekomendasi update kepada pengguna. Sebagai contoh, pertimbangkan strategi berikut untuk menyetel prioritas update:

  • Peningkatan kecil pada UI: Update prioritas rendah; tidak meminta update fleksibel atau update langsung. Mengupdate hanya saat pengguna tidak berinteraksi dengan aplikasi Anda.
  • Peningkatan performa: Update prioritas sedang; meminta update fleksibel.
  • Update keamanan penting: Update prioritas tinggi; meminta update langsung.

Untuk menentukan prioritas, Google Play menggunakan nilai bilangan bulat antara 0 dan 5, dengan 0 adalah nilai default dan 5 adalah prioritas tertinggi. Guna menyetel prioritas untuk update, gunakan kolom inAppUpdatePriority di bawah Edits.tracks.releases di Google Play Developer API. Semua versi yang baru ditambahkan dalam rilis dianggap sebagai prioritas yang sama dengan rilis tersebut. Prioritas hanya dapat disetel saat meluncurkan rilis baru, dan tidak dapat diubah setelahnya.

Tetapkan prioritas menggunakan Google Play Developer API seperti yang dijelaskan di dokumentasi Play Developer API. Prioritas update dalam aplikasi harus ditentukan di resource Edit.tracks yang diteruskan di metode Edit.tracks: update. Contoh berikut menunjukkan perilisan aplikasi dengan kode versi 88 dan inAppUpdatePriority 5:

{
  "releases": [{
      "versionCodes": ["88"],
      "inAppUpdatePriority": 5,
      "status": "completed"
  }]
}

Dalam kode aplikasi, Anda dapat memeriksa tingkat prioritas untuk update tertentu menggunakan updatePriority(). Prioritas yang ditampilkan mempertimbangkan inAppUpdatePriority untuk semua kode versi aplikasi antara versi terinstal dan versi terbaru yang tersedia, tanpa bergantung pada jalur rilis. Misalnya, perhatikan skenario berikut:

  • Anda merilis versi 1 ke jalur produksi tanpa prioritas.
  • Anda merilis versi 2 ke jalur pengujian internal dengan prioritas 5.
  • Anda merilis versi 3 ke jalur produksi tanpa prioritas.

Saat pengguna produksi mengupdate dari versi 1 ke versi 3, mereka akan mendapatkan prioritas 5, meskipun versi 2 dipublikasikan di jalur yang berbeda.

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

Memulai update

Setelah memastikan bahwa update tersedia, Anda dapat meminta update menggunakan 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());

Setiap instance AppUpdateInfo hanya dapat digunakan untuk memulai update sekali saja. Untuk mencoba kembali update jika gagal, minta AppUpdateInfo baru dan periksa kembali apakah update tersedia dan diizinkan.

Anda dapat mendaftarkan peluncur hasil aktivitas menggunakan kontrak ActivityResultContracts.StartIntentSenderForResult bawaan. Periksa bagian tentang mendapatkan callback untuk status update.

Langkah selanjutnya bergantung pada apakah Anda meminta update fleksibel atau update langsung.

Mengonfigurasi update dengan AppUpdateOptions

AppUpdateOptions berisi kolom AllowAssetPackDeletion yang menentukan apakah update diizinkan untuk menghapus paket aset jika penyimpanan perangkat terbatas. Kolom ini ditetapkan ke false secara default, tetapi Anda dapat menggunakan metode setAllowAssetPackDeletion() untuk menetapkannya ke 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());

Mendapatkan callback untuk status update

Setelah memulai update, callback peluncur hasil aktivitas yang terdaftar akan mendapatkan hasil dialog konfirmasi:

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

Ada beberapa nilai yang mungkin Anda terima dari callback onActivityResult():

  • RESULT_OK: Pengguna telah menyetujui update. Untuk update langsung, Anda mungkin tidak menerima callback ini karena update seharusnya sudah selesai saat kontrol waktu diberikan kembali ke aplikasi Anda.
  • RESULT_CANCELED: Pengguna telah menolak atau membatalkan update.
  • ActivityResult.RESULT_IN_APP_UPDATE_FAILED: Beberapa error lain mencegah pengguna memberikan izin atau update dilanjutkan.

Menangani update fleksibel

Saat Anda memulai update fleksibel, dialog terlebih dahulu akan muncul kepada pengguna untuk meminta izin. Jika pengguna mengizinkan, maka download akan dimulai di latar belakang, dan pengguna dapat melanjutkan berinteraksi dengan aplikasi. Bagian ini menjelaskan cara memantau dan menyelesaikan update dalam aplikasi fleksibel.

Memantau status update aplikasi

Setelah download dimulai untuk update fleksibel, aplikasi Anda harus memantau status update untuk mengetahui kapan update dapat diinstal dan menampilkan progresnya di UI aplikasi.

Anda dapat memantau status update yang sedang berlangsung dengan mendaftarkan pemroses untuk menginstal update status. Anda juga dapat memberikan status progres di UI aplikasi untuk memberi tahu pengguna tentang progres download.

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

Menginstal update fleksibel

Saat Anda mendeteksi status InstallStatus.DOWNLOADED, Anda harus memulai ulang aplikasi untuk menginstal update.

Tidak seperti update langsung, Google Play tidak otomatis memicu mulai ulang aplikasi untuk update fleksibel. Hal ini karena selama proses update fleksibel, pengguna berharap untuk terus berinteraksi dengan aplikasi hingga memutuskan bahwa mereka ingin menginstal update.

Sebaiknya Anda memberi notifikasi (atau indikasi UI lain) untuk memberi tahu pengguna bahwa update siap untuk diinstal dan meminta konfirmasi sebelum memulai ulang aplikasi.

Contoh berikut mendemonstrasikan penerapan snackbar Desain Material yang meminta konfirmasi dari pengguna untuk memulai aplikasi:

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

Saat Anda memanggil appUpdateManager.completeUpdate() di latar depan, platform menampilkan UI layar penuh yang memulai ulang aplikasi di latar belakang. Setelah platform menginstal update, aplikasi Anda memulai ulang ke dalam aktivitas utamanya.

Jika Anda lebih memilih memanggil completeUpdate() saat aplikasi berada di latar belakang, update akan diinstal secara senyap tanpa mengaburkan UI perangkat.

Kapan pun pengguna membawa aplikasi Anda ke latar belakang, periksa apakah aplikasi memiliki update yang menunggu untuk diinstal. Jika aplikasi Anda memiliki update di status DOWNLOADED, minta pengguna untuk menginstal update. Jika tidak, data update akan terus mengisi penyimpanan perangkat pengguna.

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

Menangani update langsung

Jika Anda memulai update langsung dan pengguna setuju untuk memulai update, Google Play akan menampilkan progres update di bagian atas UI aplikasi selama durasi update. Jika pengguna menutup atau menghentikan aplikasi Anda selama update, update tersebut harus terus mendownload dan menginstal di latar belakang tanpa konfirmasi tambahan dari pengguna.

Namun, jika aplikasi kembali ke latar depan, Anda harus memastikan bahwa update tidak terhenti di status UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS. Jika update terhenti di status ini, lanjutkan update:

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

Alur update menampilkan hasil seperti yang dijelaskan di dokumentasi referensi untuk startUpdateFlowForResult(). Secara khusus, aplikasi Anda harus dapat menangani kasus saat pengguna menolak update atau membatalkan download. Saat pengguna melakukan salah satu tindakan ini, UI Google Play akan menutup. Aplikasi Anda harus menentukan cara terbaik untuk melanjutkan.

Jika memungkinkan, biarkan pengguna meneruskan tanpa update dan minta mereka lagi nanti. Jika aplikasi Anda tidak dapat berfungsi tanpa update, pertimbangkan untuk menampilkan pesan informatif sebelum memulai ulang alur update atau meminta pengguna untuk menutup aplikasi. Dengan begitu, pengguna memahami bahwa mereka dapat meluncurkan kembali aplikasi Anda saat mereka siap untuk menginstal update yang diperlukan.

Langkah berikutnya

Uji update dalam aplikasi pada aplikasi Anda untuk memastikan bahwa integrasi berfungsi dengan benar.