从 AppWidgetProvider 启动 WorkManager 任务会导致无休止的 onUpdate 调用

问题描述 投票:0回答:1

我正在尝试创建一个小部件,如本指南

中所述

...如果您的小部件设置过程可能需要几秒钟(可能在执行 Web 请求时)并且您需要继续您的过程,请考虑在 onUpdate() 方法中使用 WorkManager 启动任务。

但是,通过在

onUpdate
方法中对工作请求进行排队,我大约每半秒就会收到无尽的
onUpdate
调用,并传入相同的意图。我尝试使用
goAsync()
GlobalScope.launch
来更新小部件网络请求,它工作得很好。不过,建议使用 workmanager api 来完成 i/o 工作,这就是我正在尝试做的。

我通过 New -> Widget -> App Widget 菜单模板创建了一个应用程序小部件,并尝试像这样对工作进行排队:

class NewAppWidget : AppWidgetProvider() {
    override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager,appWidgetIds: IntArray) {
        val inputData = Data.Builder().putIntArray("ids", appWidgetIds).build()
        val workRequest = OneTimeWorkRequestBuilder<WidgetUpdateWorker>().setInputData(inputData).build()
        WorkManager.getInstance(context.applicationContext).enqueue(workRequest)
    }
}

工人:

class WidgetUpdateWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, params) {
    override suspend fun doWork(): Result {
        val appWidgetIds = inputData.getIntArray("ids")
        val appWidgetManager = AppWidgetManager.getInstance(applicationContext)
        delay(200L) // Simulate network call. The behavior is the same with Retrofit and a real network call
        appWidgetIds?.forEach { id ->
            val views = RemoteViews(applicationContext.packageName, R.layout.new_app_widget)
            views.setTextViewText(R.id.appwidget_text, "Setting text from worker")
            appWidgetManager.updateAppWidget(id, views)
        }
        return Result.success()
    }
}

应用程序小部件信息 XML:

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/app_widget_description"
    android:initialKeyguardLayout="@layout/new_app_widget"
    android:initialLayout="@layout/new_app_widget"
    android:minWidth="180dp"
    android:minHeight="110dp"
    android:previewImage="@drawable/example_appwidget_preview"
    android:resizeMode="horizontal|vertical"
    android:updatePeriodMillis="86400000"
    android:widgetCategory="home_screen" />

因此,我在主屏幕上看到一个不断闪烁和更新的小部件。这种情况也会发生在模拟器和真实设备上。

我做错了什么?我的目标是 API 30 并使用 WorkManager 版本

androidx.work:work-runtime-ktx:2.6.0

android broadcastreceiver android-widget android-workmanager
1个回答
14
投票

我找到了有关此问题的一些信息,我想我现在会回答我自己的问题

为什么会出现这种情况

根据问题跟踪器上的回复,这是预期的行为。当没有待处理的工作时,WorkManager 会禁用其内部

RescheduleReceiver
,并且组件的状态更改会触发
onUpdate
onUpdate
然后将另一个工作请求排入队列,最终陷入无限循环。我还建议阅读 CommonsWare 的帖子,其中详细介绍了这一点。

库开发人员推荐的解决方案是不要“无条件地将工作排入

onUpdate
”。这可能很难实现,而且由于我没有时间尝试这个建议,我将在下面提供一个对我有用的解决方法。

解决方法

到目前为止,我发现的最简单的解决方法是在 AppWidgetProvider 的

onEnabled
中设置一个远在未来的虚拟工作请求,以便始终有一个待处理的工作请求。例如:

override fun onEnabled(context: Context) {
    val alwaysPendingWork = OneTimeWorkRequestBuilder<YourWorker>()
        .setInitialDelay(5000L, TimeUnit.DAYS)
        .build()

    WorkManager.getInstance(context).enqueueUniqueWork(
        "always_pending_work",
        ExistingWorkPolicy.KEEP,
        alwaysPendingWork
    )
}

override fun onDisabled(context: Context) {
    WorkManager.getInstance(context).cancelUniqueWork("always_pending_work")
}

不要忘记每个AppWidgetProvider必须提供唯一的工作名称。

"${YourWorker::class.simpleName}_work"
对我来说效果很好。

也应该可以(并且可能是更好的方法)使用 WorkManager 的定期工作而不是依赖于小部件的更新间隔,但从我所读到的内容来看,它需要额外的、更复杂的状态管理。您还可以考虑在这里使用 JobIntentService 或 JobScheduler 而不是 WorkManager (但我只会将它们用作最后的手段)

© www.soinside.com 2019 - 2024. All rights reserved.