Building an Android service that never stops running
Tags: android, kotlin, services
Published: 2019-06-30 02:59:05

---
title: Building an Android service that never stops running
published: true
description: Use Android foreground services to build a service that never stops running
tags: android, kotlin, services
canonical_url: https://robertohuertas.com/2019/06/29/android_foreground_services/
cover_image: https://thepracticaldev.s3.amazonaws.com/i/hppxh2gir7cp7fdij0xi.jpg
---

I've been struggling these days trying to find a way to run an endless service in [Android](https://www.android.com/). This is just a guide for all of you who pursue the same goal. Hope it helps! :grin:

## The problem

Due to Android battery optimizations introduced in Android 8.0 (API level 26), background services have now some important [limitations](https://developer.android.com/about/versions/oreo/background.html#services). Essentially, they are killed once the app is in background for a while making them worthless for our purpose of running an always-running service.

According to Android recommendations, we should be using [JobScheduler](https://developer.android.com/reference/android/app/job/JobScheduler.html), which seems to work pretty well and will handle [wakelocks](https://developer.android.com/training/scheduling/wakelock#cpu) for us, keeping the phone awake while the jobs are running.

Unfortunately, this won't work either. [JobScheduler](https://developer.android.com/reference/android/app/job/JobScheduler.html) will run jobs at Android's discretion and, on top of that, once the phone enters in [Doze Mode](https://developer.android.com/training/monitoring-device-state/doze-standby), the frequency of these jobs being run will constantly decrease. And even worst, if you ever want to access the network -say you need to send data to your server- you won't be able to. Check out the [list of restrictions](https://developer.android.com/training/monitoring-device-state/doze-standby#restrictions) Doze Mode imposes.

[JobScheduler](https://developer.android.com/reference/android/app/job/JobScheduler.html) works well if you don't mind about not having access to the network and you don't care about not controlling the periodicity either. In our case, we want our service to run at a very specific frequency and never be stopped, so we'll need something else.

## It's all about Foreground Services

If you've been looking over the internet for a solution to this problem it's very likely that you've eventually arrived to this [page from the Android's documentation](https://developer.android.com/guide/components/services).

There, we are introduced to the different types of services that Android provides. Take a look at the `Foreground Service` description:

  > A foreground service performs some operation that is noticeable to the user. For example, an audio app would use a foreground service to play an audio track. Foreground services must display a Notification. Foreground services continue running even when the user isn't interacting with the app.

It seems to be precisely what we're looking for... an it is, indeed! :wink:

## Show me the code

Creating a `foreground service` is really a straight-forward process so I will visit and explain all the steps needed to build a foreground service that never stops.

As usual, I've created a [repository with all the code](https://github.com/robertohuertasm/endless-service) in case you want to take a look at it and skip the rest of the post.

## Adding some dependencies

I'm using [Kotlin](https://kotlinlang.org/) for this example, so we will be leveraging [coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html) and the [Fuel](https://github.com/kittinunf/fuel) library for HTTP requests.

In order to add these dependencies we must add them to our `build.gradle` file:

```gradle
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    implementation 'com.jaredrummler:android-device-names:1.1.8'

    implementation 'com.github.kittinunf.fuel:fuel:2.1.0'
    implementation 'com.github.kittinunf.fuel:fuel-android:2.1.0'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0-M1'

    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
```

## Writing our service

`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.

Note that we will have to override some of the [Service callback methods](https://developer.android.com/guide/components/services#Basics) that handle key aspects of the service lifecycle.

It's also very important that we use a [partial wakelock](https://developer.android.com/training/scheduling/wakelock#cpu) so our service never gets affected by [Doze Mode](https://developer.android.com/training/monitoring-device-state/doze-standby). Bear in mind that this will have an impact in the battery life of our phone so we must evaluate whether our use case can be handled by any of the other alternatives Android offers in order to run processes in the background.

There are some utility function calls (`log`, `setServiceState`) and some custom enums (`ServiceState.STARTED`) in the code, but don't worry too much. If you want to see where they come from, just take a look at the [example repository](https://github.com/robertohuertasm/endless-service).

```kotlin
class EndlessService : Service() {

    private var wakeLock: PowerManager.WakeLock? = null
    private var isServiceStarted = false

    override fun onBind(intent: Intent): IBinder? {
        log("Some component want to bind with the service")
        // We don't provide binding, so return null
        return null
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        log("onStartCommand executed with startId: $startId")
        if (intent != null) {
            val action = intent.action
            log("using an intent with action $action")
            when (action) {
                Actions.START.name -> startService()
                Actions.STOP.name -> stopService()
                else -> log("This should never happen. No action in the received intent")
            }
        } else {
            log(
                "with a null intent. It has been probably restarted by the system."
            )
        }
        // by returning this we make sure the service is restarted if the system kills the service
        return START_STICKY
    }

    override fun onCreate() {
        super.onCreate()
        log("The service has been created".toUpperCase())
        var notification = createNotification()
        startForeground(1, notification)
    }

    override fun onDestroy() {
        super.onDestroy()
        log("The service has been destroyed".toUpperCase())
        Toast.makeText(this, "Service destroyed", Toast.LENGTH_SHORT).show()
    }

    private fun startService() {
        if (isServiceStarted) return
        log("Starting the foreground service task")
        Toast.makeText(this, "Service starting its task", Toast.LENGTH_SHORT).show()
        isServiceStarted = true
        setServiceState(this, ServiceState.STARTED)

        // we need this lock so our service gets not affected by Doze Mode
        wakeLock =
            (getSystemService(Context.POWER_SERVICE) as PowerManager).run {
                newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "EndlessService::lock").apply {
                    acquire()
                }
            }

        // we're starting a loop in a coroutine
        GlobalScope.launch(Dispatchers.IO) {
            while (isServiceStarted) {
                launch(Dispatchers.IO) {
                    pingFakeServer()
                }
                delay(1 * 60 * 1000)
            }
            log("End of the loop for the service")
        }
    }

    private fun stopService() {
        log("Stopping the foreground service")
        Toast.makeText(this, "Service stopping", Toast.LENGTH_SHORT).show()
        try {
            wakeLock?.let {
                if (it.isHeld) {
                    it.release()
                }
            }
            stopForeground(true)
            stopSelf()
        } catch (e: Exception) {
            log("Service stopped without being started: ${e.message}")
        }
        isServiceStarted = false
        setServiceState(this, ServiceState.STOPPED)
    }

    private fun pingFakeServer() {
        val df = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.mmmZ")
        val gmtTime = df.format(Date())

        val deviceId = Settings.Secure.getString(applicationContext.contentResolver, Settings.Secure.ANDROID_ID)

        val json =
            """
                {
                    "deviceId": "$deviceId",
                    "createdAt": "$gmtTime"
                }
            """
        try {
            Fuel.post("https://jsonplaceholder.typicode.com/posts")
                .jsonBody(json)
                .response { _, _, result ->
                    val (bytes, error) = result
                    if (bytes != null) {
                        log("[response bytes] ${String(bytes)}")
                    } else {
                        log("[response error] ${error?.message}")
                    }
                }
        } catch (e: Exception) {
            log("Error making the request: ${e.message}")
        }
    }

    private fun createNotification(): Notification {
        val notificationChannelId = "ENDLESS SERVICE CHANNEL"

        // depending on the Android API that we're dealing with we will have
        // to use a specific method to create the notification
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager;
            val channel = NotificationChannel(
                notificationChannelId,
                "Endless Service notifications channel",
                NotificationManager.IMPORTANCE_HIGH
            ).let {
                it.description = "Endless Service channel"
                it.enableLights(true)
                it.lightColor = Color.RED
                it.enableVibration(true)
                it.vibrationPattern = longArrayOf(100, 200, 300, 400, 500, 400, 300, 200, 400)
                it
            }
            notificationManager.createNotificationChannel(channel)
        }

        val pendingIntent: PendingIntent = Intent(this, MainActivity::class.java).let { notificationIntent ->
            PendingIntent.getActivity(this, 0, notificationIntent, 0)
        }

        val builder: Notification.Builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) Notification.Builder(
            this,
            notificationChannelId
        ) else Notification.Builder(this)

        return builder
            .setContentTitle("Endless Service")
            .setContentText("This is your favorite endless service working")
            .setContentIntent(pendingIntent)
            .setSmallIcon(R.mipmap.ic_launcher)
            .setTicker("Ticker text")
            .setPriority(Notification.PRIORITY_HIGH) // for under android 26 compatibility
            .build()
    }
}
```

## Time to deal with the Android Manifest

We will need some extra permissions for `FOREGROUND_SERVICE`, `INTERNET` and `WAKE_LOCK`. Be sure you don't forget to include them because it won't work otherwise.

Once we put them in place we will need to declare our service.

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

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

    <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">

        <service
                android:name=".EndlessService"
                android:enabled="true"
                android:exported="false">
        </service>

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>
</manifest>
```

## Can you tell me how I start the service?

Yes, you're right. You see, depending on the Android version we must start the service with a particular method.

If the Android version is below API 26 we must use [startService](https://developer.android.com/reference/android/content/Context.html#startService(android.content.Intent)). In any other case, [startForegroundService](https://developer.android.com/reference/android/content/Context.html#startForegroundService(android.content.Intent)) is what we must use instead.

Here you can see our `MainActivity`, just a screen with two buttons to *start* and *stop* the service. That's all you need to start our *endless service*.

Remember that you can check out the complete code in this [GitHub repository](https://github.com/robertohuertasm/endless-service).

```kotlin
class MainActivity : AppCompatActivity() {

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

        title = "Endless Service"

        findViewById<Button>(R.id.btnStartService).let {
            it.setOnClickListener {
                log("START THE FOREGROUND SERVICE ON DEMAND")
                actionOnService(Actions.START)
            }
        }

        findViewById<Button>(R.id.btnStopService).let {
            it.setOnClickListener {
                log("STOP THE FOREGROUND SERVICE ON DEMAND")
                actionOnService(Actions.STOP)
            }
        }
    }

    private fun actionOnService(action: Actions) {
        if (getServiceState(this) == ServiceState.STOPPED && action == Actions.STOP) return
        Intent(this, EndlessService::class.java).also {
            it.action = action.name
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                log("Starting the service in >=26 Mode")
                startForegroundService(it)
                return
            }
            log("Starting the service in < 26 Mode")
            startService(it)
        }
    }
}
```

## Bonus: Start the service on Android boot

Ok, we now have our *endless service* making network requests every minute as we wanted but then the user restarts the phone... and our service doesn't start again... :disappointed:

Don't worry, we can find a solution for this, too. We will create a [BroadCastReceiver](https://developer.android.com/reference/android/content/BroadcastReceiver) called `StartReceiver`.

```kotlin
class StartReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        if (intent.action == Intent.ACTION_BOOT_COMPLETED && getServiceState(context) == ServiceState.STARTED) {
            Intent(context, EndlessService::class.java).also {
                it.action = Actions.START.name
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    log("Starting the service in >=26 Mode from a BroadcastReceiver")
                    context.startForegroundService(it)
                    return
                }
                log("Starting the service in < 26 Mode from a BroadcastReceiver")
                context.startService(it)
            }
        }
    }
}
```

Then, we'll modify again our `Android Manifest` and add a new permission (`RECEIVE_BOOT_COMPLETED`) and our new [BroadCastReceiver](https://developer.android.com/reference/android/content/BroadcastReceiver).

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.robertohuertas.endless">
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"></uses-permission>
    <uses-permission android:name="android.permission.INTERNET"></uses-permission>
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

    <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">

        <service
                android:name=".EndlessService"
                android:enabled="true"
                android:exported="false">
        </service>

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

        <receiver android:enabled="true" android:name=".StartReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
            </intent-filter>
        </receiver>

    </application>
</manifest>
```

Take into account that the service won't be rebooted unless it was already running. That's how we programmed it, it's not that it has to be like that.

Anyway, if you want to test this, just spin up one emulator with `Google Services` in it and be sure to be running [adb](https://developer.android.com/studio/command-line/adb) in *root* mode.

```sh
adb root
# If you get an error then you're not running the proper emulator.
# Be sure to stop the service
# and force a system restart:
adb shell stop
adb shell start
# wait for the service to be restarted!
```

## Bonus 2: Restart the service when the task is removed

[Michal Materowski](https://github.com/mmaterowski) wrote to me with this case and its solution, so kudos for him!

Theoretically, according to `Android` documentation, returning **RETURN_STICKY** from the service's `onStartCommand` method should be enough for Android to keep the foreground service running.

Michal was testing all this with a *Xiaomi Note 5* with **Android Pie** and every time he swiped an app from recent apps, it worked flawlessly. Unfortunately, whenever he pressed the `Clear all recent apps` button ([MIUI](https://en.miui.com/) specific), the service was stopped and the notification was gone. The `Clear all recent apps` button was probably doing some sort of battery life optimization by killing all processes and their associated services.

He found out that `Android` documentation states that `onTaskRemoved` **"is called if the service is currently running and the user has removed a task that comes from the service's application."**, so the plan was to leverage this to restart the service. Bear in mind, though, that `onTaskRemoved` is not called if the application is killed in any other way (e.g. stopped from the phone settings).

Add these lines to your **service**:

```kotlin
override fun onTaskRemoved(rootIntent: Intent) {
    val restartServiceIntent = Intent(applicationContext, EndlessService::class.java).also {
        it.setPackage(packageName)
    };
    val restartServicePendingIntent: PendingIntent = PendingIntent.getService(this, 1, restartServiceIntent, PendingIntent.FLAG_ONE_SHOT);
    applicationContext.getSystemService(Context.ALARM_SERVICE);
    val alarmService: AlarmManager = applicationContext.getSystemService(Context.ALARM_SERVICE) as AlarmManager;
    alarmService.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 1000, restartServicePendingIntent);
}
```

You can check out the original [Michal Materowski's PR](https://github.com/robertohuertasm/endless-service/pull/4/files) with the whole code.

**IMPORTANT**: Michal had to manually set `Autostart` permission, otherwise the **service was not started on boot**.

![Autostar](https://dev-to-uploads.s3.amazonaws.com/i/byo4tirtix6mf8sbkwgv.png)

According to Michal, some people mentioned that setting `stopWithTask` flag might help, but it didn't make a difference for him:

```xml
<service android:name=".EndlessService"" android:stopWithTask="false" />
```

Lots of kudos to [Michal Materowski](https://github.com/mmaterowski) for his help on this case.

Enjoy! :heart:

--
Originally published at [robertohuertas.com](https://robertohuertas.com/2019/06/29/android_foreground_services) on June 29, 2019.