Hỗ trợ cập nhật trong ứng dụng (Kotlin hoặc Java)

Bài viết này hướng dẫn cách hỗ trợ cập nhật trong ứng dụng bằng Kotlin hoặc Java. Bên cạnh đó, cũng có hướng dẫn riêng cho các trường hợp trong đó quá trình triển khai sử dụng mã gốc (C/C++) và các trường hợp mà quá trình triển khai sử dụng Unity.

Kiểm tra xem có bản cập nhật mới chưa

Trước khi yêu cầu cập nhật, hãy kiểm tra xem có bản cập nhật cho ứng dụng của bạn hay không. Sử dụng AppUpdateManager để kiểm tra bản cập nhật:

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

Thực thể AppUpdateInfo được trả về chứa trạng thái sẵn sàng của bản cập nhật. Tuỳ thuộc vào trạng thái của bản cập nhật, thực thể cũng chứa các nội dung sau:

  • Nếu có bản cập nhật và cho phép cập nhật, thực thể này cũng chứa ý định để bắt đầu quá trình cập nhật.
  • Nếu đang cập nhật ứng dụng, thực thể đó cũng sẽ báo cáo trạng thái tiến trình cập nhật.

Kiểm tra tình trạng lỗi thời của bản cập nhật

Ngoài việc kiểm tra xem có bản cập nhật hay không, bạn cũng nên kiểm tra xem bao nhiêu thời gian đã qua kể từ khi người dùng nhận được thông báo gần đây nhất về bản cập nhật thông qua Cửa hàng Play. Việc này có thể giúp bạn quyết định xem bạn nên bắt đầu lên lịch cập nhật linh hoạt hay cập nhật tức thì. Ví dụ như bạn có thể chờ một vài ngày trước khi thông báo cho người dùng về bản cập nhật linh hoạt và vài ngày sau đó mới yêu cầu cập nhật tức thì.

Sử dụng clientVersionStalenessDays() để kiểm tra số ngày kể từ khi Cửa hàng Play có bản cập nhật:

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

Kiểm tra mức độ ưu tiên của bản cập nhật

API Nhà phát triển Google Play cho phép bạn thiết lập mức độ ưu tiên của mỗi bản cập nhật. Điều này cho phép ứng dụng quyết định mức độ đề xuất bản cập nhật cho người dùng. Hãy cân nhắc ví dụ sau đây về chiến lược thiết lập mức độ ưu tiên cập nhật:

  • Cải tiến nhỏ về giao diện người dùng: Bản cập nhật ưu tiên thấp; không yêu cầu cập nhật linh hoạt cũng như cập nhật tức thì. Chỉ cập nhật khi người dùng không tương tác với ứng dụng.
  • Cải thiện hiệu suất: Bản cập nhật ưu tiên trung bình; yêu cầu cập nhật linh hoạt.
  • Bản cập nhật bảo mật quan trọng: Bản cập nhật ưu tiên cao; yêu cầu cập nhật tức thì.

Để xác định mức độ ưu tiên, Google Play sử dụng giá trị số nguyên từ 0 đến 5, trong đó 0 là giá trị mặc định và 5 là mức độ ưu tiên cao nhất. Để thiết lập mức độ ưu tiên cho một bản cập nhật, hãy sử dụng trường inAppUpdatePriority trong Edits.tracks.releases của API nhà phát triển Google Play. Tất cả các phiên bản mới thêm vào trong bản phát hành đều có mức độ ưu tiên tương tự với bản phát hành. Bạn chỉ có thể thiết lập mức độ ưu tiên vào thời điểm ra mắt bản phát hành mới và không thể thay đổi về sau.

Thiết lập mức độ ưu tiên bằng cách sử dụng API Nhà phát triển Google Play như được mô tả trong tài liệu API Nhà phát triển Play. Mức độ ưu tiên của bản cập nhật trong ứng dụng phải được chỉ định trong tài nguyên Edit.tracks được truyền trong phương thức Edit.tracks: update. Ví dụ sau minh hoạ việc phát hành ứng dụng có mã phiên bản 88 và inAppUpdatePriority 5:

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

Trong mã của ứng dụng, bạn có thể kiểm tra mức độ ưu tiên của một bản cập nhật cụ thể bằng updatePriority(). Mức độ ưu tiên được trả về cân nhắc inAppUpdatePriority của tất cả các mã phiên bản ứng dụng trong khoảng thời gian từ khi cài đặt phiên bản đến khi có phiên bản mới nhất.

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

Bắt đầu cập nhật

Sau khi xác nhận đã có bản cập nhật mới, bạn có thể yêu cầu cập nhật bằng AppUpdateManager.startUpdateFlowForResult():

Kotlin

