Android NDK开发详解后台任务之支持长时间运行的 worker

WorkManager 为长时间运行的 worker 提供内置支持。在这种情况下,WorkManager 可以向操作系统提供一个信号,指示在此项工作执行期间应尽可能让进程保持活跃状态。这些 worker 可以运行超过 10 分钟。这一新功能的示例用例包括批量上传或下载(不可分块)、在本地进行的机器学习模型处理,或者对应用的用户很重要的任务。

在后台,WorkManager 会代表您管理和运行前台服务以执行 WorkRequest,同时还会显示可配置的通知。

ListenableWorker 现在支持 setForegroundAsync() API,而 CoroutineWorker 则支持挂起 setForeground() API。这些 API 允许开发者指定此 WorkRequest 是“重要的”(从用户的角度来看)或“长时间运行的”任务。

从 2.3.0-alpha03 开始,WorkManager 还允许您创建 PendingIntent,此 Intent 可用于取消 worker,而不必使用 createCancelPendingIntent() API 注册新的 Android 组件。此方法与 setForegroundAsync() 或 setForeground() API 一起使用时特别有用,可用于添加一个取消 Worker 的通知操作。

创建和管理长时间运行的 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。您还可以调用 setForegroundAsync() 以更新持续运行的 Notification。

下面是一个简单的示例,说明了一个下载文件的长时间运行 worker。此 worker 会跟踪进度,以更新持续显示下载进度的 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
   }
}

将前台服务类型添加到长时间运行的 worker

注意 :根据应用的目标 API 级别以及服务正在执行的工作类型,您可能需要声明前台服务类型。无论您以哪个 Android 版本为目标平台,声明前台服务类型都是最佳做法。如需了解详情,请参阅前台服务文档。

如果您的应用以 Android 14(API 级别 34)或更高版本为目标平台,您必须为所有长时间运行的 worker 指定前台服务类型。如果您的应用以 Android 10(API 级别 29)或更高版本为目标平台,且包含需要位置信息访问权限的长时间运行的 worker,请指明该 worker 使用前台服务类型 location。

如果您的应用以 Android 11(API 级别 30)或更高版本为目标平台,且包含需要访问摄像头或麦克风的长时间运行的 worker,请分别声明 camera 或 microphone 前台服务类型。

如需添加这些前台服务类型,请完成以下各部分中介绍的步骤。

在应用清单中声明前台服务类型

在应用的清单中声明 worker 的前台服务类型。在以下示例中,worker 需要位置信息和麦克风访问权限:

AndroidManifest.xml

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

注意 :清单合并工具将上述代码段中的 元素声明与 WorkManager 的 SystemForegroundService 在其自己的清单中定义的声明合并在一起。

在运行时指定前台服务类型

调用 setForeground() 或 setForegroundAsync() 时,请务必指定前台服务类型。
注意 :从 Android 14(API 级别 34)开始,当您调用 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);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

五一编程

程序之路有我与你同行

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值