Cách phân phối bản cập nhật Android đến người dùng.

Xin chào tất cả các bạn, hôm nay chúng ta sẽ cùng nhau tìm hiểu cách phân phối các bản cập nhật Android đến tay người dùng.

1. Tạo sao việc phân phối bản cập nhật này lại quan trọng?

Trong quá trình phát triển ứng dụng, không ít thì nhiều cũng phát sinh những bug không mong muốn hoặc có thể là team phát triển quyết định tung ra một tính năng mới sau một thời gian dài phát triển. Đã bao nhiêu lần bạn phải update một bản hot-fix cho ứng dụng và cầu nguyện rằng tất cả người dùng của bạn sẽ cập nhật, hoặc một tính năng mà bạn cho rằng nó sẽ "Make your app greate again" mà cuối cùng không ai update.Mình chắc chắn bạn đã ở trong tình huống đó một vài lần, và nó khá là đau đầu khi chúng ta không có một cơ chế thực sự để bắt người dùng update lên bản mới mặc dù bản cũ mắc phải một lỗi vô cùng ngớ ngẩn hoặc một tính năng tuyệt vời mà không ai xài đến.

2. Vậy trước đây các developer giải quyết vấn đề này như thế nào?

Thực ra thì mình không dám khẳng định là tất cả mọi developer đều giải quyết như thế này nhưng khá nhiều dự án mình được biết sử dụng một cách đó là khi người dùng khởi động ứng dụng chúng ta sẽ lấy version của ứng dụng và gửi về back-end và sẽ check xem phiên bản đó có được hỗ trợ không. Nếu được phiên bản được hỗ trợ không có vấn đề gì cả còn nếu không thì sẽ hiển thị một thông báo update cho người dùng và đưa họ đến cửa hàng để cập nhât. Ok vậy mọi thứ có vẻ đều ổn vậy thì có vấn đề gì, vấn đề ở đây là chúng ta phải xử lý quá nhiều công việc cả ở client và back-end, và người dùng khi được đưa đến store để cập nhật app không có gì đảm bảo là họ sẽ click update như bạn mong muốn. Bạn không cần lo lắng về điều đó vì Google đã có giải pháp cho chúng ta in-app updates. Sử dụng tính năng của thư viên Google Play Core.

  • Lưu ý: Tính năng này chỉ hoạt động từ phiên bản Android 5.0 trở lên và yêu cầu thư viện Play Core 1.5.0 trở lên.
  • Thêm thư viện: implementation 'com.google.android.play:core:1.6.1' (phiên bản mới hơn có thể được phát hành bạn có thể check trong trang android develop).

3. Các loại cập nhật hỗ trợ.

3.1. Flexible update (không bắt buộc).

In-app updates sẽ download và cài đặt bản cập nhật ứng dụng ở dưới background. Tùy chọn này thường phù hợp trong trường hợp bạn phát hành một tính năng mới và mong muốn người dùng sẽ cập nhật để trải nghiệm tính năng này. Điều này có nghĩa là nó cho phép người dùng có thể chọn update hoặc không, với các bản hot-fix sẽ cần tùy chọn khác.

3.2. Immediate update(bắt buộc).

Như đã trình bày ở trên, mong muốn của developer với các bản hot-fix là tất cả người dùng bắt buộc phải cập nhật ứng dụng thì mới được tiếp tục sử dụng ứng dụng, in-app updates cũng có thể làm được điều này bằng cách cung cấp một giao diện thông báo cập nhật bắt buộc cho người dùng. Ứng dụng sẽ ngay lập tức được cập nhật khi người dùng đồng ý, và dĩ nhiên như đã trình bày, tùy chọn này thường sẽ phù hợp với các bản update mang tính chất vá lỗi quan trọng yêu cầu tất cả người dùng phải cập nhật. Lưu ý: Khi bạn phát hành một ứng dụng dưới hình thức App Bundle dung lượng bản cập nhật (đã nén) tối đa mà bạn có thể update là 150MB. Một điều nữa là in-app updates sẽ không hoạt động với các file ứng dụng (APK) có sử dụng tập mở rộng (file .obb).

4. Cách cài đặt.

4.1. Check phiên bản update.

Trước khi đưa ra yêu cầu cập nhật ứng dụng tới người dùng chúng ta phải chắc chắn rằng bản cập nhật cho ứng dụng đã sắn sàng. Chúng ta sẽ sử dụng AppUpdateManager để kiếm tra. Tại đây chúng ta sẽ có 4 trạng thái có thể có đó là:

  • DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS
  • UNKNOWN
  • UPDATE_AVAILABLE
  • UPDATE_NOT_AVAILABLE Khi trạng thái là availble ta sẽ tiến hành cập nhật:
// Creates instance of the manager.
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
          // For a flexible update, use AppUpdateType.FLEXIBLE
          && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
              // Request the update.
    }
});

