0

In an AppWidgetProvider class, I have a simple CountDownTimer running.

object : CountDownTimer(30000, 1000) {

    override fun onTick(millisUntilFinished: Long) {
        val appWidgetManager = AppWidgetManager.getInstance(context)
        val views = RemoteViews(context.packageName,R.layout.view_group)
        views.setTextViewText(R.id.m_text_view,"seconds remaining: " + millisUntilFinished / 1000)
        appWidgetManager.updateAppWidget(appWidgetIds[0], views)
    }

    override fun onFinish() {
        // Do nothing
    }
}.start()

This updates the corresponding widget (appWidgetIds[0]) as expected, but this is only the case while the app is open. When I swipe off the app in the recents menu, I.e. closing it, the widget stops updating.

I have tried using a separate service too, but same result. Presumably because the service is not a foreground service, and I don't want to have a persistent notification so I can't use a foreground service.

olfek
  • 3,210
  • 4
  • 33
  • 49

2 Answers2

2

When I swipe off the app in the recents menu, I.e. closing it, the widget stops updating.

That generally terminates your process, at which point your CountDownTimer no longer exists.

I have tried using a separate service too, but same result

That would not necessarily prevent your process from being terminated, though it might help get you a process back faster. However, on Android 8.0+, your service would be stopped after a minute.

Doing work every 30 seconds in the background is fairly evil from a battery consumption standpoint, which is why it is impractical on modern versions of Android.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Surely it cant be impossible, there must be a way. I've seen other apps successfully implement this functionality. I.e. widget count down timers. – olfek Jun 10 '18 at 14:19
  • 1
    @daka: Many of those other apps will suffer from the same limitation that you are seeing. A few might spawn the service in a separate process, but that might not fix your task-swiping problem on all devices, and even then the app will still have limits (Doze mode/app standby on Android 6.0+, 1-minute service lifetime on Android 8.0+, etc.). It also does not change the underlying problems with your app (e.g., having to hold a `WakeLock` to keep the device awake constantly, so your `CountDownTimer` continues running) that harm users. – CommonsWare Jun 10 '18 at 14:24
  • Okay, I get that there is a lot going against me android 6+, but I just tried it on android 4.0 and I get the same issue. – olfek Jun 10 '18 at 14:45
  • 1
    @daka: You can try having your service run in a separate process and see if it gives you better results on older versions of Android. – CommonsWare Jun 10 '18 at 14:46
  • Thank you for pointing me in the right direction, I found the answer. – olfek Jun 10 '18 at 15:21
  • I've posted it below – olfek Jun 10 '18 at 15:33
  • Now I have an issue where if the widget exists, and _then_ then app is started and closed, the widget service does not restart. – olfek Jun 10 '18 at 15:57
  • This issue only affects android versions greater than 6.0 (API 23). I think I will rethink what I wish to offer in my app, because it seems the frequent updating of the widget doesn't play well with the latest (and probably future) versions of android. – olfek Jun 10 '18 at 19:46
-1

Found the answer.

In the on the onStartCommand method of the service, return START_STICKY flag. This will tell the OS, if for any reason the service closes, run it again when you have enough resources.

source
more info

On restart, you can get all widget ids as follows:

ComponentName thisWidget = new ComponentName(context,MyWidgetProvider.class);
int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);

source

EDIT

Just so you know, in versions of android greater than API 23, even the START_STICKY flag isn't enough to get the OS to restart the service.

I've decided to skip this feature because it won't work in future versions of android.

olfek
  • 3,210
  • 4
  • 33
  • 49