App keeps running when foreground service is stopped last

android keep service running after app killed
android foreground service stops
android background service is restarting when application is killed
how to stop foreground service in android oreo
foreground service lifecycle
stop service when app is killed android
foreground service is getting killed
"stopping service due to app idle"

I came across a behaviour in Android's process management in conjunction with foreground services, that really confuses me.

What is reasonable for me
  1. When you swipe your app from 'Recent apps', the OS should finish the app process in the relatively near future.
  2. When you swipe your app from 'Recent apps' while running a foreground service, the app stays alive.
  3. When you stop the foreground service before swiping your app from 'Recent apps' you get the same as for 1).
What confuses me

When you stop the foreground service while having no activities in foreground (app does NOT appear in 'Recent apps'), I would expect the app being killed now.

However, this is not happening, the app process is still alive.

Example

I have created a minimal example that shows this behaviour.

The ForegroundService:

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.IBinder
import androidx.core.app.NotificationCompat
import timber.log.Timber

class MyService : Service() {

    override fun onBind(intent: Intent?): IBinder? = null

    override fun onCreate() {
        super.onCreate()
        Timber.d("onCreate")
    }

    override fun onDestroy() {
        super.onDestroy()
        Timber.d("onDestroy")

        // just to make sure the service really stops
        stopForeground(true)
        stopSelf()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Timber.d("onStartCommand")
        startForeground(ID, serviceNotification())
        return START_NOT_STICKY
    }

    private fun serviceNotification(): Notification {
        createChannel()

        val stopServiceIntent = PendingIntent.getBroadcast(
            this,
            0,
            Intent(this, StopServiceReceiver::class.java),
            PendingIntent.FLAG_UPDATE_CURRENT
        )
        return NotificationCompat.Builder(this, CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            .setContentTitle("This is my service")
            .setContentText("It runs as a foreground service.")
            .addAction(0, "Stop", stopServiceIntent)
            .build()
    }

    private fun createChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val notificationManager = getSystemService(NotificationManager::class.java)
            notificationManager.createNotificationChannel(
                NotificationChannel(
                    CHANNEL_ID,
                    "Test channel",
                    NotificationManager.IMPORTANCE_DEFAULT
                )
            )
        }
    }

    companion object {
        private const val ID = 532207
        private const val CHANNEL_ID = "test_channel"

        fun newIntent(context: Context) = Intent(context, MyService::class.java)
    }
}

The BroadcastReceiver to stop the service:

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent

class StopServiceReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {

        val serviceIntent = MyService.newIntent(context)

        context.stopService(serviceIntent)
    }
}

The Activity:

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        startService(MyService.newIntent(this))
    }
}

The manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.christophlutz.processlifecycletest">

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name=".MyService"/>
        <receiver android:name=".StopServiceReceiver" />
    </application>

</manifest>

Try it out the following ways:

  1. Start app, stop foreground service, remove app from 'Recent apps'
  2. Start app, remove app from 'Recent apps', stop foreground service

You can see in Android Studio's LogCat that the app process is marked [DEAD] for case 1 but not for case 2.

Since it is pretty easy to reproduce it might be an intended behaviour, but I did not find any real mention of this in the docs.

Does anybody know what is going on here?

Android system is known for its self-awareness in terms of memory, processor power and applications processes lifetime - it decides by itself whether to kill a process of not(the same with activities and services)

Here is the official documentation regarding this matter.

Look at what it says regarding foreground

There will only ever be a few such processes in the system, and these will only be killed as a last resort if memory is so low that not even these processes can continue to run. Generally, at this point, the device has reached a memory paging state, so this action is required in order to keep the user interface responsive.

and visible processes(Foreground Service is a visible process)

The number of these processes running in the system is less bounded than foreground processes, but still relatively controlled. These processes are considered extremely important and will not be killed unless doing so is required to keep all foreground processes running.

It means that the Android OS will have your app process running as long as it will have memory enough to support all foreground processes. Even if you stop it - the system may just move it to cached processes and handle it in queue-like manner. Eventually it will be killed either way - but usually it is not for you to decide. Frankly you should not care at all what is happening with your app process after(and as long as) all the Android lifecycle methods are called. Android knows better.

Of course you ca kill the process with android.os.Process.killProcess(android.os.Process.myPid()); but it is not recommended since it disrupts proper Android elements lifecycle and proper callbacks may not be called, thus your app may misbehave in some cases.

Hope it helps.

How to stop Android service when app is closed, How would you make a service persist even after application is killed? Android app stop after running in background by using foreground service? Discussion in ' Android Development ' started by Ranjeet Dahiwade , Mar 9, 2020 . Ranjeet Dahiwade Lurker

On Android it is the operating system who decides which applications are killed.

