WorkManager 內建支援長時間執行的工作站。在這種情況下,WorkManager 可以提供作業系統的信號,告知作業系統在執行此作業時,必須盡可能讓程序保持運作狀態。這些工作站執行時間超過 10 分鐘。此新功能的範例用途包括大量上傳或下載 (不可分批)、在本機端處理機器學習模型,或是應用程式中對使用者很重要的工作。
以技術實際上來說,WorkManager 會代替您執行前景服務,以執行 WorkRequest,並顯示可設定的通知。
ListenableWorker 現在支援 setForegroundAsync() API,並 CoroutineWorker 支援停權的 setForeground() API。開發人員可藉由這些 API 指定 WorkRequest 是「重要」 (從使用者的角度來看) 或「長時間執行」。
從 2.3.0-alpha03 開始,WorkManager 也可讓您建立 PendingIntent,其可用於取消工作站,而無須使用 createCancelPendingIntent() API 註冊新的 Android 元件。此方法在與 setForegroundAsync() 或 setForeground() API 搭配使用時特別實用,可用於新增通知動作來取消 Worker。
建立及管理長時間執行的工作站
視您是否需要使用 Kotlin 或 Java 編寫程式碼而定,做法會略有不同。
Kotlin
Kotlin 開發人員應使用 CoroutineWorker。除了使用 setForegroundAsync(),您也可以使用這個方法的暫停版本 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
使用 ListenableWorker 或 Worker 的開發人員可以呼叫 setForegroundAsync() API,此 API 會回傳 ListenableFuture<Void>。您也可以呼叫 setForegroundAsync() 來更新進行中的 Notification。
下列為長時間執行下載檔案的工作站範例。此工作站會追蹤進度,以更新正在進行的 Notification,並顯示下載進度。
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
   }
}
將前景服務類型新增至長時間執行的工作站
如果您的應用程式指定 Android 14 (API 級別 34) 以上版本,則必須為所有長時間執行的工作站指定前景服務類型。如果您的應用程式指定 Android 10 (API 級別 29) 以上版本,並含有需要存取位置資訊的長時間執行工作站,表示工作站使用了 location 的前景服務類型。
如果您的應用程式指定 Android 11 (API 級別 30) 以上版本,而且包含需要存取相機或麥克風的長時間執行工作站,請各自宣告 camera 或 microphone 前景服務類型。
如要新增這些前景服務類型,請完成下列各節說明的步驟。
在應用程式資訊清單中宣告前景服務類型
在應用程式的資訊清單中宣告工作站的前景服務類型。在下列範例中,工作站需要存取位置資訊和麥克風:
AndroidManifest.xml
<service android:name="androidx.work.impl.foreground.SystemForegroundService" android:foregroundServiceType="location|microphone" tools:node="merge" />
指定執行階段的前景服務類型
呼叫 setForeground() 或 setForegroundAsync() 時,請務必指定前景服務類型。
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); }
