You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

设置NetworkType.UNMETERED的WorkManager在doWork中无可用网络

问题分析与解决方案

核心原因

WorkManager的网络约束仅作为任务触发的前置条件,并非在任务执行全程保证网络可用。当设备锁屏进入低功耗状态(如Doze模式),系统会主动限制后台网络访问、暂停DNS解析,导致WorkManager按约束启动Worker后,实际网络连接已失效,从而出现UnknownHostException。

具体解决方案

1. 强化WorkManager约束,适配后台低功耗环境

调整约束条件,增加对设备状态的限制,让WorkManager仅在更稳定的环境下触发任务:

private fun scheduleNextSyncWorker(context: Context) {
    val nextScheduleDelay = 60000L

    val constraints = Constraints.Builder()
        .setRequiredNetworkType(NetworkType.UNMETERED)
        .setRequiresCharging(true) // 仅充电时执行,避开低功耗限制
        .setRequiresBatteryNotLow(true) // 低电量时不触发
        .setRequiresDeviceIdle(true) // 设备空闲时执行(适合夜间备份场景)
        .build()

    val test = OneTimeWorkRequestBuilder<TestWorker>()
        .setInitialDelay(nextScheduleDelay, TimeUnit.MILLISECONDS)
        .setConstraints(constraints)
        .addTag("test")
        .setBackoffCriteria( // 配置自动重试策略
            BackoffPolicy.LINEAR,
            OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
            TimeUnit.MILLISECONDS
        )
        .build()
    WorkManager.getInstance(context)
        .enqueueUniqueWork("test", ExistingWorkPolicy.REPLACE, test)
}

2. 替换过时的网络检查逻辑,验证真实联网状态

废弃的activeNetworkInfo无法准确判断是否有实际互联网访问,改用NetworkCapabilities验证真实网络连通性:

private suspend fun hasValidInternet(context: Context): Boolean {
    val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    val activeNetwork = connectivityManager.activeNetwork ?: return false
    val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork) ?: return false
    
    return networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) &&
            networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
}

在Worker中使用该方法做检查,若网络无效则返回Result.retry(),让WorkManager自动重试:

class TestWorker(appContext: Context, workerParameters: WorkerParameters) : CoroutineWorker(appContext, workerParameters) {
    override suspend fun doWork(): Result {
        return try {
            withContext(Dispatchers.IO) {
                // 等待有效网络,超时则触发重试
                var waitedMillis = 0L
                val maxWait = 10000L
                while (!hasValidInternet(applicationContext) && waitedMillis < maxWait) {
                    Log.d("TEST", "等待有效网络连接...")
                    delay(1000)
                    waitedMillis += 1000
                }

                if (!hasValidInternet(applicationContext)) {
                    Log.d("TEST", "网络等待超时,触发重试")
                    return@withContext Result.retry()
                }

                // 执行API调用与备份逻辑
                // ...
            }
            Result.success()
        } catch (t: Throwable) {
            Log.e("TEST", "备份任务失败", t)
            // 根据错误类型决定重试策略:网络类错误优先重试
            if (t is UnknownHostException || t is IOException) Result.retry() else Result.failure()
        } finally {
            // 限制重试次数,避免无限循环,仅在未达上限时调度下一次任务
            if (runAttemptCount < 3) {
                scheduleNextSyncWorker(applicationContext)
            }
        }
    }
}

3. 规避Doze模式与App Standby限制

  • 若夜间备份必须在非充电状态执行,可申请REQUEST_IGNORE_BATTERY_OPTIMIZATIONS权限,但需注意:该权限需要用户手动授权,且Google Play对高频使用该权限的应用有审核限制。
  • 优先选择充电+空闲的执行条件,这是系统允许后台任务正常运行的最优场景,无需额外权限。

4. 放弃手动全局调度,遵循WorkManager设计规范

避免在finally中使用GlobalScope手动调度任务,改用WorkManager的内置重试机制或任务链,确保任务调度的可靠性与系统兼容性。


内容的提问来源于stack exchange,提问作者Kevin van Mierlo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 03:27:03