Implementing Android Service To Run Background Tasks
Learn how to implement an Android Service to run background tasks to perform specific data operations for your application.

In this post I will show you an example of implementing an Android Service to run background tasks.

Android Services are used as components to perform background tasks. Common tasks are data transfers in network operations, disk I/O operations and a music player.

Services do not require a UI for the user interact to perform their tasks. They can be called to complete a task and will continue until the task is complete. If the user navigates away from the application that called upon it, the Service will still be running.

Β There are three main types of Services in Android to use. Though similarly implemented, they are used for different reasons.

Background Services perform background data operations that do not require user intervention.

If you start the Service under the startService() call, you will need to help determine when the Service is to stop with either the stopSelf() or stopService() call.

Foreground Services allow the user interaction. They require a notification component to be visible while running so the user is aware it is running. Most commonly used for playing music.

Bound Services allow components in the application to interact with the Service. A messenger component can be used to bind to the Service to send messages back and forth.

If you start the Service under the bindService() call, the Service will be active as long as there is a component bound to it.

When creating an Android Service, determining how the Service will be stopped if very important and needs to ensure it is handled properly.

Android Service to Run Background Tasks

Android Background Service Running a Handler

In the example shown below, I have an AndroidΒ ServiceΒ starting aΒ HandlerΒ that calls aΒ RunnableΒ that loops itself until theΒ ServiceΒ is explicitly called to cancel by the user in the app from a button click.

Add Service To Android Manifest

First we need to specify our custom class in the AndroidManifest.xml as a Service. We specify the android:exported parameter as false because we do not want this Service accessed outside our application.

    <application
        ...>
        
        <activity
            ...
        </activity>
        
        <service
            android:name=".CustomService"
            android:exported="false" />
            
    </application>


Create Our Custom Service Class

Our custom Service starts a Handler that runs a simple math function of adding two random numbers. These numbers and the equation are logged in the Logcat every 300ms. This handler will run until the app is destroyed or the user selects the STOP button to cancel the Handler and the Service.

class CustomService : Service() {

    val myHandlerThread = HandlerThread("CustomServiceWorker")
    var handler : Handler? = null
    val mathFunction = object : Runnable {
        override fun run() {
            val first = Math.floor(Math.random() * 100)
            val second = Math.floor(Math.random() * 100)
            val total = first + second
            logi("${first} + ${second} = ${total}")
            handler?.postDelayed(this, 300)
        }

    }

    override fun onCreate() {
        super.onCreate()
        myHandlerThread.start()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        if(handler == null){
            handler = Handler(myHandlerThread.looper)
            handler?.postDelayed(mathFunction, 300)
        }
        return START_STICKY
    }

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

    override fun onDestroy() {
        super.onDestroy()
        logi("onDestroy")
        if(handler != null) {
            handler?.removeCallbacks(mathFunction)
        }
        myHandlerThread.quitSafely()
    }

}

//Kotlin Extension function created
fun Service.logi(message : String){
    Log.i("CustomService", message)
}

Instead of using Handlers, you can implement the use of coroutines. Handlers are more of a traditional approach to handling background tasks. While coroutines are more modern providing a better structured feel.

class CustomService : Service() {

    private val serviceCoroutine = CoroutineScope(Dispatchers.Default)
    private var serviceJob : Job? = null

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        if(serviceJob == null){
            serviceJob = serviceCoroutine.launch {
                while(isActive) {
                    val first = Math.floor(Math.random() * 100)
                    val second = Math.floor(Math.random() * 100)
                    val total = first + second
                    logi("${first} + ${second} = ${total}")
                    delay(300)
                }
            }
        }
        return START_STICKY
    }

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

    override fun onDestroy() {
        super.onDestroy()
        logi("onDestroy")
        serviceJob?.cancel()
        serviceJob = null
    }

}

fun Service.logi(message : String){
    Log.i("CustomService", message)
}

Building The Basic UI

All that is left to do is to create a basic UI example. The UI will consist of two buttons to start /stop the Service.

class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {


            SampleServiceTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    Column(modifier = Modifier.fillMaxSize().padding(innerPadding), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) {
                        Button(
                            onClick = {
                                Intent(this@MainActivity, CustomService::class.java).apply {
                                    startService(this)
                                }
                            }
                        ) {
                            Text(text = "Start")
                        }

                        Button(
                            onClick = {
                                Intent(this@MainActivity, CustomService::class.java).apply {
                                    stopService(this)
                                }
                            }
                        ) {
                            Text(text = "Stop")
                        }
                    }
                }
            }
        }
    }
}