Các chủ đề nâng cao về WorkManager

WorkManager giúp bạn dễ dàng thiết lập và lên lịch các yêu cầu tác vụ phức tạp. Bạn có thể sử dụng API cho các tình huống như sau:

Tác vụ theo chuỗi

Ứng dụng của bạn có thể cần chạy nhiều tác vụ theo thứ tự cụ thể. WorkManager cho phép bạn tạo và đưa trình tự công việc vào hàng đợi để chỉ định nhiều tác vụ và xác định thứ tự thực hiện các tác vụ.

Ví dụ: Giả sử ứng dụng của bạn có 3 đối tượng OneTimeWorkRequest: workA, workBworkC. Các tác vụ phải chạy theo thứ tự đó. Để thêm các đối tượng này vào hàng đợi, hãy tạo một trình tự bằng phương thức WorkManager.beginWith(OneTimeWorkRequest), truyền đối tượng OneTimeWorkRequest đầu tiên. Phương thức đó trả về một đối tượng WorkContinuation, giúp xác định một trình tự của các tác vụ. Sau đó, hãy thêm các đối tượng OneTimeWorkRequest còn lại theo thứ tự bằng phương thức WorkContinuation.then(OneTimeWorkRequest). Cuối cùng, hãy đưa toàn bộ trình tự vào hàng đợi bằng phương thức WorkContinuation.enqueue():

Kotlin

WorkManager.getInstance(myContext)
    .beginWith(workA)
        // Note: WorkManager.beginWith() returns a
        // WorkContinuation object; the following calls are
        // to WorkContinuation methods
    .then(workB)    // FYI, then() returns a new WorkContinuation instance
    .then(workC)
    .enqueue()

Java

WorkManager.getInstance(myContext)
    .beginWith(workA)
        // Note: WorkManager.beginWith() returns a
        // WorkContinuation object; the following calls are
        // to WorkContinuation methods
    .then(workB)    // FYI, then() returns a new WorkContinuation instance
    .then(workC)
    .enqueue();

WorkManager chạy các tác vụ theo thứ tự mà bạn yêu cầu, theo các điều kiện ràng buộc đã chỉ định cho tác vụ. Nếu bất kỳ tác vụ nào trả về kết quả Result.failure() thì toàn bộ trình tự sẽ kết thúc.

Bạn cũng có thể truyền nhiều đối tượng OneTimeWorkRequest đến bất kỳ lệnh gọi nào trong số 2 lệnh beginWith(List<OneTimeWorkRequest>)then(List<OneTimeWorkRequest>). Nếu bạn truyền nhiều đối tượng OneTimeWorkRequest đến một lệnh gọi phương thức duy nhất, thì WorkManager sẽ chạy tất cả tác vụ đó (song) trước khi chạy phần còn lại của trình tự. Ví dụ:

Kotlin

WorkManager.getInstance(myContext)
    // First, run all the A tasks (in parallel):
    .beginWith(Arrays.asList(workA1, workA2, workA3))
    // ...when all A tasks are finished, run the single B task:
    .then(workB)
    // ...then run the C tasks (in parallel):
    .then(Arrays.asList(workC1, workC2))
    .enqueue()

Java

WorkManager.getInstance(myContext)
    // First, run all the A tasks (in parallel):
    .beginWith(Arrays.asList(workA1, workA2, workA3))
    // ...when all A tasks are finished, run the single B task:
    .then(workB)
    // ...then run the C tasks (in parallel):
    .then(Arrays.asList(workC1, workC2))
    .enqueue();

Bạn có thể tạo nhiều trình tự phức tạp hơn bằng cách kết hợp nhiều chuỗi với phương thức WorkContinuation.combine(List<OneTimeWorkRequest>). Ví dụ: Giả sử bạn muốn chạy trình tự như sau:

Figure 1. Bạn có thể sử dụng WorkContinuation để thiết lập các tác vụ theo chuỗi phức tạp.

Để thiết lập trình tự này, hãy tạo 2 chuỗi riêng biệt rồi kết hợp các chuỗi đó thành một chuỗi thứ ba:

Kotlin

val chain1 = WorkManager.getInstance(myContext)
    .beginWith(workA)
    .then(workB)
val chain2 = WorkManager.getInstance(myContext)
    .beginWith(workC)
    .then(workD)
