这里的问题似乎不会出现在基于AOSP的ROM上。也就是说,我可以很容易地在基于CyanogenMod 11的ROM上重新创建这个问题,但在AOSP ROM(和模拟器)上,START_STICKY的行为恰好符合我的预期。话虽如此,我看到来自Nexus 5用户的报告显示他们似乎遇到了这个问题,所以也许在AOSP中仍然存在这个问题。
在模拟器和AOSP ROM上,当我对进程执行“kill 5838”时,我从logcat中看到以下内容(正如我所预期的)。
12-22 18:40:14.237 D/Zygote ( 52): Process 5838 terminated by signal (15)
12-22 18:40:14.247 I/ActivityManager( 362): Process com.xxxx (pid 5838) has died.
12-22 18:40:14.247 W/ActivityManager( 362): Scheduling restart of crashed service com.xxxx/com.xxxx.NotifyingService in 5000ms
12-22 18:40:19.327 I/ActivityManager( 362): Start proc com.xxxx for service xxxx.pro/com.xxxx.NotifyingService: pid=5877 uid=10054 gids={50054, 3003, 3002, 1028}
我发现如果我从最近任务列表中“滑动”结束任务,会出现相同的重启行为。所以这很好——这意味着核心AOSP代码的行为与以前的级别相同。
我正在查看Cyanogenmod服务代码,试图弄清楚为什么事情没有被安排重新启动——但还没有成功。它似乎应该重新安排它。Cyanogenmod使用一个AOSP没有的服务映射,但不确定是否是问题(可疑的)
https://github.com/CyanogenMod/android_frameworks_base/blob/cm-11.0/services/java/com/android/server/am/ActiveServices.java#L2092。
一个相当巧妙的解决方法是,使用类似于onTaskRemoved AlarmService的机制来启用一个X分钟后的闹钟。然后,在您的应用程序正在运行时,每隔几分钟重置闹钟,这样只有在事情真正被杀死而没有重新启动时才会响起。这并不是绝对可靠的——使用Handler可以获得正常运行时间,而使用Alarm Service则使用实时时间,因此即使您的“重置”处理程序设置了比较长的时间,也有可能触发您的闹钟。但是,如果您设置了一个意图额外项,您可以选择忽略onStartCommand,如果您的服务已经在运行中,则将其转换为noop。
我根本不喜欢以下hack——但它不会造成任何真正的伤害。如果用户进行显式强制关闭,那么闹钟管理器将销毁任何设置的闹钟,以便服务不会重新启动(这就是用户想要的)。
首先,创建一个帮助程序方法,将设置一个20分钟的闹钟,这将导致onStartCommand被触发为您的服务。每2分钟有一个处理程序,将重置20分钟的闹钟。如果处理程序在实时20分钟内运行,则闹钟永远不会响起。如果设备处于睡眠状态,则不能保证处理程序运行(这很好)。
private void ensureServiceStaysRunning() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
{
final int restartAlarmInterval = 20*60*1000;
final int resetAlarmTimer = 2*60*1000;
final Intent restartIntent = new Intent(this, NotifyingService.class);
restartIntent.putExtra("ALARM_RESTART_SERVICE_DIED", true);
final AlarmManager alarmMgr = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
Handler restartServiceHandler = new Handler()
{
@Override
public void handleMessage(Message msg) {
PendingIntent pintent = PendingIntent.getService(getApplicationContext(), 0, restartIntent, 0);
alarmMgr.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + restartAlarmInterval, pintent);
sendEmptyMessageDelayed(0, resetAlarmTimer);
}
};
restartServiceHandler.sendEmptyMessageDelayed(0, 0);
}
}
在您的onCreate方法中,您可以调用此方法。同样,在您的onStartCommand方法中,请确保如果您的服务已经启动并运行,则忽略此方法。例如:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
...
if ((intent != null) && (intent.getBooleanExtra("ALARM_RESTART_SERVICE_DIED", false)))
{
Log.d(TAG, "onStartCommand after ALARM_RESTART_SERVICE_DIED");
if (IS_RUNNING)
{
Log.d(TAG, "Service already running - return immediately...");
ensureServiceStaysRunning();
return START_STICKY;
}
}
return START_STICKY;
}
START_STICKY
服务将不会重新启动,也不会有"Scheduling restart of crashed service
"日志条目。4.3没有受到影响,但这是一个AOSP问题,而不是一些修改(例如CM)引入的问题,因为我能够在AOSP模拟器中重现这个问题。我不认为这是一个有意的更改。 - Flow