Search code examples
androidandroid-service

Cannot keep android service alive after app is closed


I am trying to spawn a service that stays alive all the time, even if the user closes the application. According to these threads

Keep location service alive when the app is closed

Android Service Stops When App Is Closed

Android: keep Service running when app is killed

this can be accomplished with IntentServices or Service.START_STICKY

Yet, I tried both types of services without success. In other words, my services get killed when the app is closed by the user. Can someone point out if this is can be done and how? Here is what I have tried without success:

With IntentService:

public class MyIntentService extends IntentService {
    private final int mPollingTimeMS = 500;
    private int mInitializationPollingCount = 0;
    private Thread mPollThread;
    public MyIntentService() {
        super("MyIntentService");
    }
    @Override
    protected void onHandleIntent(Intent intent) {
        mPollThread = new Thread() {
            public void run() {
                while (true) {
                    try {
                        Log.e(Constants.Engine.LOGGER_TAG_DEV,
                                "SDK Service Running: " +
                                        mInitializationPollingCount * mPollingTimeMS +
                                        "ms have elapsed");
                        mInitializationPollingCount++;
                        sleep(mPollingTimeMS);

                    } catch (Exception e) {
                        StackTraceElement trace = new Exception().getStackTrace()[0];
                        Logger.e(Constants.Engine.LOGGER_TAG_APP, "[Exception:" + e.toString() + "]" +
                                trace.getClassName() + "->" + trace.getMethodName() + ":" + trace.getLineNumber());
                    }
                }
            }
        };
        mPollThread.start();
    }
}

and with Services:

public class MyService extends Service {
    public MyService() {
    }
    private final int mPollingTimeMS = 500;
    private int mInitializationPollingCount = 0;
    private Thread mPollThread;
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        mPollThread = new Thread() {
            public void run() {
                while (true) {
                    try {
                        Log.e(Constants.Engine.LOGGER_TAG_DEV,
                                "SDK Service Running: " +
                                        mInitializationPollingCount * mPollingTimeMS +
                                        "ms have elapsed");
                        mInitializationPollingCount++;
                        sleep(mPollingTimeMS);

                    } catch (Exception e) {
                        StackTraceElement trace = new Exception().getStackTrace()[0];
                        Logger.e(Constants.Engine.LOGGER_TAG_APP, "[Exception:" + e.toString() + "]" +
                                trace.getClassName() + "->" + trace.getMethodName() + ":" + trace.getLineNumber());
                    }
                }
            }
        };
        mPollThread.start();
        return Service.START_STICKY;
    }
    @Override
    public IBinder onBind(Intent intent) {
        // I tried to return null here, but this
        // service gets killed no matter what.
        return null;
    }
}

and here is the manifest:

    <service
        android:name=".mycompany.MyService"
        android:enabled="true"
        android:exported="true"
        android:process=":process1">
    </service>
    <service
        android:name=".mycompany.MyIntentService"
        android:process=":process2"
        android:exported="false">
    </service>

I shall added that I am closing the test app not with a close button, but using the Android OS app manager. See picture below

enter image description here

Lastly, the driver activity (not much there)

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Intent intent1 = new Intent(getBaseContext(), MyService.class);
        startService(intent1);
        Intent intent2 = new Intent(getBaseContext(), MyIntentService.class);
        startService(intent2);

    }
}

I also try to add a notification and make it a foreground service but still the same thing. The moment I close the app, everything gets killed. This is what I added:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    showNotification();
...etc..

private void showNotification() {
    Intent notificationIntent = new Intent(this, MainActivity.class);
    notificationIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
            notificationIntent, 0);
    int iconId = R.mipmap.ic_launcher;
    int uniqueCode = new Random().nextInt(Integer.MAX_VALUE);
    Notification notification = new NotificationCompat.Builder(this)
            .setSmallIcon(iconId)
            .setContentText("Context Text")
            .setContentIntent(pendingIntent).build();
    startForeground(uniqueCode, notification);
}

Solution

  • Here is an example of foreground service that I use and that works, it remains active when the app is closed. Of course, it also must be started, and for that task the app must be running at a first glance, or a receiver of a boot event must be set, but this is another story.

    public class MyService extends Service {
    static final int NOTIFICATION_ID = 543;
    
    public static boolean isServiceRunning = false;
    
    @Override
    public void onCreate() {
        super.onCreate();
        startServiceWithNotification();
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent != null && intent.getAction().equals(C.ACTION_START_SERVICE)) {
            startServiceWithNotification();
        }
        else stopMyService();
        return START_STICKY;
    }
    
    // In case the service is deleted or crashes some how
    @Override
    public void onDestroy() {
        isServiceRunning = false;
        super.onDestroy();
    }
    
    @Override
    public IBinder onBind(Intent intent) {
        // Used only in case of bound services.
        return null;
    }
    
    
    void startServiceWithNotification() {
        if (isServiceRunning) return;
        isServiceRunning = true;
    
        Intent notificationIntent = new Intent(getApplicationContext(), MyActivity.class);
        notificationIntent.setAction(C.ACTION_MAIN);  // A string containing the action name
        notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        PendingIntent contentPendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
    
        Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.my_icon);
    
        Notification notification = new NotificationCompat.Builder(this)
                .setContentTitle(getResources().getString(R.string.app_name))
                .setTicker(getResources().getString(R.string.app_name))
                .setContentText(getResources().getString(R.string.my_string))
                .setSmallIcon(R.drawable.my_icon)
                .setLargeIcon(Bitmap.createScaledBitmap(icon, 128, 128, false))
                .setContentIntent(contentPendingIntent)
                .setOngoing(true)
    //                .setDeleteIntent(contentPendingIntent)  // if needed
                .build();
        notification.flags = notification.flags | Notification.FLAG_NO_CLEAR;     // NO_CLEAR makes the notification stay when the user performs a "delete all" command
        startForeground(NOTIFICATION_ID, notification);
    }
    
    void stopMyService() {
        stopForeground(true);
        stopSelf();
        isServiceRunning = false;
    }
    }
    

    Then I run it with

        Intent startIntent = new Intent(getApplicationContext(), MyService.class);
        startIntent.setAction(C.ACTION_START_SERVICE);
        startService(startIntent);
    

    Please note the two constants used as Actions, these are Strings that must start with the package name.