val chain3 = WorkContinuation
    .combine(Arrays.asList(chain1, chain2))
    .then(workE)
chain3.enqueue()

Java

WorkContinuation chain1 = WorkManager.getInstance(myContext)
    .beginWith(workA)
    .then(workB);
WorkContinuation chain2 = WorkManager.getInstance(myContext)
    .beginWith(workC)
    .then(workD);
WorkContinuation chain3 = WorkContinuation
    .combine(Arrays.asList(chain1, chain2))
    .then(workE);
chain3.enqueue();

Trong trường hợp này, WorkManager chạy workA trước workB. Trình này cũng chạy workC trước workD. Sau khi cả workBworkD đều đã hoàn tất, WorkManager sẽ chạy workE.

Có một số biến thể của phương thức WorkContinuation cung cấp cách viết ngắn gọn cho các tình huống cụ thể. Để biết thông tin chi tiết, hãy xem mục tài liệu tham khảo WorkContinuation.

Trình tự công việc duy nhất

Bạn có thể tạo một trình tự công việc duy nhất, bằng cách bắt đầu trình tự bằng một lệnh gọi đến beginUniqueWork(String, ExistingWorkPolicy, OneTimeWorkRequest) thay vì beginWith(OneTimeWorkRequest). Mỗi trình tự công việc duy nhất đều có tên; WorkManager chỉ cho phép một trình tự công việc có tên đó tại một thời điểm. Khi tạo một trình tự công việc duy nhất mới, bạn sẽ chỉ định những việc WorkManager phải làm nếu đã có một trình tự chưa hoàn tất có cùng tên:

  • Huỷ trình tự hiện có và THAY THẾ trình tự đó bằng trình tự mới
  • GIỮ trình tự hiện có và bỏ qua yêu cầu mới của bạn
  • THÊM trình tự mới vào trình tự hiện tại, chạy tác vụ đầu tiên của trình tự mới sau khi tác vụ cuối cùng của trình tự hiện tại kết thúc

Trình tự công việc duy nhất có thể hữu ích nếu bạn có một tác vụ không nên đưa vào hàng đợi nhiều lần. Ví dụ: Nếu ứng dụng của bạn cần đồng bộ hoá dữ liệu với mạng, thì bạn có thể thêm một trình tự có tên là "đồng bộ hoá" và chỉ định hệ thống bỏ qua tác vụ mới của bạn nếu đã có một trình tự có tên đó. Trình tự công việc duy nhất cũng có thể hữu ích nếu bạn cần thiết lập dần một chuỗi dài của các tác vụ. 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 dài các thao tác. Mỗi thao tác huỷ này có thể mất một chút thời gian, nhưng hệ thống phải thực hiện chúng 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 chuỗi nếu cần.

Tham số đầu vào và giá trị trả về

