我已经阅读了有关该主题的所有Stackoverflow答案,但没有一个起作用。
问题:每当我的设备进入睡眠模式一个小时或更长时间时,该服务就会被终止
从START_STICKY
返回onStartCommand()
并使用startForeground()
public int onStartCommand(Intent intent, int flags, int startId) {
notification = makeStickyNotification(); //I've simplified the irrelevant code, obviously this would be a real notification I build
startForeground(1234, notification);
return START_STICKY;
}
这可以很好地工作,并且即使设备内存不足,它甚至可以重新启动我的服务,但不足以解决我的设备进入睡眠状态时出现的问题。
在我的活动的onCreate()
和服务的onStartCommand()
中使用警报管理器来呼叫调用我的服务的广播接收器
Intent ll24 = new Intent(this, AlarmReceiver.class);
PendingIntent recurringLl24 = PendingIntent.getBroadcast(this, 0, ll24, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarms = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarms.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 1000*60, recurringLl24); // Every minute
这有助于使我的服务保持活动状态,但又不能解决我的问题
使用Schedule Task Executor保持活动状态
if (scheduleTaskExecutor == null) {
scheduleTaskExecutor = Executors.newScheduledThreadPool(1);
scheduleTaskExecutor.scheduleAtFixedRate(new mainTask(), 0, 1, TimeUnit.SECONDS);
}
...
class mainTask implements Runnable {
public void run() {
// 1 Second Timer
}
}
这也只能使服务保持活动状态,但长时间睡眠后也无法保持活动状态。
单独的任务清单
android:launchMode="singleTop"
这什么也没做
我如何(1)不必使手机进入睡眠状态并每小时检查一次就可以测试此问题,以及(2)即使设备处于睡眠状态也可以保持服务运行?
答案 0 :(得分:15)
您的服务被 Doze or Standby mode of Android 杀死。这是在Android 6.0(API级别23)中引入的。
打ze限制
在Doze中,以下限制适用于您的应用程序:
- 网络访问已暂停。
- 系统忽略唤醒锁。
- 标准
AlarmManager
警报(包括setExact()
和setWindow()
)被推迟到下一个维护窗口。- 如果您需要设置在打ze睡时触发的警报,请使用
setAndAllowWhileIdle()
或setExactAndAllowWhileIdle()
。- 设置为
setAlarmClock()
的警报继续正常触发-在这些警报触发之前,系统立即退出Doze。- 系统不执行Wi-Fi扫描。
- 系统不允许运行同步适配器。系统不允许
JobScheduler
运行。
因此系统忽略了您的Alarm Clocks, Scheduler
等
为改善用户体验,Android 8.0(API级别26)强制实施 应用程序在后台运行时可以做什么的限制。
如果应用仍然需要始终运行其服务,那么我们可以创建前台服务。
后台服务限制:当应用处于空闲状态时,存在一些限制 使用后台服务。这不适用于前台 服务,对用户来说更明显。
因此,请创建前台服务。服务运行时,您将在其中放置用户通知。 See this answer(还有很多其他人)
现在,如果您不希望收到服务通知,该怎么办。一个解决方案就是解决这个问题。
您可以创建一些定期任务来启动服务,服务将完成其工作并自行停止。如此一来,您的应用就不会被视为耗电了。
您可以使用Alarm Manager,Job Scheduler,Evernote-Jobs或Work Manager创建定期任务。
我使用Work-Manager创建了永久运行的服务,效果很好。
答案 1 :(得分:6)
谋杀之谜已经解决,我知道是什么使我的服务丧生。这就是我所做的:
startsticky
,startforeground
,alarmmanager
,scheduleTaskExecutor
,甚至 wakelock
无法保存我服务,我意识到凶手不可能是Android系统,因为我已采取一切可能的措施来防止该系统杀死我的服务,并且它仍然会被杀死。 我意识到我需要寻找另一个嫌疑犯,因为该服务不会因为系统而死。为此,我不得不进行调查。我运行了以下命令:
adb shell dumpsys activity processes > tmp.txt
这将为我提供所有正在运行的进程及其系统优先级的详细日志。本质上,tmp.txt
将是这个谋杀之谜中的侦探。
我仔细浏览了文件。看来系统对我的服务进行了优先排序:
Proc #31: adj=prcp /FS trm= 0 2205:servicename.service/uID (fg-service)
上面的行表示在Android设备上运行的进程的确切优先级。 adj=prcp
表示该服务是可见的前台服务。
这时,我意识到我的服务必须在运行几个小时后遇到一些错误,因此我让它运行并终止。死后,我再次产生dumpsys
来检查错误:
tmp.txt
文件中未将我的服务列为任务。激动的是,我滚动到dumpsys
的底部,解决了这个谜团!com.curlybrace.ruchir.appName.MyService $ 2.onForeground(MyService.java:199) 在com.rvalerio.fgchecker.AppChecker $ 2.run(AppChecker.java:118) 在android.os.Handler.handleCallback(Handler.java:751) 在android.os.Handler.dispatchMessage(Handler.java:95) 在android.os.Looper.loop(Looper.java:154) 在android.app.ActivityThread.main(ActivityThread.java:6123) 在java.lang.reflect.Method.invoke(本机方法) 在com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run(ZygoteInit.java:867) 在com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
导致我的服务被终止的堆栈跟踪显示在这里!从本质上讲,用于检查正在使用的前台应用程序的变量在不活动数小时后将变为空,这将导致异常并终止服务!
要点:
如果您的服务被终止,并且您已尽一切努力确保它不会被终止,请执行dumpsys
并检查设备活动过程的实质。我保证您会以这种方式找到问题。
我仍然希望获得@Khemraj的赏金,因为对于那些未正确启动服务的人来说,他的回答可能是一个很好的解决方案。但是,我接受这个答案,因为实际上是解决问题的解决方案。
答案 2 :(得分:1)
从SDK 26开始,服务应在前台具有其相对的“ MainActivity”,或者应使用“ startForegroundService()”在前台启动该服务。如果目标SDK为26+,则“ startForeground()”将无法正常工作,但需要使用我刚才解释的另一种方法。
此后,您可以使用以下代码杀死并重新启动应用程序(是的,即使该服务也以这种方式被杀死):
Intent mStartActivity = new Intent(context, StartActivity.class);
int mPendingIntentId = 123456;
PendingIntent mPendingIntent = PendingIntent.getActivity(context, mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager mgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent);
System.exit(0);
答案 3 :(得分:1)
onDestroy()确实不可靠,不会经常被调用。与 onLowMemory()回调相同。如果android决定终止您的进程或用户决定强制停止您的应用,则无法采取保证回调的方法。
这比用户设备进入睡眠模式是正常的,您的服务会死掉。了解有关wakelocks的信息。在服务中尝试类似的方法: 在清单中:
<uses-permission android:name="android.permission.WAKE_LOCK" />
使用中:
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"tag");
wakeLock.acquire();
但是对于用户来说,这是一个棘手的难题,在Android世界中,它完全是反模式的,因为电池消耗很小。
另一个选择是每隔10分钟触发一次服务。在WakefulBroadcastReceiver(可在其中启动服务)上做出待定的意图,并使用带有标志 RTC_WAKE_UP
的警报管理器对其进行调度答案 4 :(得分:1)
打ze模式会终止服务以节省电池。对您来说,唯一有效的解决方案是在Oreo及更高版本中创建前台服务。 https://developer.android.com/about/versions/oreo/background