Muz*_*ant 51 android sticky android-service
我的一个应用程序有一个backgrouod服务,它使用START_STICKY返回代码onStartCommand在系统杀死它时自动重启.似乎这不再适用于Android KitKat.这有什么解决方案吗?我应该在Kitkat做一些不同的事情来保持服务的运行吗?
注意:在Android-Devlopers小组中有一个关于从最近的应用列表行为中滑动应用程序的类似讨论.这两个问题可以相关吗? https://groups.google.com/forum/#!topic/android-developers/H-DSQ4-tiac
编辑:看到Android问题跟踪器上有开放的错误:
https://code.google.com/p/android/issues/detail?id=63793 https://code.google.com/p/android/issues/detail?id=63618
Edit2:即使服务正在运行startForeground,在单独的进程android:stopWithTask="false"中使用AndroidManifest.xml文件中的标志,也会发生同样的情况......
Edit3:Android问题跟踪器上的更多相关错误:
https://code.google.com/p/android/issues/detail?id=62091 https://code.google.com/p/android/issues/detail?id=53313 https://code.google. COM/p /安卓/问题/细节?ID = 104308
是否有某种解决方法来获得以前的行为?
Rak*_*ari 28
似乎这是Android 4.4中存在的一个错误,并通过以下方式解决了这个问题:
@Override
public void onTaskRemoved(Intent rootIntent) {
Intent restartService = new Intent(getApplicationContext(),
this.getClass());
restartService.setPackage(getPackageName());
PendingIntent restartServicePI = PendingIntent.getService(
getApplicationContext(), 1, restartService,
PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarmService.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() +1000, restartServicePI);
}
Run Code Online (Sandbox Code Playgroud)
从这篇文章中找到了这个答案
Geo*_*ner 22
这里的问题似乎不会出现在基于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}
Run Code Online (Sandbox Code Playgroud)
如果我从最近的任务列表中"滑动"结束任务,我会看到相同的重启行为.所以这一切都很好 - 这意味着核心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可以提供正常运行时间与使用实时的警报服务,因此即使设置的时间比"重置"处理程序长,也可以触发警报.但是如果你设置了额外的意图,你可以选择忽略onStartCommand,如果你的服务已经启动并运行,将其变成noop.
我根本不是以下黑客的粉丝 - 但它不应该造成任何真正的伤害.如果用户执行显式强制关闭,则警报管理器将销毁任何警报集,以便服务不会重新启动(这是用户想要的).
首先,创建一个帮助方法,将警报设置为20分钟,这将导致为您的服务触发onStartCommand.每2分钟有一个Handler,它将重置20分钟的闹钟.如果处理程序在实时20分钟内运行,则警报将永远不会消失.如果设备处于睡眠状态(这很好),则无法保证处理程序运行.
private void ensureServiceStaysRunning() {
// KitKat appears to have (in some cases) forgotten how to honor START_STICKY
// and if the service is killed, it doesn't restart. On an emulator & AOSP device, it restarts...
// on my CM device, it does not - WTF? So, we'll make sure it gets back
// up and running in a minimum of 20 minutes. We reset our timer on a handler every
// 2 minutes...but since the handler runs on uptime vs. the alarm which is on realtime,
// it is entirely possible that the alarm doesn't get reset. So - we make it a noop,
// but this will still count against the app as a wakelock when it triggers. Oh well,
// it should never cause a device wakeup. We're also at SDK 19 preferred, so the alarm
// mgr set algorithm is better on memory consumption which is good.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
{
// A restart intent - this never changes...
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) {
// Create a pending intent
PendingIntent pintent = PendingIntent.getService(getApplicationContext(), 0, restartIntent, 0);
alarmMgr.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + restartAlarmInterval, pintent);
sendEmptyMessageDelayed(0, resetAlarmTimer);
}
};
restartServiceHandler.sendEmptyMessageDelayed(0, 0);
}
}
Run Code Online (Sandbox Code Playgroud)
在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;
}
}
// Do your other onStartCommand stuff..
return START_STICKY;
}
Run Code Online (Sandbox Code Playgroud)
Muz*_*ant 16
这不是一个100%可行的解决方案,但它几乎完全消除了问题,它是最好的.到目前为止,我整合了此解决方案以及覆盖onTaskRemoved(请参阅此答案)和保持活动通知(请参阅此答案).其他答案非常感谢!
经过进一步的调查,似乎这个bug已经存在于Jelly Bean中并且看起来有一个解决方案(至少在我的情况下似乎有效.如果需要,将继续测试并更新答案).
根据我的观察,这只发生在接收广播设置的服务上AlarmManager.
要重现该错误,请按照下列步骤操作:
startForeground从应用程序中启动服务作为前台服务(用于此)使用adb shell dumpsys >C:\dumpsys.txt您可以监视不同步骤之间的服务状态.(Process LRU list在dumpsys输出中查找)在第2步和第3步,您将看到如下内容:
Proc # 2: prcp F/S/IF trm: 0 11073:<your process name>/u0a102 (fg-service)
Run Code Online (Sandbox Code Playgroud)
具体来说,请注意F/S/IF并(fg-service)指示该服务作为前台服务运行(有关如何在此链接上分析dumps的更多详细信息:https://stackoverflow.com/a/14293528/624109 ).
在第4步之后,您将看不到您的服务Process LRU list.相反,您可以查看设备logcat,您将看到以下内容:
I/ActivityManager(449): Killing 11073:<your process name>/u0a102 (adj 0): remove task
Run Code Online (Sandbox Code Playgroud)
似乎导致该行为的原因是接收到的广播将服务从其前景状态中取出然后被杀死.
为了避免这种情况,你可以使用这个简单的创建解决方案时,您PendingIntent对AlarmManager(来源:https://code.google.com/p/android/issues/detail?id=53313#c7)
AlarmManager am = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent("YOUR_ACTION_NAME");
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 1, intent, 0);
Run Code Online (Sandbox Code Playgroud)
请注意以下步骤:
FLAG_RECEIVER_FOREGROUND如果您将这些步骤中的任何一个退出,它将无效.
请注意,它FLAG_RECEIVER_FOREGROUND是在API 16(Jelly Bean)上添加的,因此有意义的是,这是bug首次出现时......
最有可能的是KitKat在杀死进程时更具侵略性,这就是为什么KitKat强调它的原因,但看起来这已经与Jelly Bean相关了.
注意2:请注意有关服务配置的问题中的详细信息 - 作为前台服务在单独的进程中运行,并在清单中将endWithTask设置为false.
注3:当应用程序收到android.appwidget.action.APPWIDGET_CONFIGURE消息并显示新小部件的配置活动时,会发生同样的事情(通过创建新小部件替换上面的步骤4).我发现只有在窗口小部件提供程序(处理的接收方android.appwidget.action.APPWIDGET_UPDATE)设置为在与活动进程不同的进程上运行时才会发生.更改后,配置活动和窗口小部件提供程序都在同一个进程上,这不再发生.
小智 8
我发现这个简单的技巧来解决这个问题而不使用AlarmManager.
创建一个广播接收器,每次onDestroy()调用服务中的方法都会调用广播:
public class RestartService extends BroadcastReceiver {
private static final String TAG = "RestartService";
public RestartService() {
}
@Override
public void onReceive(Context context, Intent intent) {
Log.e(TAG, "onReceive");
context.startService(new Intent(context, YourService.class));
}
}
Run Code Online (Sandbox Code Playgroud)向清单添加自定义广播意图
<receiver
android:name=".RestartService"
android:enabled="true" >
<intent-filter>
<action android:name="restartApps" />
</intent-filter>
</receiver>
Run Code Online (Sandbox Code Playgroud)然后,发送广播onDestroy(),可能是这样的:
@Override
public void onDestroy() {
Intent intent = new Intent("restartApps");
sendBroadcast(intent);
super.onDestroy();
stopThread();
}
Run Code Online (Sandbox Code Playgroud)调用onDestroy()从onTaskRemoved(Intent intent)
每次用户从任务管理器关闭服务并强制关闭设置时,此技巧将重新启动您的服务,我希望这也会对您有所帮助
| 归档时间: |
|
| 查看次数: |
33218 次 |
| 最近记录: |