【问题标题】:Android: keeping a background service alive (preventing process death)Android:保持后台服务活着(防止进程死亡)
【发布时间】:2011-04-20 21:12:00
【问题描述】:

我有一个服务被定义为:

public class SleepAccelerometerService extends Service implements SensorEventListener

基本上,我正在制作一个应用程序,用于在用户将手机/设备放在床上睡觉时出于各种原因监控加速度计活动。这是一项长期运行的服务,不得在夜间终止。根据夜间发生的后台应用程序和周期性进程的数量,android 有时会杀死我的进程,从而结束我的服务。示例:

10-04 03:27:41.673: INFO/ActivityManager(1269): Process com.androsz.electricsleep (pid 16223) has died.
10-04 03:27:41.681: INFO/WindowManager(1269): WIN DEATH: Window{45509f98 com.androsz.electricsleep/com.androsz.electricsleep.ui.SleepActivity paused=false}

我不想强迫用户在我的应用中将“SleepActivity”或其他一些活动作为前台。我不能让我的服务定期运行,因为它不断拦截 onSensorChanged。

有什么建议吗?源代码在这里:http://code.google.com/p/electricsleep/

【问题讨论】:

    标签: android service process alarmmanager


    【解决方案1】:

    保持较小的服务占用空间,这会降低 Android 关闭您的应用程序的可能性。您无法阻止它被杀死,因为如果可以,那么人们可以轻松创建持久性间谍软件

    【讨论】:

    • 或者你可以启动另一个服务并让他们互相监视以确保他们确实被杀死然后另一个重新启动它。丑陋但可能有效
    • 我确实需要优化我的服务消耗的内存(我只是将数据转储到文件中的 SQLite db),特别是因为它随着时间的推移而固有地增长,但是,我想知道为什么我的服务被杀死而其他服务继续运行。我知道服务中内置了优先级系统 - 我如何提高服务的优先级?
    • @Jon - 如果你的服务被杀死,它最终会重新启动。我知道您说过您不想这样做,但提高优先级的唯一方法是让操作系统相信该服务当前对用户处于活动状态(在前台 - 使用 startForeground(int, Notification))
    • 不是正确的是,保持它很小会降低它被杀死的可能性(暂时直到它重新启动)。此处使用的确切算法因版本而异,但通常确保后台服务偶尔会被终止/重新启动,无论它们使用的内存如何。
    • 我想要两个服务是onForground。我怎样才能做到这一点? stackoverflow.com/questions/35168209/…
    【解决方案2】:

    对于 Android 2.0 或更高版本,您可以使用 startForeground() method 在前台启动您的服务。

    documentation says the following:

    启动的服务可以使用startForeground(int, Notification) API 将服务置于前台状态,系统认为它是用户主动意识到的,因此在内存不足时不会被终止。 (理论上,在当前前台应用程序的极端内存压力下,服务仍然可能被杀死,但实际上这应该不是问题。)

    主要用于当终止服务会对用户造成干扰时,例如杀死音乐播放器服务将停止音乐播放。

    您需要提供Notification 来显示在“正在进行”部分的通知栏中的方法。

    【讨论】:

    • 谢谢。之前顺便听说过 startForeground ,但没想到 startForeground 会是 Service 中的一个方法(虽然现在想起来还是有道理的。)
    • 我有一个问题。如果我不想显示任何通知但仍想使用 startForeground() 方法怎么办。
    • @farhanahaque - 你不能。您应该只使用startForeground(),因为如果终止该服务会对用户造成干扰 - 例如。做一些诸如播放音乐之类的事情。在这种情况下,它应该是用户知道的,因此可以通过通知看到。 startForeground() 并不意味着可以替代适当地响应生命周期事件。
    • @DaveWebb 在发生崩溃或在极端内存压力下确实被杀死的罕见事件时,可以使用什么来重新启动此类服务?
    • @gonzobrains:在 onStart 命令中,您可以设置一个警报,以确保您的服务重新启动,以防它在两者之间被杀死。这是代码:PendingIntent localPendingIntent = PendingIntent.getService(this , 0, new Intent(this, UsbService.class), 0); AlarmManager localAlarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);日历 localCalendar = Calendar.getInstance(); localCalendar.setTimeInMillis(System.currentTimeMillis()); localCalendar.add(13, 50); localAlarmManager.set(0, localCalendar.getTimeInMillis(), localPendingIntent);
    【解决方案3】:

    当您使用 BIND_AUTO_CREATE 将 Service 绑定到 Activity 时,您的服务会在您的 Activity 被销毁且未绑定之后被终止。它不取决于您如何实现 Services unBind 方法,它仍然会被杀死。

    另一种方法是使用 Activity 中的 startService 方法启动服务。这样,即使您的 Activity 被销毁,您的服务也不会被销毁甚至暂停,但您必须在适当的时候使用 stopSelf/stopService 自行暂停/销毁它。

    【讨论】:

      【解决方案4】:

      http://developer.android.com/reference/android/content/Context.html#BIND_ABOVE_CLIENT

      public static final int BIND_ABOVE_CLIENT -- 在 API 级别 14

      中添加

      bindService(Intent, ServiceConnection, int) 的标志:表示绑定到此服务的客户端应用程序认为该服务比应用程序本身更重要。设置后,平台将尝试让内存不足杀手在终止应用程序之前终止它所绑定的服务,但不能保证一定是这种情况。

      同一组的其他标志是:BIND_ADJUST_WITH_ACTIVITY、BI​​ND_AUTO_CREATE、BIND_IMPORTANT、BIND_NOT_FOREGROUND、BIND_WAIVE_PRIORITY。

      请注意,BIND_AUTO_CREATE 的含义在 ICS 中已更改,并且 未指定的旧应用程序 BIND_AUTO_CREATE 将自动为它们设置标志 BIND_WAIVE_PRIORITY BIND_ADJUST_WITH_ACTIVITY

      【讨论】:

        【解决方案5】:

        作为Dave already pointed out,您可以以前台优先级运行Service。但是这种做法应该只在绝对必要的情况下使用,即如果服务被 Android 杀死会导致糟糕的用户体验。这就是“前台”的真正含义:您的应用以某种方式位于前台,如果它被杀死(例如,因为它播放歌曲或视频),用户会立即注意到它。

        在大多数情况下,为您的服务请求前台优先级是适得其反的!

        这是为什么呢?当 Android 决定杀死 Service 时,它这样做是因为它缺乏资源(通常是 RAM)。根据不同的优先级,Android 决定终止哪些正在运行的进程(包括服务)以释放资源。这是您希望发生的健康过程,以便用户获得流畅的体验。如果你在没有充分理由的情况下请求前台优先级,只是为了不让你的服务被杀掉,那很可能会导致糟糕的用户体验。或者您能否保证您的服务保持在最低资源消耗范围内并且没有内存泄漏?1

        Android 提供 sticky services 来标记服务,如果它们被杀死,应该在一些宽限期后重新启动。这种重启通常会在几秒钟内发生。

        您要为 Android 编写 XMPP 客户端的图像。您是否应该为包含您的 XMPP 连接的 Service 请求前台优先级?绝对不,绝对没有理由这样做。但是您想使用START_STICKY 作为服务的onStartCommand 方法的返回标志。让你的服务在资源压力时停止,恢复正常后重启。

        1:我很确定许多 Android 应用程序都有内存泄漏。这是休闲(桌面)程序员不太关心的事情。

        【讨论】:

        • START_STICKY 并非用于该用途,而是为了保证在进程终止后完成限时任务。如果由于某种原因服务没有“粘住”(例如无法重新连接到 XMPP 服务器),系统可能会决定它尝试了足够多的时间并简单地停止重新启动服务,更不用说这需要服务保持运行在连接建立时循环,这根本无法完成。也就是说,我找不到在进程终止时保持/恢复连接的解决方案(除了通过AlarmManager 安排定期重新连接尝试)。
        • START_STICKY 正是您想在此处使用的。 "...尝试了足够多的时间,然后停止重新启动服务,..." 不, Android 系统不知道 XMPP 连接(重新)尝试执行粘性 Android 服务。它只是看到服务正在运行并使其保持运行。这就是 Android API 在服务启动时提供的合约。 "...提到这将要求服务保持循环运行...",是的,这正是(大多数)Android 服务具有 Looper 的原因(通常从使用 Android 的用户中抽象出来)服务)。
        • 对不起,我从START_REDELIVER_INTENT 弄错了。尽管服务肯定有一个 Looper 用于处理传递给它的意图队列,但我们不希望在持久连接的情况下保持它运行,而是传递一个意图并在 Service.onStartCommand() 内保持一个 while 循环或IntentService.onHandleIntent() 方法,这是不可行的。
        • “在里面保持一个 while 循环”。是什么让您认为您需要这样的 while 循环?你不需要一个。
        • 我再次检查了文档,我认为您是对的。我误解了 Android Service 的概念。一直想知道为什么需要 stopSelf() 方法......结果表明,如果服务没有停止,来自空闲服务的 Loopers 对于系统来说相当便宜,我只是没想到它们会这样设计。感谢您的澄清!
        【解决方案6】:

        我遇到了类似的问题。一段时间后,在某些设备上,Android 会终止我的服务,甚至 startForeground() 也无济于事。我的客户不喜欢这个问题。我的解决方案是使用AlarmManager 类来确保服务在必要时运行。我使用AlarmManager 来创建一种看门狗定时器。它不时检查服务是否应该运行并重新启动它。 我还使用SharedPreferences 来保持服务是否应该运行的标志。

        创建/关闭我的看门狗计时器:

        void setServiceWatchdogTimer(boolean set, int timeout)
        {
            Intent intent;
            PendingIntent alarmIntent;
            intent = new Intent(); // forms and creates appropriate Intent and pass it to AlarmManager
            intent.setAction(ACTION_WATCHDOG_OF_SERVICE);
            intent.setClass(this, WatchDogServiceReceiver.class);
            alarmIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
            AlarmManager am=(AlarmManager)getSystemService(Context.ALARM_SERVICE);
            if(set)
                am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timeout, alarmIntent);
            else
                am.cancel(alarmIntent);
        }
        

        接收并处理来自看门狗定时器的意图:

        /** this class processes the intent and
         *  checks whether the service should be running
         */
        public static class WatchDogServiceReceiver extends BroadcastReceiver
        {
            @Override
            public void onReceive(Context context, Intent intent)
            {
        
                if(intent.getAction().equals(ACTION_WATCHDOG_OF_SERVICE))
                {
                    // check your flag and 
                    // restart your service if it's necessary
                    setServiceWatchdogTimer(true, 60000*5); // restart the watchdogtimer
                }
            }
        }
        

        确实,我使用WakefulBroadcastReceiver 而不是BroadcastReceiver。我为您提供了 BroadcastReceiver 的代码只是为了简化它。

        【讨论】:

          【解决方案7】:

          我正在开发一个应用程序,并面临通过应用程序终止来终止我的服务的问题。我在谷歌上进行了研究,发现我必须将其设为前台。以下是代码:

          public class UpdateLocationAndPrayerTimes extends Service {
          
           Context context;
          @Override
          public void onCreate() {
              super.onCreate();
              context = this;
          }
          
          @Override
          public int onStartCommand(Intent intent, int flags, int startId) {
          
              StartForground();
              return START_STICKY;
          }
          
          @Override
          public void onDestroy() {
          
          
              super.onDestroy();
          }
          
          @Nullable
          @Override
          public IBinder onBind(Intent intent) {
              return null;
          }
          
          
          private void StartForground() {
              LocationChangeDetector locationChangeDetector = new LocationChangeDetector(context);
              locationChangeDetector.getLatAndLong();
              Notification notification = new NotificationCompat.Builder(this)
                      .setOngoing(false)
                      .setSmallIcon(android.R.color.transparent)
          
                      //.setSmallIcon(R.drawable.picture)
                      .build();
              startForeground(101,  notification);
          
              }
          }
          

          希望能有帮助!!!!

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多