我们开发了一个Android应用程序,它涉及后台服务。为了实现此后台服务,我们使用了IntentService
。我们希望应用程序每60 seconds
轮询一次服务器。所以在IntentService
中,服务器在while循环中轮询。在while循环结束时,我们使用了Thread.sleep(60000)
,以便下一次迭代仅在60秒后开始。
但在Logcat
中,我看到有时需要应用程序超过5几分钟醒来(从睡眠中走出来并开始下一次迭代)。它永远不会像我们希望的那样1 minute
。
这是什么原因?背景服务应该以不同的方式实施吗?
Problem2
Android会在一段时间后终止此后台进程(意向服务)。不能确切地说什么时候。但有时在后台服务被杀之前的几小时甚至几天。如果你能告诉我这个的原因,我将不胜感激。因为服务并不意味着被杀死。只要我们想要它们,它们就意味着在后台运行。
代码:
@Override
protected void onHandleIntent(Intent intent) {
boolean temp=true;
while(temp==true) {
try {
//connect to the server
//get the data and store it in the sqlite data base
}
catch(Exception e) {
Log.v("Exception", "in while loop : "+e.toString());
}
//Sleep for 60 seconds
Log.v("Sleeping", "Sleeping");
Thread.sleep(60000);
Log.v("Woke up", "Woke up");
//After this a value is extracted from a table
final Cursor cur=db.query("run_in_bg", null, null, null, null, null, null);
cur.moveToLast();
String present_value=cur.getString(0);
if(present_value==null) {
//Do nothing, let the while loop continue
}
else if( present_value.equals("false") || present_value.equals("False") ) {
//break out of the while loop
db.close();
temp=false;
Log.v("run_in_bg", "false");
Log.v("run_in_bg", "exiting while loop");
break;
}
}
}
但是每当服务被杀死时,它就会在进程处于睡眠状态时发生。最后一个日志显示为 - Sleeping : Sleeping
。为什么服务会被杀死?
答案 0 :(得分:78)
主要问题是我们不能说
服务并不意味着被杀死。只要我们想要它们,它们就意味着在后台运行。
基本上,这不是真的。系统仍然可以在低内存和其他情况下终止服务。 有两种方法可以解决这个问题:
onStartCommand()
并返回START_STICKY
作为结果。它会告诉系统即使它因内存不足而想要终止服务,只要内存恢复正常就应该重新创建它。答案 1 :(得分:6)
您可以使用专为此目的而设计的ScheduledExecutorService。
不要使用定时器,如"Java Concurrency in Practice"中所示,它们可能非常不准确。
答案 2 :(得分:5)
IntentService
无意继续在while
循环中运行。我们的想法是对Intent
做出反应,做一些处理并在完成后停止服务。
这并不意味着它不起作用,我不能告诉你为什么你会看到如此长的延迟,但更清晰的解决方案是使用一些外部来源定期戳服务。除了vanilla Java方法,您还可以查看Handler
文档中提到的AlarmManager
或AlarmManager
。
Handler
方式可以像这样工作
public class TriggerActivity extends Activity implements Handler.Callback {
// repeat task every 60 seconds
private static final long REPEAT_TIME = 60 * 1000;
// define a message id
private static final int MSG_REPEAT = 42;
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler(this);
}
@Override
protected void onStart() {
super.onStart();
// start cycle immediately
mHandler.sendEmptyMessage(MSG_REPEAT);
}
@Override
protected void onStop() {
super.onStop();
// stop cycle
mHandler.removeMessages(MSG_REPEAT);
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler = null;
}
@Override
public boolean handleMessage(Message msg) {
// enqueue next cycle
mHandler.sendEmptyMessageDelayed(MSG_REPEAT, REPEAT_TIME);
// then trigger something
triggerAction();
return true;
}
private void triggerAction() {
// trigger the service
Intent serviceIntent = new Intent(this, MyService.class);
serviceIntent.setAction("com.test.intent.OPTIONAL_ACTION");
startService(serviceIntent);
}
}
一个简单的Activity
(可以扩展为在所有活动中都具有该功能)在运行时始终发送Message
(此处位于onStart
和{onStop
之间{1}})
答案 3 :(得分:4)
更好的解决方案是让AlarmManager每60秒关闭一次。然后,此AlarmManager启动轮询服务器的服务,然后该服务启动一个新的AlarmManager,它是一个非常有效的递归解决方案。
此解决方案将更加可靠,因为您没有Android操作系统的威胁会导致您的服务中断,迫在眉睫。根据API:警报管理器适用于您希望在特定时间运行应用程序代码的情况,即使您的应用程序当前未运行。
在您的UI /主要活动等中,设置此计时器,在60秒内完成:
long ct = System.currentTimeMillis(); //get current time
AlarmManager mgr=(AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
Intent i= new Intent(getApplicationContext(), yourservice.class);
PendingIntent pi=PendingIntent.getService(getApplicationContext(), 0, i, 0);
mgr.set(AlarmManager.RTC_WAKEUP, ct + 60000 , pi); //60 seconds is 60000 milliseconds
在yourservice.class
你可以拥有它,它会检查连接状态,如果它有好处,它会将计时器设置为在另外60秒内关闭:
public class yourservice extends IntentService {
public yourservice() { //needs this constructor
super("server checker");
}
@Override
protected void onHandleIntent(Intent intent) {
WifiManager wificheck = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
if(check for a certain condition your app needs etc){
//could check connection state here and stop if needed etc
stopSelf(); //stop service
}
else{ //poll the server again in 60 seconds
long ct = System.currentTimeMillis();
AlarmManager mgr=(AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
Intent i= new Intent(getApplicationContext(), yourservice.class);
PendingIntent pi=PendingIntent.getService(getApplicationContext(), 0, i, 0);
mgr.set(AlarmManager.RTC_WAKEUP, ct + 60000 , pi);
stopSelf(); //stop service since its no longer needed and new alarm is set
}
}
}
答案 4 :(得分:2)
服务被杀死。就像app被杀了一样。 Android哲学可以随时杀死。
你应该像其他人所写的那样假设你的背景服务永远运行。
但你可以使用前ground service来大幅降低被杀/重启的可能性。请注意,这会强制始终可见的通知。例如音乐播放器,vpn应用程序和sportstracker使用此API。
答案 5 :(得分:1)
对于问题1,来自vanilla Java,Thread.Sleep()
可以保证在计时器到期后唤醒线程,但不完全在它到期后,它可能稍后主要取决于其他线程的状态,优先级等等;所以,如果你让你的线程休息一秒钟,那么它至少会睡一秒钟,但是根据很多因素它可能是10,我对Android开发并不是很精通,但我很确定这是相同的情况
对于问题2,服务可以在内存变低或用户手动时被杀死,因此其他人指出可能使用AlarmManager在一段时间后重新启动服务将帮助您让它一直运行。
答案 6 :(得分:0)
听起来你应该使用Service
代替IntentService
但是如果你想使用IntentService
并让它每60秒运行一次,你应该使用AlarmManager
而不只是告诉Thread
要睡觉.. IntentService
想要停止,让它在AlarmManager
再次运行时将其唤醒。
答案 7 :(得分:0)
It could be probably for two reasons..
temp==true
添加它是线程,即创建long delays
至6 seconds
。
如果系统正在为大型数据库工作,则在每个查询之间创建long delays
将添加到系统内存中。
当应用程序的内存使用量变得如此之大以至于system memory gets low
,系统必须terminate the process
..
解决问题..
您可以使用警报管理器在特定时间间隔后用Alarm Manager
替换以撤消系统服务。
START_REDELIVER_INTENT
。在应用程序终止后,它将恢复您的上一次工作意图。对于它的用法,研究https://developer.android.com/reference/android/app/Service.html#START_REDELIVER_INTENT 答案 8 :(得分:0)
Android非常适合杀掉长期运行的服务。我发现CommonsWare的WakefulIntentService在我的应用程序中很有用:https://github.com/commonsguy/cwac-wakeful
它允许您通过睡眠来指定时间间隔。
答案 9 :(得分:0)
您可以尝试在后台运行JobService的Jobscheduler实现,建议在Android O之上运行。