如果相关服务被杀死,如何更新小部件?

时间:2011-04-23 12:13:27

标签: android service android-widget

我有一个录音应用程序,我正在为此开发一个小部件。

录制是由处于前台状态的服务中运行的音频引擎执行的。

每当音频引擎状态变为暂停/播放/录制时,广播就会被发送,并由更新小部件的接收者处理。

因此,单击窗口小部件中的记录按钮开始记录,这将导致广播被发送,最后记录按钮在窗口小部件中变为红色。如果您从应用程序而不是小部件开始录制,也会发生同样的事情。

到目前为止一切都很好。

现在,如果服务因某种原因被杀死,则不会调用onDestroy()并且我没有机会发送暂停或关闭广播,以便关闭记录按钮。结果:按钮保持红色,而录制已停止。

这是可能发生的最糟糕的事情。

为什么呢?用户查看他的主屏幕,他/她看到红色按钮并认为仍在执行录制。

与音乐播放不同,用户可以注意到音乐何时停止通过耳机播放...录音时,如果音乐停止,则不会听到任何声音。

我可以理解系统需要杀死服务。我很好。我不需要重新启动服务以及所有这些。

但是如果服务关闭,我需要更新小部件,这样用户就不会被误导,认为他的采访/音乐会/排练/备忘录仍然被记录,而事实并非如此。

那么如果服务被杀,我如何快速更新小部件

3 个答案:

答案 0 :(得分:3)

我发现我认为是对自己问题的回答。

我在我的服务中从onStartCommand()返回START_STICKY

如果我从DDMS手动终止服务,我会在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

几秒钟后:

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

该服务确实重启,这允许我从onStartCommand()发送广播以指示音频引擎已暂停(我不会自动恢复录制)。

窗口小部件提供程序对此广播作出反应并适当地更新窗口小部件。

这适用于Froyo并解决了我的问题:用户在查看窗口小部件时不会被误导。由于引擎崩溃,录制停止,小部件反映了这一点。

它不是防弹的,因为它依赖于系统自动重启服务,但我认为这是非常安全的。

答案 1 :(得分:0)

  

那么如果服务被杀,我怎么能快速更新小部件呢?

你做不到。因此,使用startForeground()向Android表明您的服务是前台用户体验的一部分。

答案 2 :(得分:0)

当用户在最近的任务列表中滑动它时,前台服务的进程将被终止。

在轻扫时刻,机器人会调用(*)您服务的onTaskRemoved方法。您可以在那里更新您的小部件:

@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();
}

如果您在stopSelf内拨打onTaskRemoved,服务将正常结束:onDestroy将被调用,并且不会安排重启。
如果您致电stopSelf,则Android将终止进程并继续执行常规服务终止 - 根据onStartCommand返回值:

  • START_NOT_STICKY - 服务不会重新启动
  • START_STICKY - 服务将以空意图重新启动
  • ...... etc

让我强调一下 - 即使你不打电话给stopSelf,服务也会在onTaskRemoved退出时停止运作。因此,您无法在Handler上发送广播或安排某些内容。但是,如果您真的想要广播,请使用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);

我遇到了和你一样的问题,并且最初以同样的方式解决了它 - onStartCommand中已清除的小部件。但是在其中一个设备上,服务的重启始终在5秒内完成,这对用户来说非常明显,让人觉得应用程序很慢。

注意:
*)实际上它可能没有被调用 - 参见docs