Quản lý công việc

Sau khi bạn xác định WorkerWorkRequest, bước sau cùng là thêm công việc vào hàng đợi. Cách đơn giản nhất để thêm công việc vào hàng đợi là gọi phương thức enqueue() trong WorkManager và chuyển WorkRequest mà bạn muốn thực hiện.

Kotlin


val myWork: WorkRequest = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork)

Java


WorkRequest myWork = // ... OneTime or PeriodicWork
WorkManager.getInstance(requireContext()).enqueue(myWork);

Hãy thận trọng khi thêm công việc vào hàng đợi để tránh trùng lặp. Ví dụ: một ứng dụng có thể cố gắng tải nhật ký lên dịch vụ phụ trợ sau mỗi 24 giờ. Nếu không cẩn thận, bạn có thể thêm cùng một tác vụ vào hàng đợi nhiều lần, mặc dù chỉ cần thực hiện công việc này một lần. Để đạt được mục tiêu này, bạn có thể lên lịch công việc dưới dạng công việc duy nhất.

Công việc duy nhất

Công việc duy nhất là một khái niệm rõ ràng, giúp đảm bảo bạn chỉ có một phiên bản công việc với tên gọi cụ thể tại một thời điểm. Khác với mã nhận dạng, tên riêng là để mọi người đọc được và do nhà phát triển chỉ định thay vì do WorkManager tạo tự động. Khác với thẻ, tên riêng chỉ liên kết với một phiên bản của công việc.

Bạn có thể áp dụng công việc duy nhất cho cả công việc một lần và công việc định kỳ. Bạn có thể tạo trình tự công việc duy nhất bằng cách gọi một trong các phương thức sau đây, tuỳ theo việc bạn đang lên lịch công việc lặp lại hay công việc một lần.

Cả hai phương thức này đều chấp nhận 3 đối số:

  • uniqueWorkNameString dùng để xác định từng yêu cầu công việc.
  • existingWorkPolicyenum cho WorkManager biết việc cần làm nếu hiện có chuỗi công việc chưa hoàn thành với tên riêng đó. Xem chính sách xử lý xung đột để biết thêm thông tin.
  • workWorkRequest để lên lịch.

Bằng cách sử dụng công việc duy nhất, chúng tôi có thể khắc phục sự cố lên lịch trùng lặp được nêu ở phần trước.

Kotlin


val sendLogsWorkRequest =
       PeriodicWorkRequestBuilder<SendLogsWorker>(24, TimeUnit.HOURS)
           .setConstraints(Constraints.Builder()
               .setRequiresCharging(true)
               .build()
            )
           .build()
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
           "sendLogs",
           ExistingPeriodicWorkPolicy.KEEP,
           sendLogsWorkRequest
)

Java


PeriodicWorkRequest sendLogsWorkRequest = new
      PeriodicWorkRequest.Builder(SendLogsWorker.class, 24, TimeUnit.HOURS)
              .setConstraints(new Constraints.Builder()
              .setRequiresCharging(true)
          .build()
      )
     .build();
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
     "sendLogs",
     ExistingPeriodicWorkPolicy.KEEP,
     sendLogsWorkRequest);

Hiện tại, nếu mã chạy khi có công việc sendLogs trong hàng đợi, hệ thống sẽ giữ lại công việc hiện tại và không thêm công việc mới vào.

Trình tự công việc duy nhất cũng có thể giúp ích nếu bạn cần xây dựng dần chuỗi tác vụ dài. Ví dụ: Một ứng dụng chỉnh sửa ảnh có thể cho phép người dùng huỷ một chuỗi thao tác dài. Sẽ mất một chút thời gian để thực hiện từng thao tác huỷ, nhưng phải tiến hành theo đúng thứ tự. Trong trường hợp này, ứng dụng có thể tạo một chuỗi "huỷ" và thêm từng thao tác huỷ vào đầu chuỗi đó (nếu cần). Xem phần Chuỗi công việc để biết thêm thông tin chi tiết.

Chính sách xử lý xung đột

Khi lên lịch công việc duy nhất, bạn phải cho WorkManager biết hành động cần làm khi xảy ra xung đột. Bạn thực hiện việc này bằng cách chuyển một enum khi thêm công việc vào hàng đợi.

