WorkManager tích hợp sẵn tính năng hỗ trợ cho worker chạy trong thời gian dài. Trong những trường hợp như vậy, WorkManager có thể ra tín hiệu cho hệ điều hành rằng cần duy trì quy trình này ở trạng thái hoạt động (nếu có thể) trong khi thực thi công việc này. Những Worker này có thể chạy lâu hơn 10 phút. Các trường hợp sử dụng mẫu cho tính năng mới này bao gồm tải lên hoặc tải xuống hàng loạt (không thể chia nhỏ), phân tích mô hình học máy cục bộ hoặc công việc quan trọng với người dùng ứng dụng.
WorkManager nâng cao sẽ thay bạn quản lý và chạy dịch vụ trên nền trước để thực thi WorkRequest
, đồng thời hiển thị một thông báo có thể định cấu hình.
ListenableWorker
hiện đã hỗ trợ API setForegroundAsync()
và CoroutineWorker
hỗ trợ việc tạm ngưng API setForeground()
. Các API này cho phép nhà phát triển chỉ định rằng WorkRequest
này là quan trọng (ở góc độ người dùng) hay hoạt động lâu dài.
Bắt đầu bằng2.3.0-alpha03
, WorkManager còn cho phép bạn tạo một PendingIntent
(có thể dùng để huỷ worker mà không phải đăng ký thành phần Android mới bằng API createCancelPendingIntent()
). Phương thức này đặc biệt hữu ích khi dùng với các API setForegroundAsync()
hoặc setForeground()
(dùng để thêm một hành động thông báo để huỷ Worker
).
Tạo và quản lý trình thực thi chạy trong thời gian dài
Bạn sẽ dùng một phương pháp không quá khác biệt, tuỳ thuộc vào việc bạn đang lập trình trong Kotlin hay Java.
Kotlin
Nhà phát triển Kotlin nên sử dụng CoroutineWorker
. Thay vì sử dụng setForegroundAsync()
, bạn có thể sử dụng phiên bản tạm ngưng setForeground()
.
class DownloadWorker(context: Context, parameters: WorkerParameters) :
CoroutineWorker(context, parameters) {
private val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as
NotificationManager
override suspend fun doWork(): Result {
val inputUrl = inputData.getString(KEY_INPUT_URL)
?: return Result.failure()
val outputFile = inputData.getString(KEY_OUTPUT_FILE_NAME)
?: return Result.failure()
// Mark the Worker as important
val progress = "Starting Download"
setForeground(createForegroundInfo(progress))
download(inputUrl, outputFile)
return Result.success()
}
private fun download(inputUrl: String, outputFile: String) {
// Downloads a file and updates bytes read
// Calls setForeground() periodically when it needs to update
// the ongoing Notification
}
// Creates an instance of ForegroundInfo which can be used to update the
// ongoing notification.
private fun createForegroundInfo(progress: String): ForegroundInfo {
val id = applicationContext.getString(R.string.notification_channel_id)
val title = applicationContext.getString(R.string.notification_title)
val cancel = applicationContext.getString(R.string.cancel_download)
// This PendingIntent can be used to cancel the worker
val intent = WorkManager.getInstance(applicationContext)
.createCancelPendingIntent(getId())
// Create a Notification channel if necessary
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createChannel()
}
val notification = NotificationCompat.Builder(applicationContext, id)
.setContentTitle(title)
.setTicker(title)
.setContentText(progress)
.setSmallIcon(R.drawable.ic_work_notification)
.setOngoing(true)
// Add the cancel action to the notification which can
// be used to cancel the worker
.addAction(android.R.drawable.ic_delete, cancel, intent)
.build()
return ForegroundInfo(notificationId, notification)
}
@RequiresApi(Build.VERSION_CODES.O)
private fun createChannel() {
// Create a Notification channel
}
companion object {
const val KEY_INPUT_URL = "KEY_INPUT_URL"
const val KEY_OUTPUT_FILE_NAME = "KEY_OUTPUT_FILE_NAME"
}
}
Java
Các nhà phát triển sử dụng ListenableWorker
hoặc Worker
có thể gọi API setForegroundAsync()
, API này sẽ trả về ListenableFuture<Void>
. Bạn cũng có thể gọi setForegroundAsync()
để cập nhật Notification
đang diễn ra.
Sau đây là ví dụ đơn giản về một worker chạy trong thời gian dài thực hiện tải tệp xuống. Worker này sẽ theo dõi tiến trình để cập nhật Notification
(để cho biết tiến trình tải xuống) đang diễn ra.
public class DownloadWorker extends Worker {
private static final String KEY_INPUT_URL = "KEY_INPUT_URL";
private static final String KEY_OUTPUT_FILE_NAME = "KEY_OUTPUT_FILE_NAME";
private NotificationManager notificationManager;
public DownloadWorker(
@NonNull Context context,
@NonNull WorkerParameters parameters) {
super(context, parameters);
notificationManager = (NotificationManager)
context.getSystemService(NOTIFICATION_SERVICE);
}
@NonNull
@Override
public Result doWork() {
Data inputData = getInputData();
String inputUrl = inputData.getString(KEY_INPUT_URL);
String outputFile = inputData.getString(KEY_OUTPUT_FILE_NAME);
// Mark the Worker as important
String progress = "Starting Download";
setForegroundAsync(createForegroundInfo(progress));
download(inputUrl, outputFile);
return Result.success();
}
private void download(String inputUrl, String outputFile) {
// Downloads a file and updates bytes read
// Calls setForegroundAsync(createForegroundInfo(myProgress))
// periodically when it needs to update the ongoing Notification.
}
@NonNull
private ForegroundInfo createForegroundInfo(@NonNull String progress) {
// Build a notification using bytesRead and contentLength
Context context = getApplicationContext();
String id = context.getString(R.string.notification_channel_id);
String title = context.getString(R.string.notification_title);
String cancel = context.getString(R.string.cancel_download);
// This PendingIntent can be used to cancel the worker
PendingIntent intent = WorkManager.getInstance(context)
.createCancelPendingIntent(getId());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createChannel();
}
Notification notification = new NotificationCompat.Builder(context, id)
.setContentTitle(title)
.setTicker(title)
.setSmallIcon(R.drawable.ic_work_notification)
.setOngoing(true)
// Add the cancel action to the notification which can
// be used to cancel the worker
.addAction(android.R.drawable.ic_delete, cancel, intent)
.build();
return new ForegroundInfo(notificationId, notification);
}
@RequiresApi(Build.VERSION_CODES.O)
private void createChannel() {
// Create a Notification channel
}
}
Thêm loại dịch vụ trên nền trước cho một worker chạy trong thời gian dài
Nếu ứng dụng của bạn nhắm đến Android 14 (API cấp 34) trở lên, bạn phải chỉ định một
loại dịch vụ trên nền trước dành cho tất cả worker chạy trong thời gian dài.
Nếu ứng dụng của bạn nhắm đến Android 10 (API cấp 29) trở lên và có chứa một
worker chạy trong thời gian dài cần có quyền truy cập vào vị trí, hãy cho biết worker đó
sử dụng loại dịch vụ trên nền trước là location
.
Nếu ứng dụng của bạn nhắm đến Android 11 (API cấp 30) trở lên
và chứa một worker chạy trong thời gian dài cần quyền truy cập vào máy ảnh hoặc micrô,
khai báo nền trước camera
hoặc microphone
các loại dịch vụ tương ứng.
Để thêm các loại dịch vụ trên nền trước này, hãy hoàn thành các bước được mô tả ở phần sau đây.
Khai báo các loại dịch vụ trên nền trước trong tệp kê khai ứng dụng
Khai báo loại dịch vụ trên nền trước của worker trong tệp kê khai của ứng dụng. Trong ví dụ sau, worker này yêu cầu quyền truy cập vào vị trí và micrô:
AndroidManifest.xml
<service android:name="androidx.work.impl.foreground.SystemForegroundService" android:foregroundServiceType="location|microphone" tools:node="merge" />
Chỉ định loại dịch vụ trên nền trước trong thời gian chạy
Khi gọi setForeground()
hoặc setForegroundAsync()
, hãy đảm bảo bạn chỉ định một
loại dịch vụ trên nền trước.
MyLocationAndMicrophoneWorker
Kotlin
private fun createForegroundInfo(progress: String): ForegroundInfo { // ... return ForegroundInfo(NOTIFICATION_ID, notification, FOREGROUND_SERVICE_TYPE_LOCATION or FOREGROUND_SERVICE_TYPE_MICROPHONE) }
Java
@NonNull private ForegroundInfo createForegroundInfo(@NonNull String progress) { // Build a notification... Notification notification = ...; return new ForegroundInfo(NOTIFICATION_ID, notification, FOREGROUND_SERVICE_TYPE_LOCATION | FOREGROUND_SERVICE_TYPE_MICROPHONE); }