appUpdateManager.startUpdateFlowForResult(
    // Pass the intent that is returned by 'getAppUpdateInfo()'.
    appUpdateInfo,
    // Or 'AppUpdateType.FLEXIBLE' for flexible updates.
    AppUpdateType.IMMEDIATE,
    // The current activity making the update request.
    this,
    // Include a request code to later monitor this update request.
    MY_REQUEST_CODE)

Java

appUpdateManager.startUpdateFlowForResult(
    // Pass the intent that is returned by 'getAppUpdateInfo()'.
    appUpdateInfo,
    // Or 'AppUpdateType.FLEXIBLE' for flexible updates.
    AppUpdateType.IMMEDIATE,
    // The current activity making the update request.
    this,
    // Include a request code to later monitor this update request.
    MY_REQUEST_CODE);

Mỗi thực thể AppUpdateInfo chỉ có thể được sử dụng một lần để bắt đầu cập nhật. Để thử cập nhật lại trong trường hợp không thành công, hãy yêu cầu AppUpdateInfo mới và kiểm tra lại xem đã có bản cập nhật hay chưa và có được phép cập nhật hay không.

Các bước tiếp theo phụ thuộc vào việc bạn yêu cầu cập nhật linh hoạt hay cập nhật tức thì.

Định cấu hình cập nhật bằng AppUpdateOptions

Ngoài ra, bạn có thể tạo và truyền đối tượng AppUpdateOptions thay vì sử dụng quy trình cập nhật rõ ràng. Bên cạnh trường appUpdateType, các đối tượng AppUpdateOptions còn chứa một trường AllowAssetPackDeletion giúp xác định xem có cho phép cập nhật để xoá gói tài sản hay không trong trường hợp bộ nhớ của thiết bị bị hạn chế. Theo mặc định, trường này được đặt giá trị là false. Tuy nhiên, bạn có thể sử dụng phương thức setAllowAssetPackDeletion() để đặt thành true:

Kotlin

appUpdateManager.startUpdateFlowForResult(
    // Pass the intent that is returned by 'getAppUpdateInfo()'.
    appUpdateInfo,
    // The current activity making the update request.
    this,
    // Or pass 'AppUpdateType.FLEXIBLE' to newBuilder() for
    // flexible updates.
    AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE)
        .setAllowAssetPackDeletion(true)
        .build(),
    // Include a request code to later monitor this update request.
    MY_REQUEST_CODE)

Java

appUpdateManager.startUpdateFlowForResult(
    // Pass the intent that is returned by 'getAppUpdateInfo()'.
    appUpdateInfo,
    // The current activity making the update request.
    this,
    // Or pass 'AppUpdateType.FLEXIBLE' to newBuilder() for
    // flexible updates.
    AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE)
        .setAllowAssetPackDeletion(true)
        .build(),
    // Include a request code to later monitor this update request.
    MY_REQUEST_CODE);

Nhận lệnh gọi lại để biết trạng thái cập nhật

Sau khi bắt đầu cập nhật, bạn có thể sử dụng lệnh gọi lại onActivityResult() để xử lý lỗi hoặc huỷ cập nhật:

Kotlin

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if (requestCode == MY_REQUEST_CODE) {
        if (resultCode != RESULT_OK) {
            Log.e("MY_APP", "Update flow failed! Result code: $resultCode")
            // If the update is cancelled or fails,
            // you can request to start the update again.
        }
    }
}

Java

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
  if (requestCode == MY_REQUEST_CODE) {
    if (resultCode != RESULT_OK) {
      log("Update flow failed! Result code: " + resultCode);
      // If the update is cancelled or fails,
      // you can request to start the update again.
    }
  }
}

Bạn có thể nhận được một số giá trị khi thực hiện lệnh gọi lại onActivityResult():

  • RESULT_OK: Người dùng đã chấp nhận bản cập nhật. Đối với các bản cập nhật tức thì, bạn có thể không nhận được lệnh gọi lại này vì quá trình cập nhật đã hoàn tất trước khi ứng dụng có thể kiểm soát trở lại.
  • RESULT_CANCELED: Người dùng từ chối hoặc huỷ cập nhật.
  • ActivityResult.RESULT_IN_APP_UPDATE_FAILED: Một số lỗi khác ngăn người dùng đồng ý hoặc cập nhật.

Xử lý bản cập nhật linh hoạt

Khi bạn bắt đầu chạy bản cập nhật linh hoạt, một hộp thoại sẽ xuất hiện đầu tiên để yêu cầu người dùng đồng ý cập nhật. Nếu người dùng đồng ý, thì quá trình tải xuống sẽ bắt đầu trong nền và người dùng có thể tiếp tục tương tác với ứng dụng. Phần dưới đây mô tả cách theo dõi và hoàn tất quá trình cập nhật linh hoạt trong ứng dụng.

Theo dõi trạng thái cập nhật linh hoạt