Đối với công việc làm một lần, bạn cung cấp ExistingWorkPolicy để hỗ trợ 4 lựa chọn xử lý xung đột.

  • REPLACE công việc hiện tại bằng công việc mới. Lựa chọn này sẽ hủy công việc hiện tại.
  • KEEP công việc hiện tại và bỏ qua công việc mới.
  • APPEND công việc mới vào cuối công việc hiện tại. Chính sách này sẽ giúp công việc mới liên kết với công việc hiện tại và chạy sau khi công việc hiện tại hoàn thành.

Công việc hiện tại trở thành điều kiện tiên quyết cho công việc mới. Nếu công việc hiện tại bị CANCELLED hoặc FAILED, công việc mới cũng sẽ bị CANCELLED hoặc FAILED. Thay vào đó, nếu bạn muốn thực hiện công việc mới bất kể trạng thái của công việc hiện tại, hãy dùng APPEND_OR_REPLACE.

  • APPEND_OR_REPLACE có chức năng tương tự như APPEND, ngoại trừ việc chức năng này không phụ thuộc vào trạng thái công việc tiên quyết. Nếu công việc hiện tại là CANCELLED hoặc FAILED, công việc mới vẫn chạy.

Đối với công việc định kỳ, bạn cung cấp ExistingPeriodicWorkPolicy để hỗ trợ 2 lựa chọn: REPLACEKEEP. Những lựa chọn này có chức năng giống với các lựa chọn tương ứng trong ExistingWorkPolicy.

Quan sát công việc

Bất kỳ lúc nào sau khi thêm công việc vào hàng đợi, bạn có thể kiểm tra trạng thái công việc bằng cách truy vấn WorkManager theo name, id hoặc theo tag gắn với công việc đó.

Kotlin


// by id
workManager.getWorkInfoById(syncWorker.id) // ListenableFuture<WorkInfo>

// by name
workManager.getWorkInfosForUniqueWork("sync") // ListenableFuture<List<WorkInfo>>

// by tag
workManager.getWorkInfosByTag("syncTag") // ListenableFuture<List<WorkInfo>>

Java


// by id
workManager.getWorkInfoById(syncWorker.id); // ListenableFuture<WorkInfo>

// by name
workManager.getWorkInfosForUniqueWork("sync"); // ListenableFuture<List<WorkInfo>>

// by tag
workManager.getWorkInfosByTag("syncTag"); // ListenableFuture<List<WorkInfo>>

Truy vấn trả về ListenableFuture của đối tượng WorkInfo, bao gồm id công việc, thẻ, State hiện tại và bất kỳ tập dữ liệu đầu ra nào qua Result.success(outputData).

Biến thể LiveData của từng phương thức cho phép bạn quan sát sự thay đổi đối với WorkInfo bằng cách đăng ký trình nghe. Ví dụ: Nếu muốn hiển thị thông báo cho người dùng khi hoàn thành xong một số công việc, bạn có thể thiết lập như sau:

Kotlin


workManager.getWorkInfoByIdLiveData(syncWorker.id)
               .observe(viewLifecycleOwner) { workInfo ->
   if(workInfo?.state == WorkInfo.State.SUCCEEDED) {
       Snackbar.make(requireView(),
      R.string.work_completed, Snackbar.LENGTH_SHORT)
           .show()
   }
}

Java


workManager.getWorkInfoByIdLiveData(syncWorker.id)
        .observe(getViewLifecycleOwner(), workInfo -> {
    if (workInfo.getState() != null &&
            workInfo.getState() == WorkInfo.State.SUCCEEDED) {
        Snackbar.make(requireView(),
                    R.string.work_completed, Snackbar.LENGTH_SHORT)
                .show();
   }
});

Truy vấn công việc phức tạp

WorkManager 2.4.0 trở lên hỗ trợ truy vấn phức tạp cho các công việc trong hàng đợi bằng cách sử dụng đối tượng của WorkQuery. WorkQuery hỗ trợ hoạt động truy vấn công việc bằng cách kết hợp (các) thẻ, trạng thái và tên riêng của công việc đó.

Ví dụ sau hướng dẫn bạn cách tìm tất cả công việc bằng thẻ “syncTag”, ở trạng thái FAILED hoặc CANCELLED và có tên riêng của công việc là “preProcess” hoặc “sync”.

Kotlin


