Obsługa długotrwałych instancji roboczych

WorkManager ma wbudowaną obsługę długotrwałych instancji roboczych. W takich przypadkach WorkManager może przekazać do systemu operacyjnego sygnał, że proces powinien pozostać aktywny jeśli jest to możliwe podczas ich wykonywania. Te instancje robocze mogą działać dłużej niż 10 min. Przykładowe zastosowania tej nowej funkcji obejmują przesyłanie zbiorcze lub pobrane pliki (które nie mogą zostać podzielone na fragmenty), lokalne crunching w modelu ML lub zadanie które są ważne dla użytkownika aplikacji.

WorkManager zarządza usługą na pierwszym planie i uruchamia ją w Twoim imieniu do wykonania polecenia WorkRequest oraz wyświetlam powiadomienia.

ListenableWorker obsługuje teraz interfejs API setForegroundAsync() oraz CoroutineWorker obsługuje interfejs API zawieszania setForeground(). Te Interfejsy API pozwalają programistom określić, że WorkRequest jest ważny (z z perspektywy użytkownika) lub długotrwałe.

Od 2.3.0-alpha03 WorkManager umożliwia też tworzenie PendingIntent – za pomocą tej funkcji można anulować instancje robocze bez konieczności zarejestruj nowy komponent Androida za pomocą createCancelPendingIntent() API. Ta metoda jest szczególnie przydatna, gdy interfejsów API setForegroundAsync() lub setForeground(), których można użyć do dodania powiadomienie, aby anulować Worker.

Tworzenie długotrwałych instancji roboczych i zarządzanie nimi

Użyjesz nieco innego podejścia w zależności od tego, czy kodujesz Kotlin lub Java.

Kotlin

Programiści z Kotlin powinni używać narzędzia CoroutineWorker. Zamiast użycia setForegroundAsync(), możesz użyć zawieszającej wersji tej metody, 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

Deweloperzy używający interfejsu ListenableWorker lub Worker mogą wywoływać metodę Interfejs API setForegroundAsync(), który zwraca ListenableFuture<Void>. Ty może również wywołać funkcję setForegroundAsync(), aby zaktualizować trwające Notification.

Oto prosty przykład długotrwałej instancji roboczej, która pobiera plik. Ten Instancja robocza śledzi postęp w aktualizacji trwającego Notification, które pokazuje postęp pobierania.

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
   }
}

Dodawanie typu usługi na pierwszym planie do długotrwałej instancji roboczej

Jeśli Twoja aplikacja jest kierowana na Androida 14 (poziom interfejsu API 34) lub nowszego, musisz określić typ usługi na pierwszym planie dla wszystkich długotrwałych instancji roboczych. Jeśli aplikacja jest kierowana na Androida 10 (poziom interfejsu API 29) lub nowszego i zawiera długotrwała instancja robocza, która wymaga dostępu do lokalizacji, wskaż, że instancja robocza korzysta z typu usługi na pierwszym planie: location.

Jeśli Twoja aplikacja jest kierowana na Androida 11 (poziom interfejsu API 30) lub nowszego i zawiera długoterminową instancję roboczą, która wymaga dostępu do kamery lub mikrofonu. zadeklaruj pierwszy plan działania camera lub microphone typy usług.

Aby dodać te typy usług na pierwszym planie, wykonaj czynności opisane w poniższych sekcji.

Deklarowanie typów usług na pierwszym planie w manifeście aplikacji

Zadeklaruj typ usługi na pierwszym planie instancji roboczej w pliku manifestu aplikacji. W W tym przykładzie instancja robocza wymaga dostępu do lokalizacji i mikrofonu:

AndroidManifest.xml

<service
   android:name="androidx.work.impl.foreground.SystemForegroundService"
   android:foregroundServiceType="location|microphone"
   tools:node="merge" />

Określ typy usług na pierwszym planie w czasie działania

Gdy dzwonisz pod numer setForeground() lub setForegroundAsync(), podaj typu usługi na pierwszym planie.

Moja_lokalizacja i pracownik mikrofonu

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);
}