【问题标题】:How To Properly Update A Widget In Android 8.0 - Oreo - API 26如何在 Android 8.0 中正确更新小部件 - Oreo - API 26
【发布时间】:2017-09-12 14:36:44
【问题描述】:

假设我有一个应用程序的小部件,它的 targetSDKVersion 设置为 26。这个小部件需要 100 毫秒到 10 秒才能更新。大部分时间都在 1s 以内。在 Android O 之前,如果在我的 AppWidgetProvider 上调用 onUpdate(),我可以启动一个后台服务来更新这个小部件。但是,如果您尝试该行为,Android O 会返回 IllegalStateException。启动前台服务的显而易见的解决方案似乎是一种极端措施,因为它可以在 99% 的时间内在 10 秒内完成。

可能的解决方案

  • 启动前台服务以更新小部件。用 10 秒后就会消失的通知来惹恼用户。
  • 使用 JobScheduler 尽快安排作业。您的小部件可能会在一段时间内更新,也可能不会更新。
  • 尝试在广播接收器中完成工作。锁定任何其他应用程序的 UI 线程。糟糕。
  • 尝试在小部件接收器中执行工作。锁定任何其他应用程序的 UI 线程。糟糕。
  • 滥用 GCM 使后台服务运行。工作量很大,感觉很老套。

我个人不喜欢上述任何解决方案。希望我错过了一些东西。

(更令人沮丧的是,我的应用程序已经被系统调用 onUpdate() 加载到内存中。我看不到如何将我的应用程序加载到内存中以调用 onUpdate(),但是没有给我的应用程序 1s 进行更新UI 线程外的小部件可以节省任何电池寿命。)

【问题讨论】:

  • 你有没有找到更简洁的方法?我也面临 IllegalStateException

标签: android multithreading widget background-process android-8.0-oreo


【解决方案1】:

您没有说明更新触发机制是什么。您似乎担心延迟(“您的小部件可能会或可能不会在一段时间内更新”),因此我假设您的担忧与用户与应用小部件的交互有关,例如点击按钮。

使用 JobScheduler 尽快安排作业。您的小部件可能会在一段时间内更新,也可能不会更新。

这是“使用JobIntentService”的变体,AFAIK 是此类场景的推荐解决方案。

其他选项包括:

  • getForegroundService()PendingIntent 一起使用。有了这个,你有效地“发誓”你的服务将在 ANR 时间范围内调用startForeground()。如果工作时间超过几秒钟,请致电startForeground() 以确保 Android 不会胡思乱想。这应该最大限度地减少前台通知出现的次数。而且,如果用户点击了一个按钮,而几秒钟后您仍在忙于工作,您可能想要显示通知或以其他方式让用户知道他们要求的内容仍然存在进行中。

  • BroadcastReceiver 上使用goAsync(),在接收器上下文中执行工作,同时不占用主应用程序线程。我没有在 Android 8.0+ 上尝试过这个,所以 YMMV。

【讨论】:

  • 感谢您的回答!我确实指出触发机制是在 AppWidgetProvider 上调用的 onUpdate()。当主屏幕需要刷新时,系统会调用 onUpdate,例如重启后,或者在 20 个应用程序之间切换,这会导致主屏幕内存不足。我不认为这些信息会改变你的答案。我将测试一个 JobIntentService 并看看它是如何执行的。谢谢!
  • @Justin:“我确实指出触发机制是在 AppWidgetProvider 上调用的 onUpdate()”——FWIW,这不是我所说的触发机制。您引用的事件(例如,重新启动)是一个触发器,如果​​您使用 updatePeriodMillis,时间的流逝也是如此。而且,没有什么能阻止您创建一个绑定到应用程序小部件中的按钮的PendingIntent,该按钮最终路由到onUpdate()。如果您关心的是updatePeriodMillis,您可以废弃它并使用JobScheduler 进行定期更新。对于其他触发器,我会尝试我的答案中概述的技术。
  • 移动到 JobIntentService 后,我的小部件有时不显示更新的响应。输入日志信息,我可以看到,在一种情况下,作业延迟了 7 分钟,最后 7 分钟的所有小部件更新都同时显示出来。我已经接受了答案,但我必须继续尝试其他方法,因为 JobIntentService 显然不适用于与 UI 相关的任何内容。
  • 初始测试看起来很适合在广播接收器中使用 goAsync()!
  • @Justin 你最后坚持使用 goAsync 了吗?
【解决方案2】:

您可以使用WorkManager 更新小部件。在 API 14+ 的设备上使用 WorkManager。您需要像这样覆盖 fun onReceive(context: Context?, intent: Intent?) :

val ACTION_AUTO_UPDATE : String = "AUTO_UPDATE";

override fun onReceive(context: Context?, intent: Intent?) {
    super.onReceive(context, intent)
    if(intent?.action.equals(ACTION_AUTO_UPDATE))
    {
        val appWidgetManager = AppWidgetManager.getInstance(context)
        val thisAppWidgetComponentName = ComponentName(context!!.getPackageName(), javaClass.name)
        val appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidgetComponentName)
        for (appWidgetId in appWidgetIds) {
            // update widget
        }
    }
}

您应该创建 PeriodicWorkRequest。您必须用于重复工作。定期工作的最小间隔为 15 分钟。当小部件启用时,我们将周期性工作排入队列:

override fun onEnabled(context: Context) {
    val periodicWorkRequest = PeriodicWorkRequest.Builder(YourWorker::class.java, 15, TimeUnit.MINUTES).build()
    WorkManager.getInstance(context).enqueueUniquePeriodicWork("YourWorker", ExistingPeriodicWorkPolicy.REPLACE,periodicWorkRequest)
}

当小部件被禁用时取消它:

override fun onDisabled(context: Context) {
    WorkManager.getInstance(context).cancelAllWork()
}

最后我们创建工人类:

class YourWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params) {
var context : Context? = null

init {
    context = ctx
}

override fun doWork(): Result {
    val alarmIntent = Intent(context, YourWidget::class.java)
    alarmIntent.action = YourWidget().ACTION_AUTO_UPDATE
    context?.sendBroadcast(alarmIntent)
    return Result.success()
}

如果您想使用 WorkerManager,请添加到 build.gradle implementation 'androidx.work:work-runtime:2.3.1'

您可以找到示例here

【讨论】:

  • 1) 该示例不包含与 AppWidgetProviders 中的 WorkManager 相关的任何内容。 2) 在 AppWidgetProvider 中使用 WorkManager.getInstance(context) 会在某些设备上导致 IllegalStateException。因为上下文可能不是应用程序上下文,并且 WorkManager 可能没有在上下文中初始化。 3) onEnabled/onDisabled() 不会帮助更新 onReceive() 的小部件
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-18
  • 1970-01-01
  • 2018-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多