Sau khi quá trình tải xuống bản cập nhật linh hoạt bắt đầu, ứng dụng cần theo dõi trạng thái cập nhật để biết khi nào có thể cài đặt bản cập nhật và hiển thị tiến trình thực hiện trong giao diện người dùng của ứng dụng.

Bạn có thể theo dõi trạng thái tiến trình cập nhật bằng cách đăng ký trình nghe để cập nhật trạng thái cài đặt. Bạn cũng có thể cung cấp thanh tiến trình trong giao diện người dùng của ứng dụng để người dùng nắm bắt tiến trình tải xuống.

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

Cài đặt bản cập nhật linh hoạt

Khi phát hiện trạng thái InstallStatus.DOWNLOADED, bạn cần khởi động lại ứng dụng để cài đặt bản cập nhật.

Khác với các bản cập nhật tức thì, Google Play không tự động kích hoạt quá trình khởi động lại ứng dụng đối với bản cập nhật linh hoạt. Nguyên nhân là do trong quá trình cập nhật linh hoạt, người dùng vẫn muốn tiếp tục tương tác với ứng dụng cho đến khi họ quyết định cài đặt bản cập nhật.

Bạn nên cung cấp thông báo (hoặc một số chỉ báo giao diện người dùng khác) để cho người dùng biết rằng bản cập nhật đã sẵn sàng cài đặt và yêu cầu xác nhận trước khi khởi động lại ứng dụng.

Ví dụ sau minh hoạ cách triển khai thanh thông báo nhanh trong Material Design mà yêu cầu người dùng xác nhận để khởi động lại ứng dụng:

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

Khi gọi appUpdateManager.completeUpdate() ở nền trước, nền tảng sẽ hiển thị giao diện người dùng toàn màn hình và sẽ tiến hành khởi động lại ứng dụng trong nền. Sau khi nền tảng hoàn tất cài đặt bản cập nhật, ứng dụng sẽ khởi động lại và trở thành hoạt động chính của nền tảng.

Thay vào đó, nếu gọi completeUpdate() khi ứng dụng ở chế độ nền, thì bản cập nhật sẽ được tự động cài đặt mà không che khuất giao diện người dùng của thiết bị.

Bất cứ khi nào người dùng đưa ứng dụng của bạn lên nền trước, hãy kiểm tra xem ứng dụng có bản cập nhật đang chờ cài đặt hay không. Nếu ứng dụng có bản cập nhật ở trạng thái DOWNLOADED, hãy nhắc người dùng cài đặt bản cập nhật. Nếu không, dữ liệu cập nhật sẽ tiếp tục chiếm dung lượng lưu trữ trên thiết bị của người dùng.

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

Xử lý bản cập nhật tức thì

Khi bắt đầu quá trình cập nhật tức thì và người dùng đồng ý bắt đầu cập nhật, Google Play sẽ hiển thị tiến trình cập nhật ở trên cùng giao diện người dùng của ứng dụng trong suốt toàn bộ thời gian cập nhật. Nếu người dùng đóng hoặc dừng ứng dụng trong quá trình cập nhật, bản cập nhật sẽ tiếp tục tải xuống và cài đặt trong nền mà không cần người dùng xác nhận bổ sung.

Tuy nhiên, khi ứng dụng quay lại chế độ nền trước, bạn nên xác nhận rằng bản cập nhật không bị trì hoãn ở trạng thái UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS. Nếu quá trình cập nhật bị trì hoãn trong trạng thái này, hãy tiếp tục quá trình cập nhật:

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,
                    IMMEDIATE,
                    this,
                    MY_REQUEST_CODE
                )
            }
        }
}

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,
                    IMMEDIATE,
                    this,
                    MY_REQUEST_CODE);
            }
          });
}

Quá trình cập nhật sẽ trả về kết quả như được mô tả trong tài liệu tham khảo cho startUpdateFlowForResult(). Cụ thể, ứng dụng phải có thể xử lý các trường hợp mà người dùng từ chối cập nhật hoặc huỷ quá trình tải xuống. Khi người dùng thực hiện một trong những hành động này, giao diện người dùng của Google Play sẽ đóng lại. Ứng dụng lúc đó sẽ cần xác định cách tốt nhất để tiếp tục.

Nếu có thể, hãy cho phép người dùng tiếp tục mà không cần cập nhật và nhắc họ cập nhật sau. Nếu ứng dụng của bạn không thể hoạt động nếu không được cập nhật, hãy cân nhắc hiển thị một thông báo thông tin trước khi bắt đầu lại quy trình cập nhật hoặc nhắc người dùng đóng ứng dụng. Khi đó, người dùng sẽ biết rằng họ có thể chạy lại ứng dụng một khi đã sẵn sàng cài đặt bản cập nhật bắt buộc.

Các bước tiếp theo

Kiểm thử bản cập nhật trong ứng dụng cho ứng dụng để xác minh rằng quá trình tích hợp đang hoạt động chính xác.