AndroidKotlin

How To Create A Foreground Service In An Android Application (Kotlin)

foreground service
A sample foreground service

In this tutorial, we will learn about foreground service and how to create a foreground service in an android application.

A foreground service is a service, which works exactly same as a normal service (background service) and the only difference is, it has a notification attached to it in the notification tray of the device.

The main advantage of having a foreground service is its higher priority than its background version. Android OS implemented restriction to background execution to save battery and memory usages, i.e., it kills least used apps (or processes) to retain the memory and battery.

With Foreground service, app gets following advantages,

  1. Higher priority hence less likely to be killed by OS
  2. Indication to user about important task under processing.
  3. Quick Access to the app like controls in music player apps.

Normally, apps run long tasks in services.  In which some of them are urgent like sending or uploading files to server, such tasks are safe to run in a foreground service.

Let’s see how to create a foreground service and start it. The below code snippet will create a foreground service with a notification.

Notification will have a button to open app. When app opens, current timestamp value passed to the MainActivity and shows it on the UI.

Step 1: Create a Foreground Service

package com.devdeeds.foregroundservice

import android.app.*
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.os.Build
import android.os.Handler
import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationCompat


/** @author Jayakrishnan P.M
 * Created by Devdeeds.com on 21/3/18.
 */

class FService : Service() {

    private val mHandler: Handler? = Handler()

    private lateinit var mRunnable: Runnable


    override fun onCreate() {
        super.onCreate()
        startForegroundService()
    }

    override fun onBind(p0: Intent?): IBinder? {
        return null;
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {

        Log.d(TAG, "ON START COMMAND")

        if (intent != null) {

            when (intent.action) {

                ACTION_STOP_FOREGROUND_SERVICE -> {
                    stopService()
                }

                ACTION_OPEN_APP -> openAppHomePage(intent.getStringExtra(KEY_DATA))
            }
        }
        return START_STICKY;
    }


    private fun openAppHomePage(value: String) {

        val intent = Intent(applicationContext, MainActivity::class.java)
        intent.putExtra(KEY_DATA, value)
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        startActivity(intent)

    }


    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

            val chan = NotificationChannel(
                CHANNEL_ID,
                CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT
            )

            chan.lightColor = Color.BLUE
            chan.lockscreenVisibility = NotificationCompat.VISIBILITY_PRIVATE
            val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            service.createNotificationChannel(chan)

        }
    }

    /* Used to build and start foreground service. */
    private fun startForegroundService() {

        //Create Notification channel for all the notifications sent from this app.
        createNotificationChannel()

        // Start foreground service.
        startFService()


        mRunnable = Runnable {

            notifyNextEvent()
            mHandler?.postDelayed(mRunnable, ONE_MIN_MILLI)  //repeat
        };

        // Schedule the task to repeat after 1 second
        mHandler?.postDelayed(
            mRunnable, // Runnable
            ONE_MIN_MILLI // Delay in milliseconds
        )

    }

    private fun startFService() {

        val description = getString(R.string.msg_notification_service_desc)
        val title = String.format(
            getString(R.string.title_foreground_service_notification),
            getString(R.string.app_name)
        )

        startForeground(SERVICE_ID, getStickyNotification(title, description))
        IS_RUNNING = true
    }


    private fun getStickyNotification(title: String, message: String): Notification? {

        val pendingIntent = PendingIntent.getActivity(applicationContext, 0, Intent(), 0)

        // Create notification builder.
        val builder = NotificationCompat.Builder(applicationContext, CHANNEL_ID)
        // Make notification show big text.
        val bigTextStyle = NotificationCompat.BigTextStyle()
        bigTextStyle.setBigContentTitle(title)
        bigTextStyle.bigText(message)
        // Set big text style.
        builder.setStyle(bigTextStyle)
        builder.setWhen(System.currentTimeMillis())
        builder.setSmallIcon(R.drawable.ic_stat_name)
        //val largeIconBitmap = BitmapFactory.decodeResource(resources, R.drawable.ic_alarm_on)
        //builder.setLargeIcon(largeIconBitmap)
        // Make the notification max priority.
        builder.priority = NotificationCompat.PRIORITY_DEFAULT

        // Make head-up notification.
        builder.setFullScreenIntent(pendingIntent, true)


        // Add Open App button in notification.
        val openAppIntent = Intent(applicationContext, FService::class.java)
        openAppIntent.action = ACTION_OPEN_APP
        openAppIntent.putExtra(KEY_DATA, "" + System.currentTimeMillis())
        val pendingPlayIntent = PendingIntent.getService(applicationContext, 0, openAppIntent, 0)
        val openAppAction = NotificationCompat.Action(
            android.R.drawable.ic_menu_view,
            getString(R.string.lbl_btn_sticky_notification_open_app),
            pendingPlayIntent
        )
        builder.addAction(openAppAction)


        // Build the notification.
        return builder.build()

    }


    private fun notifyNextEvent() {

        NotificationHelper.onHandleEvent(
            getString(R.string.title_gen_notification),
            getString(R.string.description_gen_notification),
            applicationContext
        )
    }


    private fun stopService() {
        // Stop foreground service and remove the notification.
        stopForeground(true)
        // Stop the foreground service.
        stopSelf()

        IS_RUNNING = false
    }


    override fun onDestroy() {
        IS_RUNNING = false
        mHandler?.removeCallbacks(null)
    }

    companion object {

        const val TAG = "FOREGROUND_SERVICE"


        const val ACTION_STOP_FOREGROUND_SERVICE = "ACTION_STOP_FOREGROUND_SERVICE"

        const val ACTION_OPEN_APP = "ACTION_OPEN_APP"
        const val KEY_DATA = "KEY_DATA"

        private const val CHANNEL_ID: String = "1001"
        private const val CHANNEL_NAME: String = "Event Tracker"
        private const val SERVICE_ID: Int = 1
        private const val ONE_MIN_MILLI: Long = 60000  //1min

        var IS_RUNNING: Boolean = false
    }
}

Notifications generated by the app will be sending with an interval of 1 minute.

Step 2: Add Foreground Service In AndroidManifest XML File

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

    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <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=".FService"
            android:enabled="true"
            android:exported="false" />


        <receiver
            android:name=".AutoStartUpBootReceiver"
            android:enabled="true"
            android:exported="false">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.intent.action.QUICKBOOT_POWERON" />
            </intent-filter>
        </receiver>

    </application>

</manifest>

Where android.permission.WAKE_LOCK is for waking the app screen on the event of notification.

android.permission.RECEIVE_BOOT_COMPLETED is for restarting the service on device reboot. Please refer complete source code for the reference.

Step 3: Start Foreground Service From Activity

package com.devdeeds.foregroundservice

import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity


class MainActivity : AppCompatActivity() {

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


        //Start service
        if (!FService.IS_RUNNING) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                startForegroundService(Intent(applicationContext, FService::class.java))
            } else {
                startService(Intent(applicationContext, FService::class.java))
            }
        }


        //Show data sent from service to view on click
        if (intent != null && intent.getStringExtra(FService.KEY_DATA) != null) {
            findViewById<TextView>(R.id.txtValueView).text = intent.getStringExtra(FService.KEY_DATA)
        }
    }
}

In the activity, it receives data via intent and shows it in a TextView.

This example demonstrates how to communicate a foreground service with activity via a notification.

About author

Geethu is a teacher by profession, but she likes to research new things and is also interested in writing. Devdeeds is her blog, where she writes all the blog posts related to technology, gadgets, mobile apps, games, and related content.

Leave a Reply

Your email address will not be published. Required fields are marked *