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

相关推荐
还鮟4 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡5 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi005 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体
zhangphil7 小时前
Android理解onTrimMemory中ComponentCallbacks2的内存警戒水位线值
android
你过来啊你7 小时前
Android View的绘制原理详解
android
移动开发者1号10 小时前
使用 Android App Bundle 极致压缩应用体积
android·kotlin
移动开发者1号10 小时前
构建高可用线上性能监控体系:从原理到实战
android·kotlin
ii_best15 小时前
按键精灵支持安卓14、15系统,兼容64位环境开发辅助工具
android
美狐美颜sdk15 小时前
跨平台直播美颜SDK集成实录:Android/iOS如何适配贴纸功能
android·人工智能·ios·架构·音视频·美颜sdk·第三方美颜sdk
恋猫de小郭20 小时前
Meta 宣布加入 Kotlin 基金会,将为 Kotlin 和 Android 生态提供全新支持
android·开发语言·ios·kotlin