Android:保持后台服务活着(防止进程死亡)

Jon*_*lis 51 service android process alarmmanager

我有一个定义为的服务:

public class SleepAccelerometerService extends Service implements SensorEventListener
Run Code Online (Sandbox Code Playgroud)

从本质上讲,我正在制作一个应用程序,可以在用户使用他或她的手机/设备睡在床上时,出于各种原因监控加速度计活动.这是一项长期服务,不得在夜间杀死.根据夜间有多少后台应用程序和定期进程,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}
Run Code Online (Sandbox Code Playgroud)

我不想强迫用户将"SleepActivity"或我的应用中的其他活动作为前景.我不能定期运行我的服务,因为它会不断拦截onSensorChanged.

有小费吗?源代码在这里:http://code.google.com/p/electricsleep/

Dav*_*ebb 72

对于Android 2.0或更高版本,您可以使用该startForeground()方法在前台启动服务.

文件说以下:

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

主要用于当杀死服务对用户造成破坏时,例如,杀死音乐播放器服务将停止音乐播放.

您需要提供一个Notification方法,该方法显示在Ongoing部分的Notifications Bar中.

  • 我有一个问题.如果我不想显示任何通知但仍想使用startForeground()方法,该怎么办? (4认同)
  • @farhanahaque - 你不能.你应该只使用`startForeground()`来杀死服务会对用户造成破坏 - 例如做一些像播放音乐的事情.在这种情况下,它应该是用户意识到的东西,因此通过通知可见.`startForeground()`并不意味着要适当地响应生命周期事件. (4认同)
  • 谢谢.之前我曾经听说过startForeground,但我没想到startForeground会成为Service中的一个方法(虽然现在考虑它会有意义.) (2认同)

小智 16

当您使用BIND_AUTO_CREATE将服务绑定到Activity时,您的服务将在您的活动被销毁和解除绑定后被杀死.它不依赖于你如何实现你的服务unBind方法它仍将被杀死.

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


Flo*_*low 11

正如Dave已经指出的那样,你可以运行Service前景优先级.但是这种做法只应在绝对必要时使用,即如果服务被Android杀死会导致糟糕的用户体验.这就是"前景"的真正含义:你的应用程序在某种程度上处于前台,如果它被杀死,用户会立即注意到它(例如因为它播放了一首歌或一段视频).

在大多数情况下,请求服务的前台优先级是反效果的!

这是为什么?当Android决定杀死a时Service,它会这样做,因为它缺少资源(通常是RAM).基于不同的优先级类,Android决定终止哪些正在运行的进程以及此包含的服务以释放资源.这是一个您希望发生的健康过程,以便用户获得流畅的体验.如果您没有充分理由请求前台优先级,只是为了防止您的服务被杀,它很可能会导致糟糕的用户体验.或者,您能保证您的服务保持在最小的资源消耗范围内并且没有内存泄漏吗?1

Android提供了粘性服务来标记服务,如果它们被杀死,应该在一段宽限期后重新启动.这种重启通常在几秒钟内完成.

想要为Android编写XMPP客户端的图像.您是否应该为Service包含XMPP连接的前台优先级请求?绝对没有,绝对没有理由这样做.但是您希望将其START_STICKY用作服务onStartCommand方法的返回标志.因此,当资源压力时您的服务会停止,并在情况恢复正常后重新启动.

1:我很确定很多Android应用都有内存泄漏.这是休闲(桌面)程序员不关心的东西.

  • **`START_STICKY`正是你想在这里使用的.***"......尝试了足够的时间,只是停止重启服务,......"*不,Android系统不知道XMPP连接(重新)尝试粘Android服务执行.它只是看到服务运行**并保持运行**.这是Android API在服务启动时提供的合约.*"......提到这将要求服务保持循环运行......"*,是的,这就是为什么(大多数)Android服务有一个Looper(通常是使用Android服务从用户那里抽象出来的). (4认同)
  • 我再次检查了文档,我认为你是对的.我误解了Android服务的概念.一直想知道为什么总是需要一个`stopSelf()`方法......事实证明,如果服务没有停止,来自空闲服务的Loopers对系统来说相当便宜,我只是没想到它们被设计为办法.谢谢你的澄清! (2认同)

iva*_*van 5

我有类似的问题.在一些设备上一段时间后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);
}
Run Code Online (Sandbox Code Playgroud)

从看门狗定时器接收和处理意图:

/** 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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

实际上我使用WakefulBroadcastReceiver 而不是BroadcastReceiver.我给了你BroadcastReceiver的代码只是为了简化它.