38

我们开发了一个 Android 应用程序,其中涉及后台服务。为了实现这个后台服务,我们使用了IntentService. 我们希望应用程序每隔60 seconds. 因此,在 中IntentService,服务器在 while 循环中进行轮询。在我们使用的 while 循环结束时Thread.sleep(60000),下一次迭代仅在 60 秒后开始。
但是在 中Logcat,我看到有时应用程序需要超过 5 分钟才能唤醒(从睡眠中出来并开始下一次迭代)。它永远不会1 minute像我们想要的那样。

这是什么原因?后台服务应该以不同的方式实现吗?

问题2

一段时间后,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。为什么服务会被杀死?

4

10 回答 10

80

主要问题是我们不能说

服务不应该被杀死。只要我们愿意,它们就可以在后台运行。

基本上,事实并非如此。系统仍然可以在内存不足和可能的其他情况下终止服务。有两种方法可以克服这个问题:

  1. 如果您正在实施服务,请覆盖onStartCommand()START_STICKY作为结果返回。它会告诉系统,即使由于内存不足而想终止您的服务,它也应该在内存恢复正常后立即重新创建它。
  2. 如果您不确定第一种方法是否有效 - 您必须使用 AlarmManager http://developer.android.com/reference/android/app/AlarmManager.html。那是一个系统服务,它会在您告知时执行操作,例如定期执行。这将确保如果您的服务将被终止,甚至整个进程都会终止(例如强制关闭) - 它将由 AlarmManager 100% 重新启动。

祝你好运

于 2012-09-02T19:02:06.960 回答
6

您可以使用专门为此目的设计的ScheduledExecutorService 。

不要使用计时器,正如“Java 并发实践”中所展示的那样,它们可能非常不准确。

于 2012-08-31T16:59:28.083 回答
5

IntentService不打算继续while循环运行。这个想法是对一个做出反应Intent,做一些处理并在完成后停止服务。

这并不意味着它不起作用,我无法告诉您为什么会看到如此长的延迟,但更简洁的解决方案是使用一些外部资源来定期戳服务。除了 vanilla Java 方法之外,您还可以查看文档中提到的theAlarmManager或 a 。HandlerAlarmManager

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运行时始终发送自身 a (在onStartand之间onStop

于 2012-08-31T17:18:21.467 回答
4

更好的解决方案是每 60 秒触发一次 AlarmManager。然后此 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
                               }
                }
}
于 2012-09-06T00:13:10.353 回答
2

服务被杀死。就像应用程序被杀死一样。随时可能被杀死是 Android 的哲学。

正如其他人所写的那样,您不应该假设您的后台服务永远运行。

但是您可以使用前台服务来大大减少被杀死/重新启动的机会。请注意,这会强制发出始终可见的通知。例如,音乐播放器、vpn 应用程序和 sportstracker 使用此 API。

于 2012-09-07T12:27:11.200 回答
1

对于问题1,来自vanilla Java,Thread.Sleep()保证在定时器到期后唤醒线程,但不是完全在它到期后,它可能会更晚,主要取决于其他线程的状态,优先级等;所以如果你让你的线程休眠一秒钟,那么它至少会休眠一秒钟,但它可能是 10 取决于很多因素,我不是很精通 Android 开发,但我很确定这是同样的情况.

对于问题 2,服务可以在内存不足或由用户手动终止时终止,因此正如其他人所指出的那样,可能使用 AlarmManager 在一段时间后重新启动您的服务将帮助您让它一直运行。

于 2012-09-07T18:59:47.437 回答
0

Android 非常擅长终止长时间运行的服务。我发现 CommonsWare 的 WakefulIntentService 在我的应用程序中很有用:https ://github.com/commonsguy/cwac-wakeful

它允许您指定一个时间间隔,就像您尝试通过睡眠一样。

于 2012-09-07T22:46:37.453 回答
0

听起来你应该使用 aService而不是 anIntentService但如果你想使用 anIntentService并让它每 60 秒运行一次,你应该使用 theAlarmManager而不是只是告诉Thread睡眠.. IntentServices 想要停止,让它在AlarmManager什么时候唤醒它它应该再次运行。

于 2012-08-31T17:17:36.690 回答
0
It could be probably for two reasons..
  1. while循环正在创建一个问题,它使处理程序工作直到temp==true
  2. 添加到它的是线程,它正在创建long delaysupto 6 seconds。如果系统正在为大型数据库工作,则long delays在每个查询之间创建会增加系统内存。当应用程序的内存使用量变得如此巨大以至于system memory gets low系统必须terminate the process..

    问题的解决方案..

  3. Alarm Manager您可以使用警报管理器在特定时间间隔后将上述替换为撤销系统服务。

  4. 此外,为了在系统从终止中恢复应用程序后恢复意图,您应该使用START_REDELIVER_INTENT. 这是在应用程序终止后恢复您的最后一个工作意图。对于它的使用,研究https://developer.android.com/reference/android/app/Service.html#START_REDELIVER_INTENT
于 2012-09-07T05:36:05.640 回答
0

您可以尝试在后台运行 JobService 的 Jobscheduler 实现,这是在 Android O 之上推荐的。

于 2019-02-07T09:42:16.850 回答