Foreground Service in Android

Foreground Service is a special component that enables Android applications to keep doing useful work even when the app is in the background. For example, whenever you listen to your favorite podcast on the go, keep track of your location while cycling, broadcast your gameplay on Twitch, etc., all these use cases require a Foreground Service.

In this article we’ll discuss why we need Foreground Service and I’ll show you how to implement Foreground Service in Android application.

Service vs Foreground Service

Since Foreground Service works in the background, the prefix “foreground” can be a bit confusing. So, let’s understand why it actually makes sense.

It starts with a regular Service class. According to the official documentation:

A Service is an application component that can perform long-running operations in the background.

Android documentation.

Services were around from the earliest days of Android and, initially, they could indeed perform long-running operations in the background. However, over time, Google imposed more and more restrictions on the background work in Android apps. The goal of these restrictions was to prolong users’ battery life, improve performance and preserve privacy (e.g. some apps abused background Services to track user’s location). Google succeeded in their effort, but, along the way, background work in Android had become so restricted, that a regular started Service became pretty much obsolete. Nowadays, even if you start a background Service in your app, it’ll stop getting CPU resources mere minutes after the user sends your app to the background (in some cases even faster).

However, there are still valid use cases for background work: playing media, tracking location, streaming, etc. Regular started Service, being so restricted, can’t accommodate these use cases. Enter Foreground Service.

Foreground Service is a Service and, just like a regular Service, Foreground Service executes in the background. However, when a process hosts a Foreground Service, Android OS treats that process as though it has a foreground component (e.g. foreground Activity), thus not applying background restrictions to it. This allows Foreground Service to keep working in situations when a regular Service would long be sleeping (or even the entire process would be killed).

All in all, the prefix “foreground” in the context of a Foreground Service refers to the internal mechanics of Android OS, which gives more resources to “foreground” processes as opposed to “background” processes. In essence, Foreground Service is just a special VIP Service.

Declaring Foreground Service in AndroidManifest

Just like a regular Service, Foreground Service must be declared in your AndroidManifest.xml file:

<service
    android:name=".ForegroundService"
    android:enabled="true"
    android:exported="false"
    android:foregroundServiceType="dataSync"
    />

Please note the foregroundServiceType attribute. It will become mandatory in Android 14.

Unlike regular Service, Foreground Service requires additional permissions:

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

The first permission, FOREGROUND_SERVICE, is granted to the app at installation time and should be self-explanatory.

The second permission, POST_NOTIFICATIONS, is a runtime permission that was added in API 33. As you’ll see shortly, when you implement a Foreground Service, the system forces you to create a new notification corresponding to that Service that will be shown to the user. The idea is to make sure that the user is aware of the fact that there is a Foreground Service running in the background. Well, to be precise, this had been the idea in the past, but with API 33 Google changed their mind. Starting with API 33, if you want the user to see the notification, you must explicitly get their approval by asking for this permission. If the user doesn’t grant you the permission, you’ll still be able to start your Foreground Service, but the notification won’t be shown.

Implementing Foreground Service

This is the source code of a Foreground Service. This one doesn’t do anything useful, but, for your convenience, I added comments explaining where you should plug in your own logic:

class ForegroundService: Service() {

    private lateinit var notificationManager: NotificationManager

    // onStartCommand can be called multiple times, so we keep track of "started" state manually
    private var isStarted = false

    override fun onCreate() {
        super.onCreate()
        // initialize dependencies here (e.g. perform dependency injection)
        notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    }

    override fun onDestroy() {
        super.onDestroy()
        isStarted = false
    }

    override fun onBind(intent: Intent?): IBinder? {
        throw UnsupportedOperationException() // bound Service is a different story
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        if (!isStarted) {
            makeForeground()
            // place here any logic that should run just once when the Service is started
            isStarted = true
        }

        // process the command here (e.g. retrieve extras from the Intent and act accordingly)
        val demoString = intent?.getStringExtra(EXTRA_DEMO) ?: ""

        return START_STICKY // makes sense for a Foreground Service, or even START_REDELIVER_INTENT
    }