Để linh hoạt hơn nữa, bạn có thể truyền đối số tới các tác vụ của mình và các tác vụ sẽ trả kết quả về. Giá trị đã truyền và giá trị trả về là cặp khoá-giá trị. Để chuyển một đối số đến một tác vụ, hãy gọi phương thức WorkRequest.Builder.setInputData(Data) trước khi tạo đối tượng WorkRequest. Phương thức đó sẽ lấy đối tượng Data mà bạn tạo bằng Data.Builder. Lớp Worker có thể truy cập vào các đối số đó bằng cách gọi Worker.getInputData(). Để xuất ra một giá trị trả về, tác vụ phải bao gồm giá trị đó trong Result (ví dụ: trả về Result.success(Data). Bạn có thể nhận đầu ra bằng cách quan sát WorkInfo của tác vụ.

Ví dụ: Giả sử bạn có một lớp Worker thực hiện một phép tính cần nhiều thời gian. Đoạn mã sau đây cho thấy lớp Worker sẽ trông như thế nào:

Kotlin

// Define the parameter keys:
const val KEY_X_ARG = "X"
const val KEY_Y_ARG = "Y"
const val KEY_Z_ARG = "Z"

// ...and the result key:
const val KEY_RESULT = "result"

// Define the Worker class:
class MathWorker(context : Context, params : WorkerParameters)
    : Worker(context, params)  {

    override fun doWork(): Result {
        val x = inputData.getInt(KEY_X_ARG, 0)
        val y = inputData.getInt(KEY_Y_ARG, 0)
        val z = inputData.getInt(KEY_Z_ARG, 0)

        // ...do the math...
        val result = myLongCalculation(x, y, z);

        //...set the output, and we're done!
        val output: Data = workDataOf(KEY_RESULT to result)

        return Result.success(output)
    }
}

Java

// Define the Worker class:
public class MathWorker extends Worker {

    // Define the parameter keys:
    public static final String KEY_X_ARG = "X";
    public static final String KEY_Y_ARG = "Y";
    public static final String KEY_Z_ARG = "Z";
    // ...and the result key:
    public static final String KEY_RESULT = "result";

    public MathWorker(
        @NonNull Context context,
        @NonNull WorkerParameters params) {
        super(context, params);
    }

    @Override
    public Result doWork() {
        // Fetch the arguments (and specify default values):
        int x = getInputData().getInt(KEY_X_ARG, 0);
        int y = getInputData().getInt(KEY_Y_ARG, 0);
        int z = getInputData().getInt(KEY_Z_ARG, 0);

        // ...do the math...
        int result = myLongCalculation(x, y, z);

        //...set the output, and we're done!
        Data output = new Data.Builder()
            .putInt(KEY_RESULT, result)
            .build();
        return Result.success(output);
    }
}

Để tạo công việc và truyền đối số, bạn nên sử dụng mã như sau:

Kotlin

val myData: Data = workDataOf("KEY_X_ARG" to 42,
                       "KEY_Y_ARG" to 421,
                       "KEY_Z_ARG" to 8675309)

// ...then create and enqueue a OneTimeWorkRequest that uses those arguments
val mathWork = OneTimeWorkRequestBuilder<MathWorker>()
        .setInputData(myData)
        .build()
WorkManager.getInstance(myContext).enqueue(mathWork)

Java

// Create the Data object:
Data myData = new Data.Builder()
    // We need to pass three integers: X, Y, and Z
    .putInt(KEY_X_ARG, 42)
    .putInt(KEY_Y_ARG, 421)
    .putInt(KEY_Z_ARG, 8675309)
    // ... and build the actual Data object:
    .build();

// ...then create and enqueue a OneTimeWorkRequest that uses those arguments
OneTimeWorkRequest mathWork = new OneTimeWorkRequest.Builder(MathWorker.class)
        .setInputData(myData)
        .build();
WorkManager.getInstance(myContext).enqueue(mathWork);

Giá trị trả về sẽ có trong WorkInfo của tác vụ:

Kotlin

WorkManager.getInstance(myContext).getWorkInfoByIdLiveData(mathWork.id)
        .observe(this, Observer { info ->
            if (info != null && info.state.isFinished) {
                val myResult = info.outputData.getInt(KEY_RESULT,
                      myDefaultValue)
                // ... do something with the result ...
            }
        })

Java

WorkManager.getInstance(myContext).getWorkInfoByIdLiveData(mathWork.getId())
    .observe(lifecycleOwner, info -> {
         if (info != null && info.getState().isFinished()) {
           int myResult = info.getOutputData().getInt(KEY_RESULT,
                  myDefaultValue));
           // ... do something with the result ...
         }
    });

Nếu bạn liên kết các tác vụ, thì đầu ra của một tác vụ sẽ có sẵn dưới dạng đầu vào của tác vụ tiếp theo trong chuỗi đó. Nếu đó là một chuỗi đơn giản, với một OneTimeWorkRequest sau đó là một OneTimeWorkRequest duy nhất khác, thì tác vụ đầu tiên trả về kết quả bằng cách gọi Result.success(Data) và tác vụ tiếp theo sẽ tìm nạp kết quả đó bằng cách gọi getInputData(). Nếu chuỗi phức tạp hơn, ví dụ: Vì nhiều tác vụ đều gửi đầu ra đến duy nhất một tác vụ tiếp theo – bạn có thể khai báo InputMerger trên OneTimeWorkRequest.Builder để chỉ định điều sẽ xảy ra nếu các tác vụ khác nhau trả về đầu ra bằng cùng một khoá.

Tài nguyên khác

Để tìm hiểu thêm về WorkManager, hãy tham khảo các tài nguyên khác sau đây.

Mẫu