使用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可以参考我之前的文章。

相关推荐
mmsx1 小时前
android sqlite 数据库简单封装示例(java)
android·java·数据库
众拾达人4 小时前
Android自动化测试实战 Java篇 主流工具 框架 脚本
android·java·开发语言
吃着火锅x唱着歌5 小时前
PHP7内核剖析 学习笔记 第四章 内存管理(1)
android·笔记·学习
_Shirley6 小时前
鸿蒙设置app更新跳转华为市场
android·华为·kotlin·harmonyos·鸿蒙
hedalei8 小时前
RK3576 Android14编译OTA包提示java.lang.UnsupportedClassVersionError问题
android·android14·rk3576
锋风Fengfeng8 小时前
安卓多渠道apk配置不同签名
android
枫_feng8 小时前
AOSP开发环境配置
android·安卓
叶羽西9 小时前
Android Studio打开一个外部的Android app程序
android·ide·android studio
qq_1715388510 小时前
利用Spring Cloud Gateway Predicate优化微服务路由策略
android·javascript·微服务
Vincent(朱志强)11 小时前
设计模式详解(十二):单例模式——Singleton
android·单例模式·设计模式