How to update a widget if the related service gets killed?

给你一囗甜甜゛ 提交于 2019-12-04 18:35:43

I have found what I consider to be an answer to my own question.

I return START_STICKY from onStartCommand() in my service.

If I manually kill the service from DDMS, I get this in logcat:

I/ActivityManager( 2500): Process com.foo.bar (pid 21874) has died.
W/ActivityManager( 2500): Scheduling restart of crashed service com.foo.bar/.FooBarService in 5000ms

And a few seconds later:

I/ActivityManager( 2500): Start proc com.foo.bar for service com.foo.bar/.FooBarService: pid=22036 uid=10107 gids={1015, 3003} 

The service does restart, and that allows me to send a broadcast from onStartCommand() to indicate that the audio engine is paused (I don't resume recording automatically).

The widget provider reacts to this broadcast and updates the widget appropriately.

This works on Froyo and solves my problem: the user is not misled when looking at the widget. Recording stopped because the engine crashed, and the widget reflects this.

It's not bullet-proof, as it relies on the system restarting the service automatically, but I think that's quite safe.

So how can I update the widget quickly if the service gets killed?

You can't. Hence, use startForeground() to indicate to Android that your service is part of the foreground user experience.

Foreground service's process will be killed when user swipes it away in recent tasks list.

At swipe moment android will call (*) you service's onTaskRemoved method. You can update your widget right there:

@Override
public void onTaskRemoved(Intent rootIntent) {
    Log.i(TAG, "onTaskRemoved, stopping service");

    AppWidgetManager widgetManager       = AppWidgetManager.getInstance(this);
    ComponentName    widgetComponentName = new ComponentName(this, MyWidgetProvider.class);
    widgetManager.updateAppWidget(widgetComponentName, buildWidgetRemoteViews());

    stopSelf();
}

If you call stopSelf inside onTaskRemoved, service will finish normally: onDestroy will be called and it will not be scheduled for restart.
If you do not call stopSelf, Android will kill process and continue as with usual service kill - according to onStartCommand return value:

  • START_NOT_STICKY - service will not be restarted
  • START_STICKY - service will be restarted with empty intent
  • ... etc

Let me emphasis this - even if you don't call stopSelf, service will stop functioning right upon onTaskRemoved exit. So you can't send broadcast or schedule something on Handler. However, if you really want broadcast, use AlarmManager:

final int requestCode = 101;
final AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
final PendingIntent clearWidget = PendingIntent.getBroadcast(
        this,
        requestCode,
        getClearWidgetIntent(),
        PendingIntent.FLAG_UPDATE_CURRENT);

// if broadcast arrives while process is shutting down, widget update may fail
// 100ms was always OK on devices I've tested and instantaneous for user
final long startDelay = 100;
alarmManager.set(AlarmManager.ELAPSED_REALTIME,
                 SystemClock.elapsedRealtime() + startDelay,
                 clearWidget);

I've faced the same problem as you and originally solved it the same way - cleared widget in onStartCommand. But on one of the devices service's restart has been always scheduled in 5 seconds, which is very visible to the user and makes a feeling of a slow app.

Notes:
*) actually it could be not called - see docs

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!