val workQuery = WorkQuery.Builder
       .fromTags(listOf("syncTag"))
       .addStates(listOf(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
       .addUniqueWorkNames(listOf("preProcess", "sync")
    )
   .build()

val workInfos: ListenableFuture<List<WorkInfo>> = workManager.getWorkInfos(workQuery)

Java


WorkQuery workQuery = WorkQuery.Builder
       .fromTags(Arrays.asList("syncTag"))
       .addStates(Arrays.asList(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
       .addUniqueWorkNames(Arrays.asList("preProcess", "sync")
     )
    .build();

ListenableFuture<List<WorkInfo>> workInfos = workManager.getWorkInfos(workQuery);

Mỗi thành phần (thẻ, trạng thái hoặc tên) trong WorkQueryAND-ed với các thành phần khác. Từng giá trị trong thành phần đều là OR-ed. Ví dụ: (name1 OR name2 OR ...) AND (tag1 OR tag2 OR ...) AND (state1 OR state2 OR ...).

WorkQuery cũng hoạt động với mã tương đương trong LiveData là getWorkInfosLiveData().

Huỷ và dừng công việc

Nếu không cần thực hiện công việc đã thêm vào hàng đợi trước đó, bạn có thể yêu cầu huỷ. Hệ thống có thể huỷ công việc theo name, id hoặc theo tag gắn với công việc đó.

Kotlin


// by id
workManager.cancelWorkById(syncWorker.id)

// by name
workManager.cancelUniqueWork("sync")

// by tag
workManager.cancelAllWorkByTag("syncTag")

Java


// by id
workManager.cancelWorkById(syncWorker.id);

// by name
workManager.cancelUniqueWork("sync");

// by tag
workManager.cancelAllWorkByTag("syncTag");

WorkManager nâng cao sẽ bí mật kiểm tra State công việc. Nếu công việc đã hoàn tất, thì sẽ không có điều gì xảy ra. Nếu không, trạng thái công việc sẽ đổi thành CANCELLED và không thể thực hiện công việc trong tương lai. Mọi công việc WorkRequest phụ thuộc vào công việc này cũng sẽ bị CANCELLED.

Hiện tại, công việc RUNNING sẽ nhận được lệnh gọi đến ListenableWorker.onStopped(). Ghi đè phương thức này để xử lý mọi tình huống xoá có thể xảy ra. Xem phần dừng Worker đang chạy để biết thêm thông tin.

Dừng Worker đang chạy

Sau đây là một vài lý do khác nhau khiến WorkManager dừng Worker đang chạy của bạn:

  • Bạn yêu cầu huỷ công việc một cách rõ ràng (ví dụ: bằng cách gọi WorkManager.cancelWorkById(UUID)).
  • Trong trường hợp là công việc duy nhất, bạn rõ ràng đã thêm WorkRequest mới vào hàng đợi bằng ExistingWorkPolicy của REPLACE. Ngay lập tức, WorkRequest cũ sẽ bị coi là huỷ.
  • Các quy tắc ràng buộc của công việc không được đáp ứng nữa.
  • Hệ thống đã hướng dẫn ứng dụng của bạn dừng công việc vì một số lý do. Trường hợp này có thể xảy ra nếu bạn trễ hạn thực thi 10 phút. Công việc này sẽ được lên lịch để thực hiện lại sau.

Trong những tình huống này, Worker sẽ dừng lại.

Bạn nên phối hợp huỷ bỏ mọi công việc đang tiến hành và giải phóng tài nguyên mà Worker đang nắm giữ. Ví dụ: Vào thời điểm này, bạn nên đóng các quy trình xử lý đang mở đối với cơ sở dữ liệu và tệp. Có hai cơ chế bạn có thể áp dụng để biết được khi nào Worker dừng lại.

Gọi lại onStopped()

WorkManager gọi ListenableWorker.onStopped() ngay khi Worker dừng lại. Hãy ghi đè phương thức này để đóng mọi tài nguyên bạn có thể đang nắm giữ.

Thuộc tính isStopped()

Bạn có thể gọi phương thức ListenableWorker.isStopped() để kiểm tra xem worker đã dừng hay chưa. Nếu đang thực hiện các thao tác lâu dài hoặc lặp đi lặp lại trong Worker, bạn nên kiểm tra thuộc tính này thường xuyên và dùng thuộc tính này làm tín hiệu để dừng công việc càng sớm càng tốt.

Lưu ý: WorkManager bỏ qua Result do Worker đặt đã nhận được tín hiệu onStop, vì Worker này bị coi là đã dừng hoạt động.