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

相关推荐
工程师老罗9 小时前
如何在Android工程中配置NDK版本
android
Libraeking12 小时前
破壁行动:在旧项目中丝滑嵌入 Compose(混合开发实战)
android·经验分享·android jetpack
市场部需要一个软件开发岗位13 小时前
JAVA开发常见安全问题:Cookie 中明文存储用户名、密码
android·java·安全
JMchen12315 小时前
Android后台服务与网络保活:WorkManager的实战应用
android·java·网络·kotlin·php·android-studio
crmscs15 小时前
剪映永久解锁版/电脑版永久会员VIP/安卓SVIP手机永久版下载
android·智能手机·电脑
localbob15 小时前
杀戮尖塔 v6 MOD整合版(Slay the Spire)安卓+PC端免安装中文版分享 卡牌肉鸽神作!杀戮尖塔中文版,电脑和手机都能玩!杀戮尖塔.exe 杀戮尖塔.apk
android·杀戮尖塔apk·杀戮尖塔exe·游戏分享
机建狂魔15 小时前
手机秒变电影机:Blackmagic Camera + LUT滤镜包的专业级视频解决方案
android·拍照·摄影·lut滤镜·拍摄·摄像·录像
hudawei99615 小时前
flutter和Android动画的对比
android·flutter·动画
lxysbly17 小时前
md模拟器安卓版带金手指2026
android
儿歌八万首18 小时前
硬核春节:用 Compose 打造“赛博鞭炮”
android·kotlin·compose·春节