How To Properly Update A Widget In Android 8.0 - Oreo - API 26
You don't indicate what the update trigger mechanism is. You seem concerned about latency ("Your widget may or may not get updated for a while"), so I am going to assume that your concern is tied to user interaction with the app widget, such as tapping a button.
Use JobScheduler to schedule a job as quickly as possible. Your widget may or may not get updated for a while.
This is a variation on "use JobIntentService
", which AFAIK is the recommended solution for this sort of scenario.
Other options include:
Use
getForegroundService()
withPendingIntent
. With this, you effectively "pinky swear" that your service will callstartForeground()
within the ANR timeframe. If the work takes longer than a few seconds, callstartForeground()
to ensure that Android doesn't get cranky. This should minimize the number of time the foreground notification appears. And, if the user tapped a button and you are still busy doing work a few seconds later, you probably want to show a notification or otherwise do something to let the user know that what they asked for is still in progress.Use
goAsync()
onBroadcastReceiver
, to do work in the context of the receiver while not tying up the main application thread. I haven't tried this with Android 8.0+, so YMMV.
You can use WorkManager to update a widget. Uses WorkManager on devices with API 14+. You need to override fun onReceive(context: Context?, intent: Intent?) like this:
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
}
}
}
And you should create PeriodicWorkRequest. You have to use for repeating work. Periodic work has a minimum interval of 15 minutes. We enqueue the periodicWork when widget is enabled:
override fun onEnabled(context: Context) {
val periodicWorkRequest = PeriodicWorkRequest.Builder(YourWorker::class.java, 15, TimeUnit.MINUTES).build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork("YourWorker", ExistingPeriodicWorkPolicy.REPLACE,periodicWorkRequest)
}
And cancel it when widget is disabled:
override fun onDisabled(context: Context) {
WorkManager.getInstance(context).cancelAllWork()
}
Finally we create worker class:
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()
}
If you want to use WorkerManager you add to build.gradle implementation 'androidx.work:work-runtime:2.3.1'
You can find the sample here.