    private fun makeForeground() {
        val intent = Intent(this, MainActivity::class.java)
        val pendingIntent = PendingIntent.getActivity(
            this, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
        )

        // before calling startForeground, we must create a notification and a corresponding
        // notification channel
        
        createServiceNotificationChannel()

        val notification: Notification = NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("Foreground Service")
            .setContentText("Foreground Service demonstration")
            .setSmallIcon(R.drawable.ic_your_app_logo)
            .setContentIntent(pendingIntent)
            .build()

        startForeground(ONGOING_NOTIFICATION_ID, notification)
    }

    private fun createServiceNotificationChannel() {
        if (Build.VERSION.SDK_INT < 26) {
            return // notification channels were added in API 26
        }

        val channel = NotificationChannel(
            CHANNEL_ID,
            "Foreground Service channel",
            NotificationManager.IMPORTANCE_DEFAULT
        )
        channel.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
        notificationManager.createNotificationChannel(channel)
    }

    companion object {
        private const val ONGOING_NOTIFICATION_ID = 101
        private const val CHANNEL_ID = "1001"

        private const val EXTRA_DEMO = "EXTRA_DEMO"

        fun startService(context: Context, demoString: String) {
            val intent = Intent(context, ForegroundService::class.java)
            intent.putExtra(EXTRA_DEMO, demoString)
            if (Build.VERSION.SDK_INT < 26) {
                context.startService(intent)
            } else {
                context.startForegroundService(intent)
            }
        }

        fun stopService(context: Context) {
            val intent = Intent(context, ForegroundService::class.java)
            context.stopService(intent)
        }

    }
}

Please note the API level checks that I added for developers who need to support older API versions. If your app’s minSdkVersion is 26+, you can remove all of them.

Starting and Stopping Foreground Service

You can start the above ForegroundService from other components by invoking this method:

ForegroundService.startService(context, "some string you want to pass into the service")

Most non-trivial Foreground Services will accept additional configuration parameters from their callers. To accommodate this requirement, just add these parameters to the signature of the above method and then push them into the intent (analogously to demoString).

To stop ForegroundService, invoke this method:

ForegroundService.stopService(context)

If you’ll need to stop the Service from within, you can also use stopSelf() method.

Summary

That’s it, now you know how to implement a full fledged Foreground Service in your Android application. Hope this will spare you some time.

As of this writing, the latest released version of Android OS is Android 13 (API 33). If there will be any additional changes in behavior going forward, please let me know in the comments and I’ll update this post.

Check out my advanced

Android Development Courses

8 comments on "Foreground Service in Android"

  1. “Starting with API 33, if you want the user to see the notification, you must explicitly get their approval by asking for this permission.”

    If the user does not approve notifications for our app, will Foreground Service still have all its capabilities?

    Reply
    • If the user doesn’t grant notification permission, Foreground Service will still be started, but without a notification.

      Reply
  2. 1. About foregroundServiceType, what if I don’t have a type that fits any of the available values?
    Only on Android 14 it seems there is “specialUse”.

    2. What’s the purpose of foregroundServiceType? Yet another restriction on foreground service?

    3. Isn’t it true that on some Android versions, we had to also use startService in case the app is in the foreground and startForegroundService in case it’s in the background? On which Android version has it changed?

    4. What do you think about this article of this topic:
    https://web.archive.org/web/20230327202046/https://www.hellsoft.se/your-guide-to-foreground-services-on-andorid/
    ?
    Are there any mistakes?

    Reply
    • Hi,
      1. This is a question to ask the authors of this feature. The official guidleines for Android 14 say that if you can’t find a proper type, then you should consider WorkManager.
      2. As far as I understand, starting with Android 14, the system will check that permissions that a foreground service uses match its type. So, for example, foreground service of type “camera” must request camera permission when it’s started.
      3. I’m not familiar with this requirement, frankly. If you have any links to share, I’ll appreciate that.
      4. Sorry, I don’t have time to verify other materials.

      Reply
      • My comment wasn’t published for some reason.
        I will try to write again:

        1. Not all use cases fit WorkManager at all. For example, restrictions were added for BroadcastReciever that can be registered only at runtime now, and can’t be registered in the manifest anymore. For this, you have to have a foreground service.
        2. What happens if it doesn’t?
        3. Sadly I don’t have it. I remember that Google wrote about it on the issue tracker somewhere.
        4. It’s an article like here.

        Reply
        • Hey,
          All comments go through manual approval, and sometimes I’m busy, so it takes a while. Sorry you had to type your comment twice.

          Reply

Leave a Comment