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

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

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

  1. BroadcastReceiver的onReceive()需要在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 Widget和WorkManager可以参考我之前的文章。

相关推荐
墨天梦7 小时前
D03_Compose列表与稳定身份
android·gitee·kotlin
萌新杰少8 小时前
Kuikly股票查看软件开发体验——SaiRen
android·kotlin·客户端
vilya8 小时前
把 Python 塞进 APK:Chaquopy 打包实践
android·python
用户92817267390168 小时前
Android Compose版本的AI组件库来了。
android·kotlin
知昂七昂8 小时前
00-02:AOSP 源码阅读环境与检索方法论源码剖析(Android 16 / aosp-main)
android
知昂七昂8 小时前
00-01:AOSP 源码仓库结构与 repo 工作流源码剖析(Android 16 / aosp-main)
android·google
跨境数据猎手8 小时前
Android Native 层黑盒调用实战:unidbg 模拟执行 so 的原理
android
镜象科技10 小时前
中小学AI心理健康解决方案服务商怎么选?2026年选型参考
android·java·人工智能
脚踏实地,坚持不懈!10 小时前
从 AOSP 源码角度分析 Android 定位性能问题
android
悟道子HD10 小时前
网络安全基本功——MySQL基础
android·mysql·web安全