Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Service being killed while holding wake lock and after calling startForeground

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" />
like image 962
icStatic Avatar asked Dec 27 '22 19:12

icStatic


1 Answers

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.

like image 128
CommonsWare Avatar answered Feb 07 '23 14:02

CommonsWare