Kết quả trả vể có chứa trạng thái sẵn sàng của bản cập nhật. Nếu bản cập nhật đã sẵn sàng và hợp lệ kết quả trả về AppUpdateInfo sẽ có chứa Intent để cập nhật ứng dụng. Nếu một bản cập nhật ứng dụng đang được cài đặt, kết quả cũng sẽ hiển thị trạng thái của bản cập nhật đang thực hiện.

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

Sau khi đã chắn chắn mọi thứ sẵn sàng cho cập nhật, bạn có thể yêu cầu cập nhật bằng cách sử dụng AppUpdateManager.startUpdateFlowForResult() như sau:

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

Bạn chỉ cần yêu cầu cập nhật một lần trừ khi trong quá trình cập nhật bị thất bại. Nếu phát hiện ra quá trình cập nhật không thành công, bạn có thể yêu cầu cập nhật lại. Việc yêu cầu cập nhật này có thể gây phiền toái cho người dùng nên bạn cần cân nhắc về thời điểm cập nhật và cách thức cập nhật để đảm bảo trải nghiệm người dùng được tốt nhất.

4.3. Cập nhật trạng thái cập nhật.

Sau khi yêu cầu và bắt đầu quá trình cập nhật bạn có thể theo dõi cũng như can thiệp vào quá trình này bằng cách:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
  if (myRequestCode == MY_REQUEST_CODE) {
    if (myRequestCode != RESULT_OK) {
      log("Update flow failed! Result code: " + resultCode);
      // Nếu cập nhật thất bạn hoặc bị hủy, bạn có thể yêu cầu lại ở đây.
    }
  }
}

Các giá trị trả về có thể là:

  • 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 ngay lập tức, bạn có thể không nhận được giá trị trả về này vì bản cập nhật đã được Google Play hoàn thành.
  • RESULT_CANCELED: Người dùng đã từ chối hoặc hủy bản cập nhật.
  • ActivityResult.RESULT_IN_APP_UPDATE_FAILED: Một số lỗi khác khiến người dùng không đồng ý hoặc cập nhật không thể tiếp tục.

4.4 Bắt đầu flexible update.

Với cập nhật linh hoạt người dùng ở đây sẽ có tùy chọn hủy bản cập nhật và tiếp tục sử dụng ứng dụng. Nếu nhấn nút Cập nhật, Trình quản lý tải xuống sẽ khởi động và tải xuống bản cập nhật trong tiến trình nền. Để theo dõi trạng thái của bản cập nhật, bạn cần truy cập vào InstallStateUpdateListener.

val appUpdateManager = AppUpdateManagerFactory.create(context)
val listener = InstallStateUpdatedListener {
    //...
}
updateManager.registerListener(listener)
// Unregister khi cập nhật hoàn tất
updateManager.unregisterListener(listener)

Nếu bạn theo dõi trạng thái cập nhật và trạng thái trẻ về là InstallStatus.DOWNLOADED, bạn cần khởi động lại ứng dụng để cài đặt bản cập nhật. Không giống như một bản cập nhật bắt buộc, Google Play sẽ không khởi động lại ứng dụng cho bạn. Điều đó là bởi vì trong quá trình cập nhật người dùng vẫn có thể tiếp tục sử dụng ứng dụng của bạn. Vì vậy, bạn nên cung cấp một thông báo cho người dùng rằng bản cài đặt đã sẵn sàng và yêu cầu xác nhận của người dùng để khởi động lại ứng dụng:

@Override
public void onStateUpdate(InstallState state) {
  if (state.installStatus() == InstallStatus.DOWNLOADED) {
    // Sau khi download bản cập nhật hoàn tất hiển thị thông báo để yêu cầu cài đặt
    popupSnackbarForCompleteUpdate();
  }
  ...
}

//Thông báo yêu cầu cài đặt.
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();
}

4.5 Bắt đầu immediate update.

Nếu bạn đang thực hiện cập nhật bắt buộc và người dùng đồng ý cài đặt bản cập nhật, Google Play sẽ hiển thị tiến trình cập nhật trên giao diện người dùng ứng dụng của bạn trong toàn bộ thời gian cập nhật. Trong quá trình cập nhật, nếu người dùng đóng hoặc chấm dứt ứng dụng của bạn, 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 xác nhận thêm của người dùng. Tuy nhiên, để đảm bảo mọi thứ hoạt động như mong muốn bạn vẫn cần kiểm tra UpdateAvcellence.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS. Nếu cập nhật ở trạng thái này, bạn cần tiếp tiếp tục cập nhật bằng cách:

@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.
                manager.startUpdateFlowForResult(
                    appUpdateInfo,
                    IMMEDIATE,
                    this,
                    MY_REQUEST_CODE);
            }
          });
}

5. Tổng kết.

Cảm ơn các bạn đã theo dõi bài viết. Các bạn cũng có thể tham khảo nguồn tại trang https://developer.android.com/guide