There is an order of priority: Applications that have a foreground service are rarely killed, instead other apps and services are killed after a period of inactivity and that is what happens with your app.

When you finish the foreground service, this increases the possibilities of the system killing your app but in no case does it mean that it will be killed immediately.

Building an Android service that never stops running, should be called with onTaskRemoved() when the task is removed. Bring your app to the foreground. Start a foreground service in your app by calling startForegroundService (). When such a foreground service is active, it appears as an ongoing notification in the notification area. Use elements of the Geofencing API, such as the GeofencingClient, which are optimized for minimizing power use.

The Recents Screen [...] is a system-level UI that lists recently accessed activities and tasks.

You could have several activities or task from a single app in the list. It's not a Recent Apps list.

Therefore there's no direct correlation between the elements of the Recents Screen and the app process.

Anyhow, if you close the last activity of your app and don't have anything else running within the process (like a foreground service), the process just gets cleaned up.

So when you stop a running foreground (by stopService() or stopSelf() and unbinding), the system will also cleanup the process it was running in.

So it's indeed an intended behavior.

Pitfalls of a foreground Service lifecycle, How can I make a service run continuously on Android? When the service does reach the point where it is to be shut down by the system in this situation, then the service is stopped as if the stopSelf () method has been called. To avoid running into

This depends on what the foreground service actually does. If it uses threads, network connections, file I/O etc.. that consumes memory actively, even if you try to stop the service, it will not be destroyed, hence the process will stay alive. This also includes within any interface callbacks that stay alive while you're trying to stop the service. Especially threads that still run (even interrupted) and bound services block the lifecycle that is stopping the service properly.

Make sure that you have no memory leaks in your service and close every connection (database, network etc...), remove all callbacks from all interfaces before stopping your service. You can make sure that the service is going to be destroyed if onDestroy() is called.

For different processes: I believe the reason that the process stays alive is that, the system sees in a way where the service may start again for some time, so it keeps the process alive for a short time. At least, that is what I have observed, because even after onDestroy() was called, the process stayed alive, I was able to see it from the debugger.

If you want to ensure (even though this way is not recommended) that the process gets killed for sure after everything is destroyed, you can always call this to terminate the process itself:

android.os.Process.killProcess(android.os.Process.myPid());

Processes and Application Lifecycle, Foreground Services need a notification to be shown so the user is aware that the app is still running. That makes sense if you think about it. Apps normally run in the background to update their live tiles, download new data, and receive notifications. If you want an app to continue performing these functions, you should allow it to continue running in the background. If you don’t care, feel free to prevent the app from running in the background.

Restrictions on starting activities from the background, As part of Google Play's target API level requirement existing apps need to target at the system will automatically stop the service and declare the app ANR. of onCreate() , to ensure some long-running operation won't delay it. The crash still occurs even if startForeground() is actually invoked, like in  In the event viewer, check the system logs and check for events by name Service Control manager (event ID 7035,7036 mostly). That will give you the ID what happened to which service. This event will only be generating if any service's status is changing, like from start to stop or vice versa.

Using foreground services for executing long-running processes in , In most cases, every Android application runs in its own Linux There will only ever be a few such processes in the system, and these will only be killed as a last resort It has a Service that is running as a foreground service, through unless doing so is required to keep all foreground processes running. To start a foreground service, the app must dispatch an Intent that tells Android to start the service. Then the service must register itself as a foreground service with Android. Apps that are running on Android 8.0 (or higher) should use the Context.StartForegroundService method to start the service, while apps that are running on devices

Started services in Android, These restrictions help minimize interruptions for the user and keep the user more in Note: For the purposes of starting activities, an app running a foreground service is still considered to be "in the background". When the device's screen is off, your full-screen intent is launched immediately. Last updated 2020-06-22. Part 5: Fix your App has unfortunately stopped by factory reset. Factory Reset must be used only when nothing else works. Please remember to take a back-up of all your data and contents on the cloud or an external memory device, such as a pen drive before adopting this method because the one you perform a factory reset on your device, all media, contents, data and other files are wiped out

Comments
  • You can reproduce the behavior with the example from the question&mdash;no connections, threads, or anything else keeps running. onDestroy (Service) gets called, yet the process stays alive long after everything is gone. Even if the OS was keeping the process alive in case the service gets restarted, I don't see why it doesn't do the same thing when you stop the service first, then remove the app from recents. The displayed behavior seems rather unintuitive, especially given recent changes to background execution limits, so it would be nice to know how to ensure that the process gets stopped
  • @DavidMedenjak Try using getApplicationContext().startService(MyService.newIntent(this)); instead of what you are using and tell me the results please. I believe the activity stays alive because the activity context is used instead of application context. Also if you're testing on Oreo or above, try using getApplicationContext().startforegroundService(MyService.newIntent(this));