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

后端 未结 2 1844
甜味超标
甜味超标 2020-12-05 00:13

Let\'s say I have a widget for an app that has targetSDKVersion set to 26. This widget takes between 100ms and 10s to update. Most of the time under 1s. Before Android O, if

相关标签:
2条回答
  • 2020-12-05 00:56

    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() with PendingIntent. With this, you effectively "pinky swear" that your service will call startForeground() within the ANR timeframe. If the work takes longer than a few seconds, call startForeground() 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() on BroadcastReceiver, 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.

    0 讨论(0)
  • 2020-12-05 00:59

    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.

    0 讨论(0)
提交回复
热议问题