Tạo luồng trong ListenableWorker

Trong một số trường hợp, có thể bạn sẽ cần đưa ra một chiến lược tạo luồng xử lý tuỳ chỉnh. Ví dụ: bạn có thể cần xử lý một thao tác không đồng bộ dựa trên lệnh gọi lại. WorkManager hỗ trợ trường hợp sử dụng này bằng ListenableWorker. ListenableWorker là API worker cơ bản nhất; Worker, CoroutineWorkerRxWorker đều bắt nguồn từ lớp này. ListenableWorker chỉ báo hiệu thời điểm công việc nên bắt đầu cũng như dừng lại, đồng thời để bạn hoàn toàn tùy ý quyết định của mình. Tín hiệu bắt đầu công việc được gọi trên luồng chính, vì vậy, điều quan trọng là bạn phải chuyển đến luồng nền mà bạn chọn theo cách thủ công.

Phương thức trừu tượng ListenableWorker.startWork() sẽ trả về ListenableFuture của Result. ListenableFuture là một giao diện gọn nhẹ: là một Future cung cấp chức năng đính kèm trình nghe và truyền tải các trường hợp ngoại lệ. Trong phương thức startWork, bạn dự kiến sẽ trả về một ListenableFuture. Bạn sẽ đặt lớp này cùng với Result của thao tác sau khi hoàn tất. Bạn có thể tạo thực thể ListenableFuture theo một trong 2 cách sau:

  1. Nếu bạn dùng Guava, hãy sử dụng ListeningExecutorService.
  2. Nếu không, hãy đưa councurrent-futures vào tệp gradle và sử dụng CallbackToFutureAdapter.

Nếu muốn thực thi một số công việc dựa trên lệnh gọi lại không đồng bộ, bạn sẽ làm như sau:

Kotlin

class CallbackWorker(
        context: Context,
        params: WorkerParameters
) : ListenableWorker(context, params) {
    override fun startWork(): ListenableFuture<Result> {
        return CallbackToFutureAdapter.getFuture { completer ->
            val callback = object : Callback {
                var successes = 0

                override fun onFailure(call: Call, e: IOException) {
                    completer.setException(e)
                }

                override fun onResponse(call: Call, response: Response) {
                    successes++
                    if (successes == 100) {
                        completer.set(Result.success())
                    }
                }
            }

            repeat(100) {
                downloadAsynchronously("https://example.com", callback)
            }

            callback
        }
    }
}

Java

public class CallbackWorker extends ListenableWorker {

    public CallbackWorker(Context context, WorkerParameters params) {
        super(context, params);
    }

    @NonNull
    @Override
    public ListenableFuture<Result> startWork() {
        return CallbackToFutureAdapter.getFuture(completer -> {
            Callback callback = new Callback() {
                int successes = 0;

                @Override
                public void onFailure(Call call, IOException e) {
                    completer.setException(e);
                }

                @Override
                public void onResponse(Call call, Response response) {
                    successes++;
                    if (successes == 100) {
                        completer.set(Result.success());
                    }
                }
            };

            for (int i = 0; i < 100; i++) {
                downloadAsynchronously("https://www.example.com", callback);
            }
            return callback;
        });
    }
}

Điều gì sẽ xảy ra nếu công việc của bạn bị dừng? ListenableFuture của ListenableWorker luôn bị huỷ khi công việc dự kiến sẽ dừng. Khi sử dụng CallbackToFutureAdapter, bạn chỉ cần thêm trình nghe huỷ như sau:

Kotlin

class CallbackWorker(
        context: Context,
        params: WorkerParameters
) : ListenableWorker(context, params) {
    override fun startWork(): ListenableFuture<Result> {
        return CallbackToFutureAdapter.getFuture { completer ->
            val callback = object : Callback {
                var successes = 0

                override fun onFailure(call: Call, e: IOException) {
                    completer.setException(e)
                }

                override fun onResponse(call: Call, response: Response) {
                    ++successes
                    if (successes == 100) {
                        completer.set(Result.success())
                    }
                }
            }

 completer.addCancellationListener(cancelDownloadsRunnable, executor)

            repeat(100) {
                downloadAsynchronously("https://example.com", callback)
            }

            callback
        }
    }
}

Java

public class CallbackWorker extends ListenableWorker {

    public CallbackWorker(Context context, WorkerParameters params) {
        super(context, params);
    }

    @NonNull
    @Override
    public ListenableFuture<Result> startWork() {
        return CallbackToFutureAdapter.getFuture(completer -> {
            Callback callback = new Callback() {
                int successes = 0;

                @Override
                public void onFailure(Call call, IOException e) {
                    completer.setException(e);
                }

                @Override
                public void onResponse(Call call, Response response) {
                    ++successes;
                    if (successes == 100) {
                        completer.set(Result.success());
                    }
                }
            };

            completer.addCancellationListener(cancelDownloadsRunnable, executor);

            for (int i = 0; i < 100; ++i) {
                downloadAsynchronously("https://www.example.com", callback);
            }
            return callback;
        });
    }
}

Chạy ListenableWorker trong một quy trình khác

Bạn cũng có thể liên kết một worker với một quy trình cụ thể bằng cách sử dụng RemoteListenableWorker (một cách triển khai của ListenableWorker).

RemoteListenableWorker liên kết với một quy trình cụ thể bằng 2 đối số bổ sung ARGUMENT_CLASS_NAMEARGUMENT_PACKAGE_NAME mà bạn cung cấp dưới dạng một phần của dữ liệu đầu vào khi tạo yêu cầu công việc.

Ví dụ sau đây minh hoạ việc xây dựng một yêu cầu công việc ràng buộc với một quy trình cụ thể:

Kotlin

val PACKAGE_NAME = "com.example.background.multiprocess"

val serviceName = RemoteWorkerService::class.java.name
val componentName = ComponentName(PACKAGE_NAME, serviceName)

val data: Data = Data.Builder()
   .putString(ARGUMENT_PACKAGE_NAME, componentName.packageName)
   .putString(ARGUMENT_CLASS_NAME, componentName.className)
   .build()

return OneTimeWorkRequest.Builder(ExampleRemoteListenableWorker::class.java)
   .setInputData(data)
   .build()

Java

String PACKAGE_NAME = "com.example.background.multiprocess";

String serviceName = RemoteWorkerService.class.getName();
ComponentName componentName = new ComponentName(PACKAGE_NAME, serviceName);

Data data = new Data.Builder()
        .putString(ARGUMENT_PACKAGE_NAME, componentName.getPackageName())
        .putString(ARGUMENT_CLASS_NAME, componentName.getClassName())
        .build();

return new OneTimeWorkRequest.Builder(ExampleRemoteListenableWorker.class)
        .setInputData(data)
        .build();

Đối với mỗi RemoteWorkerService, bạn cũng cần thêm định nghĩa dịch vụ vào tệp AndroidManifest.xml:

<manifest ... >
    <service
            android:name="androidx.work.multiprocess.RemoteWorkerService"
            android:exported="false"
            android:process=":worker1" />

        <service
            android:name=".RemoteWorkerService2"
            android:exported="false"
            android:process=":worker2" />
    ...
</manifest>

Mẫu