在AppWidgetProvider中启动WorkManager任务导致无限的onUpdate调用

8

我正在尝试创建一个小部件,如这个指南中所述,

...如果你的小部件设置过程可能需要几秒钟(也许是在执行网络请求),并且你需要继续进行处理,请考虑在onUpdate()方法中使用WorkManager启动Task。

但是,通过在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

好的,我找到了一个相关问题,并且有人在那里提交了一些解决方法:https://issuetracker.google.com/issues/115575872 - miredirex
1个回答

12

我找到了关于这个问题的一些信息,我认为我现在可以自己回答我的问题。

为什么会发生这种情况

根据问题跟踪器上的回复,这是预期行为。当没有未决的工作时,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。


1
感谢这个解决方法!我简直不敢相信为了防止小部件进入无限循环而必须这样做。所有其他的解决方案都更加复杂。 - Csaba Szugyiczki
我遇到了相同的问题,它在某种程度上解决了问题,但在初始延迟超时后,相同的问题会发生,并且 onUpdate 开始无限循环。 - MRX
@MRX,你的初始延迟时间如何?它是不是太短了?这个巧妙的解决方法的整个重点是设置一个几乎无限的初始延迟。顺便说一句,他们正在努力修复这个问题并添加对小部件的支持。我可能会在以后更新答案。 - miredirex
2
@miredirex 是的,我需要每30分钟更新小部件...我在GitHub存储库中找到了更好的实现,尽管它使用了CorutineWorker,因为他们在后台有很多事情要做,但是这个部分解决方案对我来说很好....在这里检查存储库-https://github.com/SimpleAppProjects/SimpleWeather-Android - MRX

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接