Foreground services let you asynchronously perform operations that are noticeable to the user. Foreground services show a status bar notification, to make users aware that your app is performing a task in the foreground and is consuming system resources.
Examples of apps that use foreground services include the following:
- A music player app that plays music in a foreground service. The notification might show the current song being played.
- A fitness app that records a user’s run in a foreground service, after receiving permission from the user. The notification might show the distance that the user has traveled during the current fitness session.
Creating a New Service
In this tutorial, we will create a fake demo fitness app service.
- Create a new file
RunningService.kt
. In this file add a class RunningService that extends fromService()
class. Here we need one mandatory functiononBind
. Now to start a service we haveonStartCommand()
method that will take theintent.action
and will start/stop the service. To stop servicestopSelf()
is a predefined method that is provided by the class. To start, we declare our ownstart()
method.
class RunningService : Service() {
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
when(intent?.action){
Actions.START.toString() -> start()
Actions.STOP.toString() -> stopSelf() // stop the service
}
return super.onStartCommand(intent, flags, startId)
}
private fun start(){
// need to show notification
// every service is also an context
val notification = NotificationCompat.Builder(this, "running_channel" ).setSmallIcon(R.drawable.ic_launcher_foreground).setContentTitle("Run is active").setContentInfo("Elapsed time: 00: 50").build()
startForeground(1, notification)
}
enum class Actions{
START, STOP
}
}
- Now in the
AndroidMainfest.xml
file, we first declare a few permisisons that will allow us to ask for Notification Permission and an less dangerous permission for Foreground Services.
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
- In same
AndroidMainfest.xml
file, now inside the<application>
, we will define a service as well with aforegrounServiceType
asdataSync
.
<application>
<service
android:name=".RunningService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="dataSync"
/>
</application>
- Now to request the permission for Notification, we need to create a
channelId
. In Step 1, we set that asrunning_channel
. However, we need to create that as soon as our app launch. So we can do this inRunningApp
which extends fromApplication()
, so that this could would be automatically run when our app first boots up. This will create the notification channel, used to send our notification.
class RunningApp : Application() {
override fun onCreate() {
super.onCreate()
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
val channel = NotificationChannel(
"running_channel", "Running Notifications", NotificationManager.IMPORTANCE_HIGH
)
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}
}
- Now we need Notification Permission from the user by showing the dialog. Since it’s a foreground service, showing notification is necessary. Now we need to request the permission from the user. So in
MainActivity
, we will request this permission.
// services -> REQ PERMISSION
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU){
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.POST_NOTIFICATIONS),0
)
}
- Now we can send the intent from
MainActivity
toRunningService
with an intent to either start ot stop the service. To start a service we can launch it usingstartService()
function on the intent.
Column {
Text("Running Service")
Button(
onClick = {
Intent(applicationContext, RunningService::class.java).also {
it.action = RunningService.Actions.START.toString()
startService(it)
}
}
) {
Text(text = "Start Run")
}
Button(
onClick = {
Intent(applicationContext, RunningService::class.java).also {
it.action = RunningService.Actions.STOP.toString()
startService(it)
}
}
) {
Text(text = "Stop Run")
}}
So even if we close our app, you can see the notification would still be there.