繁体   English   中英

如何在 Android 8.0 中正确更新小部件 - Oreo - API 26

[英]How To Properly Update A Widget In Android 8.0 - Oreo - API 26

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

可能的解决方案

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

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

(更令人沮丧的是,我的应用程序已经通过系统调用 onUpdate() 加载到内存中。我不知道如何将我的应用程序加载到内存中以调用 onUpdate(),但是没有给我的应用程序 1s 来更新小部件UI 线程正在为任何人节省电池寿命。)

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

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

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

其他选项包括:

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

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

您可以使用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 分钟。 当启用小部件时,我们将 periodWork 加入队列:

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'

您可以在此处找到示例。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM