使用WorkManager定时更新小部件(APP Widget)

前面在"Android小部件APP Widget开发"中详细介绍了APP Widget的基本使用,并且知道了设置AppWidgetProviderInfoupdatePeriodMillis可以指定小部件的定时刷新时间,然后在BroadcastReceiver中更新我们的小部件。

这里可能就会遇到两个小问题。

  1. BroadcastReceiveronReceive()需要在10秒内执行完毕,不然会有ANR异常的提示。
  2. updatePeriodMillis的最小间隔为30分钟,无法设置更小的间隔。

为了解决这两个问题我们可以使用WorkManager进行小部件的更新, WorkManager可以灵活可靠的调度和执行异步任务,而且可以设置更小的时间间隔。可以降低设备的工作负荷和耗电。这种后台持续性任务调度的方式是官方所提倡的。

具体的使用可以参考这篇文章"WorkManager介绍以及用法"

接下来我会介绍基于WorkManager来更新小部件的方式。

创建更新小部件的Worker

这里我用了CoroutineWorker,通过挂起函数getTimeStr()获取数据,然后更新UI,具体逻辑可以看注释。

kotlin 复制代码
class WidgetUpdaterWorker(private val appContext: Context, params: WorkerParameters) : CoroutineWorker(appContext, params) {

    override suspend fun doWork(): Result {
        val provider = ComponentName(appContext, HelloWidgetProvider::class.java)
        val remoteViews = RemoteViews(appContext.packageName, R.layout.hello_widget_layout)

        return try {
            //获取UI数据
            val timeStr = getTimeStr()
            //设置数据
            remoteViews.setTextViewText(R.id.text, timeStr)
            //更新小部件
            AppWidgetManager.getInstance(appContext)
                .updateAppWidget(provider, remoteViews)
            //成功
            Result.success()
        } catch (e: IOException) {
            //网络等异常进行重试
            Result.retry()
        }
    }

    private suspend fun getTimeStr(): String {
        //假装是网络延迟
        delay(500)
        // 获取当前时间
        val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
        return sdf.format(Date())
    }

}

使用WorkManager进行更新

修改之前的onUpdate逻辑,为了简单演示这里直接enqueue就行。

kotlin 复制代码
override fun onUpdate(
    context: Context,
    appWidgetManager: AppWidgetManager,
    appWidgetIds: IntArray
) {
    WorkManager.getInstance(context.applicationContext)
        .enqueue(
            OneTimeWorkRequestBuilder<WidgetUpdaterWorker>()
                .build()
        )
}

运行程序看看

上面能看到,小部件一直在initialLayout和更新后的布局之间切换。

使用WorkManager更新小部件的问题

因为能看到时间一直是在变的,所以这里猜测小部件一直在更新?

onUpdate加个日志看看

kotlin 复制代码
override fun onUpdate(
    context: Context,
    appWidgetManager: AppWidgetManager,
    appWidgetIds: IntArray
) {
    Log.d(TAG,"onUpdate")
   ·····
}

能看到,的确频繁的被调用。

经过一段Google之后终于明白了原因。

参考:issuetracker.google.com/issues/2410...

大概原因如下,画了个图。

解决方法

Application创建的时候enqueue一个InitialDelay为10年的Worker

这样做主要是阻断死循环中的 "WorkManager没有待执行的任务"这一步。

kotlin 复制代码
WorkManager.getInstance(applicationContext)
    .enqueueUniqueWork(
        "not_executed_work",
        ExistingWorkPolicy.KEEP,
        OneTimeWorkRequestBuilder<WidgetUpdaterWorker>()
            .setInitialDelay(365 * 10, TimeUnit.DAYS)
            .build()
    )

不一定是用WidgetUpdaterWorker,随便一个Worker都行。

这里使用的是enqueueUniqueWork,目的是为了防止重复添加。

折中的解决方法

这个并不是完美的方案, 但是感觉可以接受的。

解决方案也就是下面会介绍的,在小部件enable的时候添加定时更新的WorkRequest

因为添加了periodicUpdateRequest,所以不会存在"WorkManager没有待执行的任务"的情况。

不过这个方式小部件在第一次添加的时候还是会刷新多一次,因为只要一次添加WorkRequest就会触发onUpdate,貌似目前来看无解。

使用WorkManager定时更新

如果希望小部件在APP被杀死后还能进行更新,可以使用WorkManager.enqueueUniquePeriodicWork()添加一个周期任务。

kotlin 复制代码
//第一个小部件添加的时候
override fun onEnabled(context: Context) {
    //创建WorkRequest,15分钟一个周期
    val periodicUpdateRequest =
        PeriodicWorkRequestBuilder<WidgetUpdaterWorker>(
            15, TimeUnit.MINUTES,
            5, TimeUnit.MINUTES
        ).build()
    //添加UniqueWork,不用怕会重复添加,这里的TAG也可以用来取消任务
    WorkManager.getInstance(context)
        .enqueueUniquePeriodicWork(
            TAG,
            ExistingPeriodicWorkPolicy.KEEP,
            periodicUpdateRequest
        )
}

//最后一个小部件被删除的时候
override fun onDisabled(context: Context) {
    //根据TAG取消掉任务
    WorkManager.getInstance(context).cancelUniqueWork(TAG)
}

上面代码展示了在onEnabled的时候添加一个十五分钟的周期任务,在onDisabled把这个周期性任务取消。

运行程序看看

添加小部件

十五分钟后

这里能看到在17分钟30秒后重新更新了一遍,并没有准时的在15分,原因是系统会考虑到电池优化的原因,运行结果是符合预期的。

好了到这里就介绍完毕了,如果想了解APP WidgetWorkManager可以参考我之前的文章。

相关推荐
Joseph Cooper1 小时前
Linux/Android 跟踪技术:ftrace、TRACE_EVENT、atrace、systrace 与 perfetto 入门
android·linux·运维
空中海2 小时前
安卓逆向03. 动态调试、抓包分析与 Frida Hook
android
一起搞IT吧3 小时前
相机Camera日志实例分析之二十:相机Camx【照片后置4800/5000/6400万拍照】单帧流程日志详解
android·嵌入式硬件·数码相机·智能手机
jinanwuhuaguo4 小时前
(第三十三篇)五月的文明奠基:OpenClaw 2026.5.2版本的文明级解读
android·java·开发语言·人工智能·github·拓扑学·openclaw
千码君20166 小时前
Trae:一些关于flutter和 go前后端开发构建的分享
android·flutter·gradle·android-studio·trae·vibe code
重生之我是Java开发战士9 小时前
【MySQL】事务 & 用户与权限管理
android·数据库·mysql
怣疯knight11 小时前
Windows不安装 Android Studio如何打包安卓软件
android·windows·android studio
ke_csdn11 小时前
从Java演变到Kotlin下的jet pack
android
wenzhangli712 小时前
在低代码设计中践行 Harness Engineering
android·低代码·rxjava
xingpanvip12 小时前
星盘接口开发文档:组合三限盘接口指南
android·开发语言·前端·python·php·lua