4

I am having a problem where my service is being killed even though I am holding a wake lock and I have called startForeground. When this occurs the tablet (ASUS Transformer TF101), stops the service without calling onDestroy. There are no other apps visible, and log cat shows nothing out of the ordinary (no 'out of memory' message etc). Immediately after being killed, the service restarts.

The app I am developing is a chat client and needs a constant connection, it is also plugin based, so my app is developed as such: Client - HostService - Multiple child 'Services'.

The host service is sticky holds the wake lock and calls startForeground (and displays a notification as such), the child services are not sticky, do not hold wake locks and are background services.

If the client itself is open the issue does not occur, but the model I am going for is that the user can use the device and stay connected (receiving messages etc) without having the client itself open at all times.

Can anybody offer any explanation as to why the service is being killed in this way, and if so prevent it from happening? As the chat clients show when a user logs on and off, and the service dying kills all open connections, this makes the chat client 'bounce'. At present it seems to happen somewhere between every 15 and 45 minutes.

Also, if anybody is aware of a way to keep a socket connection open continuously without holding a wake lock for the entire connection duration, I would love to hear it!

The trimmed test case version of the host service source is below.

public class HostService extends Service
{
    PowerManager m_powerManager = null;
    PowerManager.WakeLock m_wakeLock = null;

    @Override
    public IBinder onBind( Intent intent )
    {
        return m_serviceImplementation;
    }

    @Override
    public void onCreate()
    {
        super.onCreate();       
    }

    @Override
    public void onDestroy()
    {
        if( m_wakeLock != null )
        {
            m_wakeLock.release();
            m_wakeLock = null;
        }

        stopForeground( true );

        super.onDestroy();
    }

    @Override
    public int onStartCommand( Intent intent, int flags, int startId )
    {
        // Display a notification about us starting. We put an icon in the
        // status bar.
        Notification notification = createNotification();

        startForeground( R.string.service_running, notification );

        if( m_powerManager == null )
        {
            m_powerManager = (PowerManager)getSystemService(Context.POWER_SERVICE);
        }

        if( m_wakeLock == null )
        {
            m_wakeLock = m_powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Keep background services running");
            m_wakeLock.acquire();
        }

        // We want this service to continue running until it is explicitly
        // stopped, so return sticky.
        return START_STICKY;
    }

    /**
     * Create a notification to show the service is running
     */
    private Notification createNotification()
    {
        CharSequence text = getText( R.string.service_running );
        CharSequence title = getText( R.string.app_name );

        // The PendingIntent to launch our activity if the user selects this
        // notification
        PendingIntent contentIntent = PendingIntent.getActivity( this, 0, new Intent(this, MainChat.class) , 0 );

        Notification notification = new Notification( android.R.drawable.sym_action_chat, title, System.currentTimeMillis() );  
        notification.setLatestEventInfo( this, title, text, contentIntent );

        return notification;
    }

    private final IMessageInterface.Stub m_serviceImplementation = new IMessageInterface.Stub()
    {
        ...
    };
}

Android Manifest (relevant bits):

<uses-sdk android:minSdkVersion="11" android:targetSdkVersion="11" />

<service android:name="com.mydomain.chatClient.server.HostService" android:exported="true" android:enabled="true" android:process=":remote"/>

<uses-permission android:name="android.permission.WAKE_LOCK" />
icStatic
  • 257
  • 4
  • 13

1 Answers1

10

I am having a problem where my service is being killed even though I am holding a wake lock and I have called startForeground.

startForeground() reduces the likelihood of a service being killed, but it does not prevent it.

The app I am developing is a chat client and needs a constant connection, it is also plugin based, so my app is developed as such: Client - HostService - Multiple child 'Services'.

I recommend getting rid of one of those layers. Even if the OS doesn't shut you down, many users will (e.g., task killer, Running Services in Settings), considering you to be running too many services.

If the client itself is open the issue does not occur, but the model I am going for is that the user can use the device and stay connected (receiving messages etc) without having the client itself open at all times.

I recommend making that optional. You may think it's sexy. Some of your users will attack you for wasting their battery.

Can anybody offer any explanation as to why the service is being killed in this way, and if so prevent it from happening?

I'd start by getting rid of android:process=":remote". You don't need it. You don't want it. You may be hurting yourself by having it, as it may accelerate Android's interest in getting rid of your service. You absolutely are hurting users by having it, because you are wasting RAM for no good reason.

Then, I'd get rid of the plugins, if you implemented those as separate applications. In that case, each one of those will be running in its own process, wasting yet more RAM. Besides, your current implementation would be flawed, as you would be stuck having your service be named com.mydomain.chatClient.server.HostService until the end of time, since you didn't use an <intent-filter> to separate the concerns of "what the service is named internally" and "what the service is called by other separately-installed applications that wish to use it". And if you didn't implement the plugins as separate applications, then I fail to see the value in having them be in separate services, rather than folding them all into the one service.

Also, if anybody is aware of a way to keep a socket connection open continuously without holding a wake lock for the entire connection duration, I would love to hear it!

