【问题标题】:Long Running Worker issue of Workmanger: Getting Exception kotlinx.coroutines.JobCancellationException: Job was cancelled in CoroutineWorker in KotlinWorkmanger 的 Long Running Worker 问题:Getting Exception kotlinx.coroutines.JobCancellationException: Job was cancelled in CoroutineWorker in Kotlin
【发布时间】:2023-02-17 00:12:25
【问题描述】:

我创建了一个简单的CoroutineWorker,它运行循环 1000 次,延迟 1000 毫秒。

这个工作者是唯一的周期性工作者,重复间隔为 15 分钟,ExistingPeriodicWorkPolicy 为 KEEP

但是当我启动工作人员并且在执行期间一段时间后工作人员被取消并出现异常JobCancellationException

完整的例外:

Exception kotlinx.coroutines.JobCancellationException: Job was cancelled; job=JobImpl{Cancelling}@ba57765
12:55:47.088 WM-Wor...rapper  I  Work [ id=4c44c3da-3c57-4cac-a40a-82c948125807, tags={ com.sk.workmanagerdemo1.DownloadingWorker } ] was cancelled
                                 java.util.concurrent.CancellationException: Task was cancelled.
                                    at androidx.work.impl.utils.futures.AbstractFuture.cancellationExceptionWithCause(AbstractFuture.java:1184)
                                    at androidx.work.impl.utils.futures.AbstractFuture.getDoneValue(AbstractFuture.java:514)
                                    at androidx.work.impl.utils.futures.AbstractFuture.get(AbstractFuture.java:475)
                                    at androidx.work.impl.WorkerWrapper$2.run(WorkerWrapper.java:311)
                                    at androidx.work.impl.utils.SerialExecutor$Task.run(SerialExecutor.java:91)
                                    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
                                    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
                                    at java.lang.Thread.run(Thread.java:923)

工人代码:

import android.content.Context
import android.util.Log
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import java.text.SimpleDateFormat
import java.util.*

class DownloadingWorker(context: Context, params: WorkerParameters) :
    CoroutineWorker(context, params) {

    override suspend fun doWork(): Result {
        return withContext(Dispatchers.IO) {
            Log.i("MYTAG", "Started ${getCurrentDateTime()}")
            return@withContext try {
                for (i in 0..1000) {
                    delay(1000)
                    Log.i("MYTAG", "Downloading $i")
                }
                Log.i("MYTAG", "Completed ${getCurrentDateTime()}")
                Result.success()
            } catch (e: Exception) {
                Log.i("MYTAG", "Exception $e")
                Result.failure()
            }
        }
    }

    private fun getCurrentDateTime(): String {
        val time = SimpleDateFormat("dd/M/yyyy hh:mm:ss")
        return time.format(Date())
    }
}

和工人的开始

private fun setPeriodicWorkRequest() {
        val downloadConstraints = Constraints.Builder()
            .setRequiresCharging(true)
            .setRequiredNetworkType(NetworkType.CONNECTED)
            .build()
        val periodicWorkRequest = PeriodicWorkRequest
            .Builder(DownloadingWorker::class.java, 15, TimeUnit.MINUTES)
            .setConstraints(downloadConstraints)
            .build()
        WorkManager.getInstance(applicationContext).enqueueUniquePeriodicWork(
            "DownloadWorker",
            ExistingPeriodicWorkPolicy.KEEP,
            periodicWorkRequest
        )
    }

我在活动中单击按钮时调用上述函数。

我不确定为什么在 10 分钟后的一段时间后我会自动收到此异常。

提前致谢。请帮助我确定原因,并请让我知道我这边的任何意见。

【问题讨论】:

  • 尝试了您的代码,但无法重现问题

标签: android kotlin kotlin-coroutines android-workmanager


【解决方案1】:

您是否检查过是否满足互联网权限赋予的 downloadConstraints 变量中设置的限制条件 尝试在 PeriodicWorkRequest.Builder 中使用 setInitialDelay() 来检查是否满足约束

【讨论】:

    【解决方案2】:

    对我来说,我无法重现该异常;尽管假设满足所有工作限制;如果需要很长时间,系统有可能取消工作进程。

    对于这种情况,文档提供了support for long-running workers,它关联了一个前台服务,该服务向系统/用户指示后台有一个长时间运行的处理;因此系统可以保持进程并且用户知道什么正在消耗他们的资源并且也可以让他们停止那个工人。

    特别是当你提到它很长时:

    我不确定为什么在 10 分钟后的一段时间后我会自动收到此异常。

    文档说了一些我认为可能是原因的地方:

    WorkManager 内置了对长时间运行的 worker 的支持。在这样的 在这种情况下,WorkManager 可以向操作系统提供一个信号,表明该进程 在执行此工作时,应尽可能保持活动状态。这些 工人可以运行超过 10 分钟。

    要启用它,您需要调用startForeground() 来更新与前台服务关联的通知;通常随着工人的进步。

    An example 由显示如何在 worker 中使用它的文档提供;在这里我已经为你的DownloadingWorker定制了它:

    
    class DownloadingWorker(context: Context, params: WorkerParameters) :
        CoroutineWorker(context, params) {
    
        private val notificationId: Int = 1
    
        override suspend fun doWork(): Result {
            return withContext(Dispatchers.IO) {
                Log.i("MYTAG", "Started ${getCurrentDateTime()}")
                return@withContext try {
                    for (i in 0..1000) {
                        delay(1000)
                        val progress = "Starting Download"
                        setForeground(createForegroundInfo(progress))
                        Log.i("MYTAG", "Downloading $i")
                        setForeground(createForegroundInfo("Downloading $i"))
                    }
                    Log.i("MYTAG", "Completed ${getCurrentDateTime()}")
                    setForeground(createForegroundInfo("Completed"))
                    Result.success()
                } catch (e: Exception) {
                    Log.i("MYTAG", "Exception $e")
                    Result.failure()
                }
            }
        }
    
        // Creates an instance of ForegroundInfo which can be used to update the
        // ongoing notification.
        private fun createForegroundInfo(progress: String): ForegroundInfo {
            val title = "notification title)"
            val cancel = "cancel download"
            val channelId = "notification id"
            // 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(channelId)
            }
    
            val notification = NotificationCompat.Builder(applicationContext, channelId)
                .setContentTitle(title)
                .setTicker(title)
                .setContentText(progress)
                .setSmallIcon(R.drawable.ic_launcher_foreground)
                .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(channelId: String) {
            // Create a Notification channel
            val serviceChannel = NotificationChannel(
                channelId,
                "Download Channel",
                NotificationManager.IMPORTANCE_DEFAULT
            )
    
            val notificationManager =
                applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as
                        NotificationManager
            notificationManager.createNotificationChannel(serviceChannel)
    
        }
    
    
        private fun getCurrentDateTime(): String {
            val time = SimpleDateFormat("dd/M/yyyy hh:mm:ss")
            return time.format(Date())
        }
    
    }
    

    确保将前台服务添加到 <application> 内的清单中:

    <application
        ....
        <service
           android:name="androidx.work.impl.foreground.SystemForegroundService"
           android:foregroundServiceType="dataSync" />
    
    </application>
    

    您还需要在 API 33+ 和清单中以编程方式请求 Manifest.permission.POST_NOTIFICATIONS

    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
    

    【讨论】:

      猜你喜欢
      • 2023-02-25
      • 1970-01-01
      • 2022-12-26
      • 1970-01-01
      • 2019-06-23
      • 2022-12-01
      • 1970-01-01
      • 2022-12-02
      • 2022-12-27
      相关资源
      最近更新 更多