If the socket is on wireless data, instead of WiFi, you do not need a WakeLock all the time. The socket will remain open, and incoming packets on that socket will wake up your code. At that point, you'd want to grab a WakeLock long enough for you to do whatever you're doing with the data when it arrives, then release the WakeLock.

If you are on WiFi, though, this trick doesn't work, so a WakeLock (and probably a WifiLock) will be required.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Gah, it saved the comment on enter! The reason behind having the extra layers is to allow third-parties to develop services that sit inside the client. For example, I do not have the resources to create functionality for esoteric (or proprietary) IM services, but somebody else may see the value in adding it at a later date. I will publish the functionality for 'common' IM services as and when I can, with a public API for others to do the same. The 'always on' background functionality is indeed optional but that would be the mode I would expect many users to use (including myself). – icStatic Jun 11 '11 at 20:02
  • I have removed the remote flag from the service and will report back if this helps in case anyone is having a similar problem. I should have also mentioned that there was a lot more to the manifest file, including some intent filters than listed - I cut this as I didn't think it was relevant to the test case. – icStatic Jun 11 '11 at 20:07
  • @icStatic: "I cut this as I didn't think it was relevant to the test case" -- OK. In that case, though, you can also get rid of `android:exported`. A `Service` is automatically exported if it has an ``. The fact that you had `android:exported` and no `` led me to believe that you had no ``. – CommonsWare Jun 11 '11 at 20:09
  • No, unfortunately removing the remote flag did not help at all. I am still at a loss as to why the process is being killed at all. I could understand if the device was low on memory, but at the time when it was killed I had a process monitor running, and it showed 150MB free. I have however noticed that my process monitor (SystemPanel Lite) states "Background, 14.0M" - and some other apps, which also have notification icons display "Foreground". This may indicate that it is in fact still running in the background and the call to startForeground failed (but still displayed the notification). – icStatic Jun 11 '11 at 20:21
  • @icStatic: "This may indicate that it is in fact still running in the background and the call to startForeground failed (but still displayed the notification)" -- that's conceivable. Stuff like `startForeground()` get into fairly nasty system innards, so I couldn't tell you under which circumstances this might occur. – CommonsWare Jun 11 '11 at 20:31
  • 1
    @icStatic: Also note that SystemPanel Lite isn't exactly reliable. For example, on a whim, I just installed it and ran one of my book samples that uses `startForeground()`. While the Settings app shows my service in Running Services, SystemPanel Lite doesn't show it at all, multiple refreshes later. – CommonsWare Jun 11 '11 at 20:37
  • @CommonsWare Aah, thanks for the heads up - I'll try one of the other apps and see what that says. I have been wondering why the child plugins do not show up at all in the process list - this may be the reason. – icStatic Jun 11 '11 at 20:55
  • You don't need to hold a wakelock in order to keep a socket open. Here is an example I made https://github.com/schwiz/android-websocket-example – Nathan Schwermann Aug 08 '13 at 18:47
  • @schwiz: You use a total of three `WakeLocks` in your sample. In fact, you do so with no `try`/`catch` blocks to ensure that you release the `WakeLock` in case of a `RuntimeException`. – CommonsWare Aug 08 '13 at 19:08
  • @CommonsWare but the wakelocks aren't held unless data is coming through, the connection remains open without a wakelock. As soon as the first bit of info comes over the wire I hold a wakelock to ensure I get the entire message. I've been able to keep a connection alive for over 12 hours with zero battery impact using this technique. – Nathan Schwermann Aug 09 '13 at 17:12
  • @schwiz: Which means that you were using mobile data, not WiFi. Otherwise, the WiFi radio alone would represent a non-zero battery impact. If you had zero battery impact, you were not using WiFi. – CommonsWare Aug 09 '13 at 22:23
  • @CommonsWare I did the majority of my testing on LTE/3G but the original question was about holding a socket open, not specifically about WiFi. – Nathan Schwermann Aug 12 '13 at 14:38
  • @schwiz: If you take the time to read my answer, you will see that I only said that you would need a `WakeLock` on WiFi. Hence, your downvote of my answer is because you think that your tests demonstrate that a `WakeLock` is not needed on WiFi. You are welcome to actually prove that this is indeed the case. – CommonsWare Aug 12 '13 at 17:35
  • @CommonsWare ahh sorry, the system won't let me un-downvote you unless you change your answer. – Nathan Schwermann Aug 14 '13 at 20:55
  • @schwiz: I'm not worried about the downvote itself. I am worried about being wrong. I'm wrong a lot, but I don't think I'm wrong in this case, which is why was pursuing more information. The downvote just made me cranky. :-[ I apologize for my 'tude. – CommonsWare Aug 14 '13 at 21:00
  • @CommonsWare no problem, my example will reconnect itself when wifi drops out and the cell connection kicks back in, on top of that the cell connection is always being briefly disconnected as you switch towers. So when the device goes to sleep and wifi turns off I considered that disconnection to be trivial knowing the cell connection would soon reconnect. This was fine for my purposes, but I suppose it could be very wrong in other circumstances. – Nathan Schwermann Aug 15 '13 at 23:36